设计模式 --并不简单的工厂模式

前言

上一篇我们讲了单例模式,今天我们再来讲另外一个比较常用的创建型模式:工厂模式(Factory Design Pattern)。

一般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。实际上,这三种我们最常用得是第一种简单工厂和工厂方法模式。而抽象工厂的原理稍微复杂点,在实际的项目中相对也不常用。所以,我们今天主要讲解的重点是前两种工厂模式。

简单工厂(Simple Factory)

// 创建抽象产品类,定义具体产品得公共接口
abstract class Product{
    public abstract void Show();
}
// 创建具体产品类(继承抽象产品类),定义生产的具体产品

//具体产品类A
class  ProductA extends  Product{
    @Override
    public void Show() {
        System.out.println("生产出了产品A");
    }
}

//具体产品类B
class  ProductB extends  Product{

    @Override
    public void Show() {
        System.out.println("生产出了产品C");
    }
}

// 创建工厂类,通过创建静态方法从而根据传入不同参数创建不同具体产品类的实例
class  Factory {
    public static Product Manufacture(String ProductName){
//工厂类里用switch语句控制生产哪种商品;
//使用者只需要调用工厂类的静态方法就可以实现产品类的实例化。
        switch (ProductName){
            case "A":
                return new ProductA();
            case "B":
                return new ProductB();
            default:
                return null;

        }
    }
}

可以看到,对于上面得简单工厂的实现方法,如果我们要添加新的Product,那势必要改动到 Factory 的代码,那这是不是违反开闭原则呢?实际上,如果不是需要频繁地添加新的 Product,只是偶尔修改一下Factory代码,稍微不符合开闭原则,也是完全可以接受的。

除此之外,在 Factory 有一组 if 分支判断逻辑,是不是应该用多态或其他设计模式来替代呢?实际上,如果 if 分支并不是很多,代码中有 if 分支也是完全可以接受的。应用多态或设计模式来替代 if 分支判断逻辑,也并不是没有任何缺点的,它虽然提高了代码的扩展性,更加符合开闭原则,但也增加了类的个数,牺牲了代码的可读性。

总结一下,尽管简单工厂模式的代码实现中,有多处 if 分支判断逻辑,违背开闭原则,但权衡扩展性和可读性,这样的代码实现在大多数情况下(比如,不需要频繁地添加 Product,也没有太多的 Product)是没有问题的。

工厂方法(Factory Method)

//创建抽象工厂类,定义具体工厂的公共接口
abstract class IFactory{
    public abstract Product Manufacture();
}

//工厂A类 - 生产A类产品
class  FactoryA extends IFactory{
    @Override
    public Product Manufacture() {
        return new ProductA();
    }
}

//工厂B类 - 生产B类产品
class  FactoryB extends IFactory{
    @Override
    public Product Manufacture() {
        return new ProductB();
    }
}

实际上,这就是工厂方法模式的典型代码实现。这样当我们新增一种 Product 的时候,只需要新增一个实现了 IFactory 接口的 Factory 类即可。所以,工厂方法模式比起简单工厂模式更加符合开闭原则。

看似很完美了,但是这里还是有一些小问题,简单工厂只是把麻烦的if判断从工厂类,移交到了使用者手里,怎么说呢?来看下面这段代码

//生产工作流程
public class FactoryPattern {
    public static void main(String[] args){
        //客户要产品A
        Product mFactoryA = load("A");
        mFactoryA.Manufacture().Show();

        //客户要产品B
        Product mFactoryB = load("A");
        mFactoryB.Manufacture().Show();
    }
    
    

  public IFactory load(String factoryType) {

    IFactory factory = null;
    if ("A".equalsIgnoreCase(factoryType)) {
      factory = new FactoryA();
    } else if ("B".equalsIgnoreCase(factoryType)) {
      factory = new FactoryB();
    } else {
      throw new InvalidRuleConfigException("factoryType is not supported: " + factoryType);
    }

    return factory;
  }
}

从上面的代码实现来看,工厂类对象的创建逻辑又耦合进了 load() 函数中,跟我们最初的代码版本非常相似。假如我们的业务需求是,根据读取的配置文件来创建相应的Product,其实不管是简单工厂模式还是方法工厂模式,上诉的if判断都无法消灭掉(所以在 GoF 的《设计模式》一书中,它将简单工厂模式看作是工厂方法模式的一种特例

抽象工厂(Abstract Factory)

讲完了简单工厂、工厂方法,我们再来看抽象工厂模式。抽象工厂模式的应用场景比较特殊,没有前两种常用,不是我们学习的重点,我们来简单了解一下就行了。

我们这里有一个需求场景,有一个工厂,他主要生产容器和模具。对于容器和模具下面又分很多种类。如下:

// 容器
ContainerProductA 
ContainerProductB

//模具
MouldProductA
MouldProductB

针对这种特殊的场景,如果还是继续用工厂方法来实现的话,我们要针对每个产品 都编写一个工厂类,也就是要编写 4 个工厂类。如果我们未来还需要增加了其他类型的生产产品(比如 杯子),那就要再对应地增加 2 个工厂类。而我们知道,过多的类也会让系统难维护。这个问题该怎么解决呢?

抽象工厂就是针对这种非常特殊的场景而诞生的。我们可以让一个工厂负责创建多个不同类型的对象(容器、模具、杯子 等),而不是只创建一种 Product 对象。这样就可以有效地减少工厂类的个数。具体的代码实现如下所示:

//创建抽象工厂类,定义具体工厂的公共接口
abstract class Factory{
   public abstract Product ManufactureContainer();
    public abstract Product ManufactureMould();
}
//创建抽象产品族类 ,定义具体产品的公共接口;
abstract class AbstractProduct{
    public abstract void Show();
}
//容器产品抽象类
abstract class ContainerProduct extends AbstractProduct{
    @Override
    public abstract void Show();
}

//模具产品抽象类
abstract class MouldProduct extends AbstractProduct{
    @Override
    public abstract void Show();
}

//容器产品A类
class ContainerProductA extends ContainerProduct{
    @Override
    public void Show() {
        System.out.println("生产出了容器产品A");
    }
}

//容器产品B类
class ContainerProductB extends ContainerProduct{
    @Override
    public void Show() {
        System.out.println("生产出了容器产品B");
    }
}

//模具产品A类
class MouldProductA extends MouldProduct{

    @Override
    public void Show() {
        System.out.println("生产出了模具产品A");
    }
}

//模具产品B类
class MouldProductB extends MouldProduct{

    @Override
    public void Show() {
        System.out.println("生产出了模具产品B");
    }
}
//A厂 - 生产模具+容器产品
class FactoryA extends Factory{

    @Override
    public Product ManufactureContainer() {
        return new ContainerProductA();
    }

    @Override
    public Product ManufactureMould() {
        return new MouldProductA();
    }
}

//B厂 - 生产模具+容器产品
class FactoryB extends Factory{

    @Override
    public Product ManufactureContainer() {
        return new ContainerProductB();
    }

    @Override
    public Product ManufactureMould() {
        return new MouldProductB();
    }
}

总结

我们来一块总结回顾一下,这里的三种工厂模式中,简单工厂和工厂方法比较常用,抽象工厂的应用场景比较特殊,所以很少用到,其实了解一下就可以了。

当创建逻辑比较复杂,是一个“大工程”的时候,我们就考虑使用工厂模式,封装对象的创建过程,将对象的创建和使用相分离。何为创建逻辑比较复杂呢?

  • 代码中如果存在 if-else分支判断,动态地根据不同的类型创建不同的对象。针对这种情况,我们就考虑使用工厂模式,将这一大坨if-else创建对象的代码抽离出来,放到工厂类中。
  • 还有一种情况,就是对象的创建十分复杂,我们也可以使用工厂类的方式来屏蔽对象的创建细节(建造者模式也是解决该类问题),在这种情况下,我们也可以考虑使用工厂模式,将对象的创建过程封装到工厂类中

对于第一种情况,当每个对象的创建逻辑都比较简单的时候,推荐使用简单工厂模式,将多个对象的创建逻辑放到一个工厂类中。

当每个对象的创建逻辑都比较复杂的时候,为了避免设计一个过于庞大的简单工厂类,推荐使用工厂方法模式,将创建逻辑拆分得更细,每个对象的创建逻辑独立到各自的工厂类中。

还是那句话,不要为了使用设计模式而用设计模式,设计模式都是在相应的使用场景下而诞生的。

posted @ 2020-08-07 16:08  正号先生  阅读(596)  评论(1编辑  收藏  举报