工厂模式之简单工厂、工厂方法与抽象工厂

1 简单工厂(Simple Factory)

1.1 问题引出

假如有一个披萨店,Pizza的种类有很多,如CheesePizza、VeggiePizza、PepperPizza等。披萨店根据收到的订单制作Pizza,披萨的制作流程有材料的准备材料、烤、切、包装几步。如何设计Pizza的订购呢?按照一般的设计思路:

那么当用户订购时:

    Pizza OrderPizza(String orderType) {
        Pizza pizza;

        if (orderType.equals("veggie")) {
            pizza = new VeggiePizza();
        } else if (orderType.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (orderType.equals("pepper")) {
            pizza = new PepperPizza();
        }
        
        // pizza制作过程
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

然而当披萨店增加或者删除披萨类型时,必须修改OrderPizza的代码。例如想要添加一个“中国披萨”,那么就要添加:

        else if (orderType.equals("china")) {
            pizza = new ChinaPizza();
        }

也就是说,只要披萨菜单存在改动,这段代码就得一改再改,这种设计明显违反了“开放-关闭原则” ,并没有做到对修改“关闭”。同时,如果有多个订单,那么每个订单也必须依赖每个子类,这样就显得冗杂。如何优化呢?这里得引出“简单工厂模式”了。

1.2 定义与结构

简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,简单工厂模式中专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

直白地讲,简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化,这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。

简单工厂模式并不在 GoF 23 种设计模式之列,与其说其是设计模式,不如说是一种编程习惯

简单工厂模式中包含如下角色:

  • Factory:工厂角色

    工厂角色负责实现创建所有实例的内部逻辑。

  • Product:抽象产品角色

    抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口。

  • ConcreteProduct:具体产品角色

    具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。

类图

时序图

1.3 实现

将简单工厂模式运用到上面的披萨店中,那么就得建造一个SimpleFactory工厂类,它被所有需要进行实例化的客户类调用。

public class SimpleFactory {
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;

        if (orderType.equals("veggie")) {
            pizza = new VeggiePizza();
        } else if (orderType.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (orderType.equals("pepper")) {
            pizza = new PepperPizza();
        }
        return pizza;
    }
}

客户代码为:

public PizzaStore{
    SimpleFactory simpleFactory;
    
    public OrderPizza(SimpleFactory simpleFactory) {
        this.simpleFactory = simpleFactory;
    }
    
	public Pizza orderPizza(String orderType) {
        Pizza pizza;
        
        pizza = simpleFactory.createPizza(orderType);
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    
    // 其他方法
}

1.4 小结

优点

  • 工厂类含有必要的判断逻辑,可决定创建产品类的实例的实际时刻。工厂和产品的职责区分明确,客户端仅仅“消费”产品。
  • 客户端只需要知道具体产品类所对应的参数即可。
  • 通过引入配置文件,可在不修改任何客户端代码的情况下更换和增加新的具体产品类,提高了系统的灵活性。

缺点

  • 工厂类集中了所有产品创建逻辑,职责过重,一旦发生异常,整个系统将受影响。
  • 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
  • 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂。
  • 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

适用场景

对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。

  • 如JDK类库中工具类java.text.DateFormat,它用于格式化一个本地日期或者时间。
public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale locale);

2 工厂方法(Factory Method)

2.1 问题引出

假如披萨店在各个地儿都有,比如Beijing,Shanghai…他们都保有他们城市的风味。要吃到不同城市风味的披萨,如果运用简单工厂模式:

// 这里创建的工厂,全是在北京地区的工厂
BeijingFactory beijingFactory = new BeijingFactory();
// 创建一个披萨店,将北京工厂的引用作为参数
PizzaStore pizzaStore = new PizzaStore(beijingFactory);
// 制作披萨时,就会得到在北京地区的奶酪披萨
pizzaStore.orderPizza("CheesePizza");

同理,上海的披萨店也类似:

ShanghaiFactory shanghaiFactory = new ShanghaiFactory();
PizzaStore pizzaStore = new PizzaStore(shanghaiFactory);
pizzaStore.orderPizza("CheesePizza");

然而,这样使得代码缺乏弹性。如何弹性地制作出具有一定规范但又不失本地特色的披萨呢?这里就得引出工厂方法模式了。

2.2 定义与结构

工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

直白地讲,“工厂方法模式”是对简单工厂模式的进一步抽象化,只是工厂方法把产品的实例化操作推迟到子类

工厂方法模式由4个要素构成。

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 createProduct() 来创建产品。
  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

类图

时序图

2.3 实现

还是以上面披萨制作为例,那么就可以把createPizza(style)放回PizzaStore中,不过设置为“抽象方法”,然后每个区域设置自己的PizzaStore子类。

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

那么创造怎么样口味的披萨由子类工厂决定:

public class MyStyleStore extends PizzaStore {
    Pizza createPizza(String type) {
        if (type.equals("veggie")) {
            pizza = new MyStyleVeggiePizza();
        } else if (type.equals("cheese")) {
            pizza = new MyStyleCheesePizza();
        } else if (type.equals("pepper")) {
            pizza = new MyStylePepperPizza();
        }
    }
}
public class BeijingStore extends PizzaStore {
    Pizza createPizza(String type) {
        if (type.equals("veggie")) {
            pizza = new BeijingVeggiePizza();
        } else if (type.equals("cheese")) {
            pizza = new BeijingCheesePizza();
        } else if (type.equals("pepper")) {
            pizza = new BeijingPepperPizza();
        }
    }
}

2.4 小结

优点

  • 用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
  • 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
  • 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

缺点

  • 类的个数容易过多,增加复杂度。
  • 考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。
  • 抽象产品只能生产一种产品。

适用场景

在工厂方法模式中,客户端不需知道具体产品类的类名,只需知道创建具体产品的工厂类;对于抽象工厂类,只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象。

工厂方法模式在JDK类库中运用比较多,例如:

3 抽象工厂(Abstract Factory)

3.1 问题引出

为了更清晰地理解工厂方法模式,先理解两个概念:

  • 产品等级 :产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
  • 产品族 :在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。

电器工厂的产品等级与产品族

3.2 定义与结构

抽象工厂模式(Abstract Factory Pattern):提供一个接口,用于创建创建一系列相关或相互依赖对象的家族,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。

抽象工厂模式与工厂方法模式区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。

抽象工厂模式同工厂方法模式一样,也是由4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。

  • AbstractFactory:抽象工厂
  • ConcreteFactory:具体工厂
  • AbstractProduct:抽象产品
  • Product:具体产品

类图

时序图

3.3 实现

public class AbstractProductX {
}
public class AbstractProductY {
}
public class ConcreteProductAX extends AbstractProductX {
}
public class ConcreteProductBX extends AbstractProductX {
}
public class ConcreteProductAY extends AbstractProductY {
}
public class ConcreteProductBY extends AbstractProductY {
}
public abstract class AbstractFactory {
    abstract AbstractProductX createProductX();
    abstract AbstractProductY createProductY();
}
public class ConcreteFactoryA extends AbstractFactory {
    AbstractProductX createProductX() {
        return new ProductAX();
    }

    AbstractProductY createProductY() {
        return new ProductAY();
    }
}
public class ConcreteFactoryB extends AbstractFactory {
    AbstractProductX createProductX() {
        return new ProductBX();
    }

    AbstractProductY createProductY() {
        return new ProductBY();
    }
}
public class Client {
    public static void main(String[] args) {
        AbstractFactory abstractFactory = new ConcreteFactoryA();
        AbstractProductX productX = abstractFactory.createProductX();
        AbstractProductY productY = abstractFactory.createProductY();
        // do something with productX and productY
    }
}

3.4 小结

优点

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

缺点

  • 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)

工厂方法模式在JDK类库中的运用:

4 总结

严格地讲,简单工厂模式并不在GoF23种设计模式之列,更像是一种编程习惯,也可以理解为工厂方法模式的特殊情况之一。

  • 简单工厂方法实现了客户类与其子类的解耦。

  • 工厂方法模式属于类创建型模式,它把产品的实例化操作也推迟到子类,实现了产品类与其子类的解耦。

  • 抽象工厂模式属于对象创建型模式,相对工厂方法模式针对一个产品等级结构,抽象工厂模式则需要面对多个产品等级结构,一个抽象工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。

三种设计模式都各有优缺点,在实际开发中,我们应该根据实际业务需求来选择。

参考

  • 8
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值