[设计模式]深入浅出之工厂模式

1,073 阅读5分钟

前言

设计模式是对代码开发经验的总结,代表着最佳编程实践,对于立志成为优秀工程师的我们,这一技能必不可。本系列会针对其特点、实现方式及应用场景做全面解析。

简单工厂

简单工厂其实不是一个设计模式,而是一种编程习惯,但是引用非常普遍

我们通过一个范例,来认识它,范例代码来源于@《Head First设计模式》

现在有一个披萨店,Pizza品种繁多,供不应求,我们称之为萌芽期

常规程式设计,我们通过传递不同参数,实例化Pizza:

Pizza orderPizza(String type) {
    Pizza pizza;
    
    // 不同的披萨
    if (type.equals("cheese")) {
        pizza = new CheesePizza();
    } else if (type.equal("greek")) {
        pizza = new GreekPizza();
    } else if (type.equal("pepperoni")) {
        pizza = new PepperoniPizza();
    } else if (type.equal("clam")) {
        pizza = new ClamPizza();
    } else if (type.equal("veggie")) {
        pizza = new VeggiePizza();
    }
    
    pizza.prepare();
    pizza.bake();
    pizza.cut();
    pizza.box();
    return pizza;
}

但是后面PizzaStore为迎合大众口味,推出不同的PizzaType,我们在原有程式做修改,就违背了开闭原则。

简单工厂可以帮我们解决困扰,我们将变化的部分抽离出来,打包到简单工厂类中

public class SimplePizzaFactory {
    public Pizza createPizza(Striing type) {
        Pizza piizza =null;
        
        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equal("greek")) {
            pizza = new GreekPizza();
        } else if (type.equal("pepperoni")) {
            pizza = new PepperoniPizza();
        } else if (type.equal("clam")) {
            pizza = new ClamPizza();
        } else if (type.equal("veggie")) {
            pizza = new VeggiePizza();
        }
    }
}

PizzaStore接入工厂代码模块:

public class PizzaStore {
    SimplePizzaFactory factory;
    
    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }
    
    public Pizza orderPizza(String type) {
        Pizza pizza;
        
        pizza = factory.createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

优势:

  • 解耦后的工厂类,可以被其他服务复用,eg:Pizza菜单、Pizza外卖服务等
  • 符合开闭原则,pizza种类的变更,单独维护工厂类即可

工厂模式

PizzaStore由于越做越好,已经在芝加哥和纽约开设了分店,两地口味差别较大,原来的架构只适应多种Pizza制作,无法适应门店新增的业务诉求。我们称之为发展期

如果将业务抽离到Pizza-产品组、Pizza门店-创建组,结构就会清晰很多,也具备更高的扩展性。

public abstract class PizzaStore {
    publc Pizza orderPizza(String type) {
        Pizza pizza;
        
        pizza = createPizza(type);
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    
    abstract Pizza createPizza(string type);
}

抽象的PizzaStore父类交出控制权,由子类自己确定实例化哪个具体的pizza

public class NYPizzaStore extends PizzaStore {
    Pizza createPizza(String item) {
        if (item.equals("cheese")) {
            pizza = new NYStyleCheesePizza();
        } else if (item.equal("greek")) {
            pizza = new NYStyleGreekPizza();
        } else if (item.equal("pepperoni")) {
            pizza = new NYStylePepperoniPizza();
        }
        return pizza;
    }
}

针对我们的Pizza产品组,我们用同样思路做父类抽象和子类继承:

public abstract calss Pizza {
    String name;
    String dough;
    String sauce;
    ArrayList toppings = new ArrayList();
    
    void prepare() {
        System.out.println("Preparing "+name);
        System.out.println("Tossiing dough..");
        System.out.println("Adding sauce..");
        System.out.println("Adding toppings: ");
        for (int i=0; i<toppings.size();i++) {
            System.out.println(" " + toppings.get(i));
        }
    }
    
    void bake() {
        System.out.println("Bake for 25 minutes at 350");
    }
    
    void cut() {
        System.out.println("Cutting the pizza into diagonal slices");
    }
    
    void box() {
        System.out.println("Place pizza in official PizzaStore box");
    }
    
    public string getName() {
        return name;
    }
}

子类继承,并根据业务需求,定制化美味的Pizza:

public class NYStyleCheesePizza extends Pizza {
    public NYStyleCheesePizza() {
        name = "NY Style Sauce and Cheese Pizza";
        dough = "Thin Crust Dough";
        sauce = "Marinara Sauce";
        
        toppings.add("Grated Reggiano Cheese");
    }
    
    // 覆盖原cut方法
    void cut() {
        System.out.println("Cutting the pizza into square slices");
    }
}

抽象工厂模式

Pizza发展极快,已经风靡全美,门店越来越多,此时,品控显的尤为重要,为了杜绝纽约店、加利福利亚店、佛罗里达店等原材料品质不一,导致Pizza质量问题,我们可以提供原材料集中供应,解决此问题。这一时期,我们称之为成熟期

我们将业务拆解为抽象原料工厂、纽约州原料工厂、加利福利亚州原料工厂、纽约州商店、以及各类抽象Pizza材料、Pizza材料

抽象原料工厂:

public interface PizzaIngredientFactory {
    public Dough createDough();
    public Sauce createSauce();
    public Cheese createCheese();
    public Veggies[] createVeggies();
    public Pepperoni createPepperoni();
    public Clams createClam();
}

纽约州原料工厂:

public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
    public Dough createDough() {
        return new ThinCrustDough();
    }
    public Sauce createSauce() {
        return new MarinaraSauce();
    }
    public Cheese createCheese() {
        return new ReggianoCheese();
    }
    public Veggies[] createVeggies() {
        Veggies veggies[] = {new Garlic(), new Onion(), new MushRoom(), new RedPepper()}
        return Veggies;
    }
    public Pepperoni createPepperoni() {
        return new SlicedPepperoni();
    }
    public Clams createClam() {
        return new FreshClams();
    }
}

抽象Pizza类:

public abstract calss Pizza {
    String name;
    String dough;
    String sauce;
    Veggies veggies[];
    Cheese cheese;
    Pepperoni pepperonil
    Clams clam;
    
    abstract void prepare();
    
    void bake() {
        System.out.println("Bake for 25 minutes at 350");
    }
    
    void cut() {
        System.out.println("Cutting the pizza into diagonal slices");
    }
    
    void box() {
        System.out.println("Place pizza in official PizzaStore box");
    }
    
    public string setName() {
        this.name = name;
    }
    
    public string getName() {
        return name;
    }
    
    public String toString() {
        // 打印Pizza代码
    }
}

Pizza实例:

public class CheesePizza extends Pizza {
    PizzaIngredientFactory ingredientFactory;
    
    public CheesePizza(PizzaIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }
    
    void prepare() {
        System.out.println("Preparing" + name);
        dough = ingredientFactory.createDough();
        sauce = ingredientFactory.createSauce();
        cheese = ingredientFactory.createCheese();
    }
}

PizzaStore实例:

public class NYPizzaStore extends PizzaStore {
    protected Pizza createPizza(String item) {
        Pizza pizza = null;
        Pizza IngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
        
        if (item.equals("cheese")) {
            pizza = new CheesePizza(ingredientFactory);
            pizza.setName("NEW York Style Cheese Pizza");
        } else if (item.equals("veggie")) {
            pizza = new VeggiePizza(ingredientFactory);
            pizza.setName("NEW York Style veggie Pizza");
        } 
    }
}

简单工厂实践

业务场景:每月都有新增活动,比如二月租屋买3送1,三月售屋优惠券,以及五月售屋五折,该如何设计实现呢?

活动包含四个必要因素:活动资格、活动方案、活动金流、以及活动有效期,我们提取公共方法,并提供抽象活动类,让每个活动子类继承实现。

abstract class Act{
    public $data = array();			 //活动明细
    abstract function checkRoleIsRight($data);   //活动资格
    abstract function actProgram($data);         //活动方案
    abstract function actMoney($data);           //活动金流
    
    /**
     * 檢測使用是否在有效期
     */
    function checkActIsExpires($actStartTime, $actEndTime) {
    	$startTime = strtotime($actStartTime);
    	$endTime = strtotime($actEndTime);
    	
    	return (NOW_TIME >= $startTime && NOW_TIME <= $endTime) ? 1 : 0;
    }
}

子活动继承并实现业务逻辑

class salNewHalfAct extends Act{

    public $actStartTime = '2020-01-01 00:00:00';
    public $actEndTime   = '2022-01-01 23:59:59';
    
    function checkRoleIsRight($data){
        //判断是否在活动时间内
        $isExpires = $this->checkActIsExpires($this->actStartTime, $this->actEndTime);
        return !$isExpires ? false : true;
    }
    
    function actProgram($data){}
    
    function actMoney($data){}
}

特点

  • 公共方法抽离,并实现通用方法,让代码更简洁
  • 业务需求只需要在特定actProgram、actMoney方法内实现,具备良好维护及扩展性

总结

我们根据Pizza店不同阶段运营情况,引入不同工厂以适应其业务:萌芽期、发展期、成熟期的需求。

接着,我们通过业务活动案例,实践了简单工厂。

由于当前互联网业务迭代极快,过多冗余的使用设计模式反而会适得其反,个人建议大家可以从成熟的业务模块入手,相信会事半功倍!

参考资料:

《Head First设计模式》