设计模式——工厂方法模式(汽车工厂)

本文首发于cdream的个人博客,点击获得更好的阅读体验!

欢迎转载,转载请注明出处。

本文主要讲述工厂方法模式,并与简单方法模式进行对比。以汽车制造厂为例进行讲解。

image-20181223133404085

一、概念

定义:工厂方法模式(Factory method pattern)是指工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作推迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

工厂方法模式主要解决两个问题
1.定义一个创建对象的单独操作(工厂方法)
2.将创建对象推迟到子类完成

二、简单工厂模式

在说工厂方法模式前先简述一下简单工厂模式,在Head First 设计模式中提到:简单工厂模式与其说是一种设计模式倒不如说是一种编程习惯。

过去在制造对象时,我们经常使用new 对象这种操作,不过一提到new,那就是面向具体编程,面向实现编程,不方便进行修改和维护。

举个简单的例子:在没有工厂方法之前,专卖店出售汽车时都要在方法里面new 对象

public class CarStore{
    public Car saleCar(String type){
        Car car;
        if (type.equals("A4")){
            car = new AduiA4();
        } else if (type.equals("A6")){
            car = new AudiA6();
        } else if (type.equals("A8")){
            car = new AudiA8();
        }
        // 售前检查
        car.check();
        // 汽车出库
        car.outStore();
        return car;
	}
}

看看,这样的设计有什么缺点?

  • 如果哪天新增加了奥迪Q5,奥迪Q7,完蛋了,我们需要对这个类进行修改了!这完全违背了开闭原则
  • 其实这还好说,关键这是一家店,如果是千千万万的专卖店,让你修改,怕别怕?

于是,我们对流程进行优化,封装变化,你明白我的意思吧,把那个该死的总是改变的地方封装起来!

public class SimpleCarFactory {
    public Car createCar(String type) {
        Car car = null;
        if (type.equals("A4")) {
            car = new AduiA4();
        } else if (type.equals("A6")) {
            car = new AudiA6();
        } else if (type.equals("A8")) {
            car = new AudiA8();
        }
        return car;
    }
}
public class CarStore{
    SimpleCarFactory scf;
    public CarStore(SimpleCarFactory scf){
        this.scf = scf;
    }
    public Car saleCar(String type){
        car = scf.createCar(type);
        // 售前检查
        car.check();
        // 汽车出库
        car.outStore();
        return car;
	}
}

现在,你随便加方法,随便开新店,所有事情都由我这个简单汽车工厂来做,对于专卖店来说,再也不用头痛这些事了!

不过简单方法模式缺点也很明显,

  • 如果工厂类集中了所以的创建逻辑,当有复杂的多层次等级结构时,所有的业务逻辑都在这个工厂类中实现,这个类会变的异常复杂。
  • 什么时候它不能工作了,整个系统都会受到影响。
  • 不符合开闭原则,一旦添加新的对象,就不得不对新的产品进行修改。

三、工厂方法模式(拆分汽车工厂为例)

为了解决简单工厂模式的缺点,于是产生了工厂方法模式。工厂方法模式中将对象实例化延迟到子类中完成,创建对象只需要添加新的子类工厂就可以。

现在要满足如下需求,奥迪公司决定在中国和德国建立汽车工厂,在中国生产国产奥迪,在德国生产进口奥迪。

先对简单工厂进行改装

public class SimpleCarFactory {
    public Car createCar(String type,String area) {
        Car car = null;
        if ("imports".equals(area)){
            if ("A4".equals(type)) {
            	car = new ImportsAduiA4();
            } else if ("A6".equals(type)) {
                car = new ImportsAudiA6();
            } else if ("A8".equals(type)) {
                car = new ImportsAudiA8();
        	}
        } else if("domestics".equals(area)){
            if ("A4".equals(type)) {
            	car = new DomesticsAduiA4();
            } else if ("A6".equals(type)) {
                car = new DomesticsAudiA6();
            } else if ("A8".equals(type)) {
                car = new DomesticsAudiA8();
        	}
        }
        return car;
    }
}

问题很明显

  1. 当奥迪公司决定在美国在开一家工厂,那又要对这个类进行修改,如果添加新的产品还是要修改。
  2. 这是一个高度依赖工厂类,本工厂依赖于所有的汽车产品类型,这是不符合依赖倒置原则的——要依赖抽象,不要依赖具体类。无论是高层组件(汽车工厂)还是低层组件(各类型汽车)都要依赖抽象。

因此我们需要改进工厂方法,让其依赖汽车接口而不是所有的奥迪汽车。

UML:

image-20181223131003136

抽象工厂类

public abstract class AbstractCarFactory {
    public abstract Car createCar(String type);
}

进口工厂

public class ImportsCarFactory extends AbstractCarFactory{
    @Override
    public Car createCar(String type) {
        if ("A4".equals(type)) {
            car = new ImportsAduiA4();
        } else if ("A6".equals(type)) {
            car = new ImportsAudiA6();
        } else if ("A8".equals(type)) {
            car = new ImportsAudiA8();
        }
        return car;
    }
}

国产工厂

public class DomesticsCarFactory extends AbstractCarFactory{
    @Override
    public Car createCar(String type) {
        if ("A4".equals(type)) {
            car = new DomesticsAduiA4();
        } else if ("A6".equals(type)) {
            car = new DomesticsAudiA6();
        } else if ("A8".equals(type)) {
            car = new DomesticsAudiA8();
        }
        return car;
    }
}

客户端

public class Test {
    public static void main(String[] args) {
        AbstractCarFactory factory01 = new ImportsCarFactory();
        AbstractCarFactory factory02 = new DomesticsCarFactory();
        Car importsA4 = factory01.create("A4");
        Car domesticsA4 = factory02.create("A4");
    }
}

这就是工厂方法模式,无论是高层组件还是低层组件都是依赖于抽象的Car接口,当我们需要进行扩展时,例如创建美国工厂,只需要创建一个新的工厂类继承抽象工厂类就可以。

四、优缺点

优点

  1. 更符合开-闭原则:新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可。
  2. 符合单一职责原则:每个具体工厂类只负责创建对应的产品。
  3. 符合依赖倒置原则:无论底层组件还是高层组件都依赖于抽象而不是具体实现。

缺点

  1. 添加新的产品时,除了产品类外,还要提供与之对应的具体工厂实现,系统类的个数将成倍增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销。
  2. 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类。(例如一家专卖店不卖国产,改卖进口了,那就得对店铺进行修改)
  3. 一个具体工厂只能创建一种类型的具体产品,进口工厂无法创建国产产品,国产工厂也不能创建进口汽车。

五、总结

本文对工厂方法模式进行了简单描述。工厂方法模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。掌握工厂方法模式的重点在于理解将实例化推迟到子类中实现和底层组件与高层组件都依赖于抽象。

本文首发于cdream个人博客

欢迎转载,转载请注明出处!


本文参考:

  1. Head First 设计模式,Eric Freeman &Elisabeth Freeman with Kathy Sierra & Bert Bates
  2. Factory method pattern,wiki
  3. 工厂方法模式(Factory Method)-最易懂的设计模式解析
posted @ 2019-01-27 09:08  cdream  阅读(1926)  评论(0编辑  收藏  举报