设计模式之工厂模式

又来了新的项目

这一天,小明主管找到小明说:我们之前的项目里面其实有很多通过new的方式去创建对象,而且是创建的一些实际的对象,这样的做法是不太好的。小明很诧异,不通过new方式,还有其他的方式创建?主管对小明说:没事,做了我们的项目你就明白这一切了。这次我们的新项目是做给一家披萨店的,我和你一起来做这个项目,你先做,我再做进一步的改进。

小明很快做了一个版本出来:

Pizza orderPizza(String type) {
    Pizza pizza;
    if(type.equals("cheese")){
        pizza = new CheesePizza();
    }else if(type.equals("greek")){
        pizza = new GreekPizza();
    }else if(type.equals("durian")){
        pizza = new DurianPizza();
    }
    pizza.prepare();
    pizza.make();
    pizza.cut();
    pizza.box();
    return pizza;   
} 

主管对小明说:如果我现在有新种类的Pizza,你的程序会犯了什么问题?小明一下发觉了问题所在,代码里面除了make()方法以外,上面的都是变化的,我应该把它们独立出来:

public class SimplePizzaFactory{
    public Pizza createPizza(String type) {
        Pizza pizza;
        if(type.equals("cheese")){
            pizza = new CheesePizza();
        }else if(type.equals("greek")){
            pizza = new GreekPizza();
        }else if(type.equals("durian")){
            pizza = new DurianPizza();
        }
        return pizza;   
    } 
}
public class PizzaStore{

    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory){
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {
        Pizza pizza = factory.createPizza(type);
        pizza.prepare();
        pizza.make();
        pizza.cut();
        pizza.box();
        return pizza;   
    } 
} 

主管继续对小明说,嗯,不错,已经慢慢走上正轨了,你现在使用的就是不算是一种设计模式的模式—简单工厂:将对象的创建放在另外的类中去解决,这样能够简单的将变化的部分独立出来,我们再有新的Pizza或者其中一种Pizza不在制作时,可以不用去修改PizzaStore的代码,因为PizzaStore是不应该被经常修改的。但是,简单工厂还不是我们想要的,如果出现了其他加盟店,我们需要将其他加盟店的整个流程都管理起来,那么仅仅只有生成Pizza的工厂是不够的,从准备到装盒的整个流程我们都需要规范其起来(prepare()、make()、cut()、box()方法),不然其他的加盟店只利用我们提供的工厂生产Pizza,而这些流程有自己的方式,那Pizza店的服务就不能统一标准了。那小明问那我们应该怎么改进呢?主管说,很简单,我们使用工厂方法模式

public abstract class PizzaStore{

    public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type);
        pizza.prepare();
        pizza.make();
        pizza.cut();
        pizza.box();
        return pizza;   
    }

    abstract Pizza createPizza(String type); 
} 
public class ChengDuPizzaStore extends PizzaStore{    
    Pizza createPizza(String type){
        if(type.equals("cheese")){
            return new CheesePizza();
        }else if(type.equals("greek")){
            return new GreekPizza();
        }else if(type.equals("durian")){
            return new DurianPizza();
        }else {
            return null;
        }
    }
} 

小明说,等等,这种模式怎么又出现了可能会改变的部分啊?而且这和简单工厂的方式区别就是新的创建对象类成了PizzaStore的子类。主管说,你说到关键了,这和简单工厂的方式很相似,但是仅仅只是相似,它和简单工厂的区别很大:简单工厂仅仅只是把对象的创建封装到另一个类中,而工厂方法模式提供了一个框架,所有子类都依靠这个框架(prepare()、make()、cut()、box()),然后把对象的创建交给了子类,因为在Pizza店和其他的一些实际的需求中,子类可能只需要决定产生什么要的东西,而其他的流程都是统一规范的。这里面体现了一个很重要的原则:依赖倒置原则,披萨店在使用Pizza时,没有具体依赖某一种披萨实现自己的流程(prepare()、make()、cut()、box()),而是一个通过依赖于一个Pizza接口。

工厂方法模式定义

定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类。

角色

  • 抽象产品
  • 产品实现
  • 抽象工厂
  • 具体工厂

优缺点

优点

  1. 在工厂方法中,用户只需要知道所要产品的具体工厂,无须关系具体的创建过程,甚至不需要具体产品类的类名
  2. 在系统增加新的产品时,我们只需要添加一个具体产品类和对应的实现工厂,无需对原工厂进行任何修改,很好地符合了“开闭原则”

缺点

  1. 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,是的系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事

披萨店的新问题

没过多久,主管找到小明说,披萨店在使用我们的程序时,出现了一个问题,披萨店的出售流程是规范了,但是有一些加盟店开始在原料上偷工减料,造成披萨在顾客中的口碑褒贬不一,所以我们需要改进一下,让每个店使用的原料是可以被监控的。小明很快想到了办法,我们将披萨生成(new的时候)统一为一个简单工厂,那么就可以统一知道披萨的生成了。主管说这样表面是解决了问题,但其实这样会使每个店生成的都是一样的,我们的目的是为了监控原料,而非统一原料。所以我们真正需要使用的是抽象工厂模式:

public interface PizzaIngredientFactory{    
    public Dough createDough();
    public Sauce createSauce();
    public Cheese createCheese();
    public Veggies[] createVeggies();
    public Clams createClams();
}
public abstract class Pizza{

    String name;
    Dough dough;
    Sauce sauce;
    Cheese cheese;
    Veggies[] veggies;
    Clams clams;

    public void make(){

    }
    public void cut(){

    }
    public void box(){

    }

    abstract void prepare();

}
public class CheesePizza extends Pizza{

    PizzaIngredientFactory factory;

    public CheesePizza(PizzaIngredientFactory factory){
        this.factory = factory;
    }

    void prepare(){
        dough = factory.createDough();
        sauce = factory.createSauce();
        cheese = factory.createCheese();
        veggies = factory.createVeggies();
        clams = factory.createClams();
    }
}

这样一来,我们的披萨名字是一样的,但是不同的地方制作相同披萨的原料可以不一样(自己实现PizzaIngredientFactory),这就是抽象工厂模式。

抽象工厂模式定义

提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

角色

  • 抽象工厂
  • 实现工厂
  • 抽象产品
  • 具体产品

产品族

不同的产品等级结构,功能相关联的产品组成的家族。抽象工厂就是创建出分属于不同产品等级机构的对象组成产品族。

优缺点

优点:

  1. 抽象工厂模式隔离了具体类的生产,使得客户并不需要知道什么被创建
  2. 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象
  3. 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”

缺点:

  1. 增加新的产品等级结构很复杂,需要修改抽象工厂和所有的具体工厂类,对“开闭原则”的支持呈现倾斜性
坚持原创分享,您的支持将鼓励我不断前行!