第21次文章:工厂模式
- 2019 年 10 月 8 日
- 筆記
在我们GOFO23种设计模式中,上期着重介绍了创造型模式中的单例模式,这周我们着重介绍另一种创造型设计模式——工厂模式。
一、工厂模式
-实现了创建者和调用者的分离。
1、详细分类
-简单工厂模式
-工厂方法模式
-抽象工厂模式
2、面向对象设计的基本原则
(1)OCP(开闭原则,Open-Closed Principle)
一个软件的实体,在后期的升级改造过程中,我们不应该通过修改其源代码来完成我们的目的,而是通过在源代码的基础上增加新的模块来完成我们需要的功能。
(2)DIP(依赖倒转原则,Dependence Inversion Principle)
在完成每一个类的功能时,我们最好是通过实现接口的方式来实现每一个功能模块的设计,也就是针对接口编程,不要针对实现编程。具体的实现类可以任意的被修改,如果针对具体是实现来编程,那么很容易牵一发而动全身,使得不同类之间耦合性太强。
(3)LoD(迪米特法则,Law of Demeter)
只与你直接的朋友通信,而避免和陌生人通信。这也属于类与类之间的解耦性,其目的还是高内聚低耦合。
二、简单工厂模式
1、要点
-简单工厂模式也被称为静态工厂模式,就是工厂类一般是使用静态方法,通过接受的参数的不同来返回不同的对象实例。
-对于增加新产品无能为力不修改代码的话,是无法扩展的。
2、利用简单工厂模式进行简单的实现
为了模拟简单工厂的设计模式,我们先讲述一个生活中的简单背景吧。比如现在建造汽车,对于一个用户而言,并不需要知道建造汽车的每一个细节,仅仅需要知道自己需要什么车就好了,比如“比亚迪”,“奥迪”等等。所以我们根据这种简单工厂的设计模式,就可以将其进行实例化。具体如下所示:
(1)因为需要依据接口进行编程,所以先建立一个汽车的接口Car
public interface Car { void run();}
接口中的run方法,在可以根据后面的需要进行具体实现。
(2)依据我们的简单假设,设计两款车——比亚迪和奥迪。所以两种不同的类就又产生了
public class Audi implements Car{ @Override public void run() { System.out.println("奥迪跑!"); }}
public class Byd implements Car{ @Override public void run() { System.out.println("比亚迪跑!"); }}
(3)假如我们不通过工厂模式进行建造这两类车,那么可以直接使用new方法进行创建
public static void main(String[] args) { Audi car1 = new Audi(); Byd car2 = new Byd(); car1.run(); car2.run(); }
tips:
这种情况属于没有使用工厂模式时的建造方法,用户需要自己去构建相应的汽车,反映在代码里面的就是使用new方法来构建。这如果对比在现实生活中,就类似于用户需要知道如何去构建一辆汽车。
(4)建造工厂类,通过工厂类实现用户的需求
public class CarFactory { public static Car createCar(String type) { if("奥迪".equals(type)) { return new Audi(); }else if("比亚迪".equals(type)) { return new Byd(); }else { return null; } } }
tips:
在这里我们将每一辆车的具体实现交给了一个工厂类——CarFactory,通过代码我们也可以看到,对于不同的汽车,我们的工厂通过传输进来的参数进行区分。
(5)利用简单工厂模式,我们再来观察一下建造一个汽车的流程。
public static void main(String[] args) { Car car1 = CarFactory.createCar("奥迪"); Car car2 = CarFactory.createCar("比亚迪"); car1.run(); car2.run(); }
tips:
1:与普通模式(3)相比,在简单工厂模式中,我们只需要调用工厂类CarFactory,并调用其中的建造方法——createCar,并传递用户需要的参数就好了。对用户而言,并不需要知道构造一辆汽车的具体方法和步骤。
2:虽然在(3)和(5)中,用户需要构建一辆汽车的代码数量并没有减少,但是这仅仅是因为我们涉及的实例背景是十分简单的一个例子。对比两者的思想,普通模式(3)更加要求用户对创建汽车细节的掌握,而简单工厂模式(5),主要是将整个创建汽车的细节全部封装在工厂中。假如遇到一个更加复杂的工艺流程,可以明显的感受到简单工厂模式的便利之处。
3:但是简单工厂也有一个很明显的缺点,就是不满足我们之前提到的OCP开闭原则。假如我们需要新增一款汽车的建造方法,比如说“奔驰”,那么我们就需要在CarFactory中新增“type”类型识别,以及相应的创建流程。而这些改动,都是在源代码块CarFactory中进行实现的,而不是直接进行扩展所得。
三、工厂方法模式
1、要点
-为了避免简单工厂模式的缺点,不完全满足OCP。
-工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类。工厂方法模式属于简单工厂模式的一个升级版。
2、模式实现
在前面的简单工厂模式中,不满足OCP原则的核心在于,我们将CarFactory设计成为了一个“混乱”的类。每一种汽车的构建生产,我们都将其构造方法放入到CarFactory中,这样就会导致所有不同汽车的构造都依赖于这一个类中进行实现,所以新增汽车的构造也需要在CarFactory中进行实现,这样就大大的提升了每个类的耦合性,使得工厂中的功能太过于杂乱。所以针对这个问题,就有相应的工厂方法模式对其进行解决。工厂方法模式直接在简单工厂模式的基础上,更加细分CarFactory的每一种功能,将CarFactory也定义为一个接口,然后每一种不同的汽车,单独创建一个工厂,并且实现CarFactory接口的功能。这样使用一组单一汽车的制造工厂来代替一个混合汽车制造工厂。下面来具体的说明其中的改变。
(1)将CarFactory从实现类改为一个接口
public interface CarFactory { Car createCar();}
(2)针对每一款汽车,构造不一样的制造工厂
public class AudiCarFactory implements CarFactory { @Override public Car createCar() { return new Audi(); }}
public class BydCarFactory implements CarFactory{ @Override public Car createCar() return new Byd(); }}
(3)此时我们在构建“比亚迪”和“奥迪”的时候,程序就成为下面这样
public static void main(String[] args) { Car c1 = new AudiCarFactory().createCar(); Car c2 = new BydCarFactory().createCar(); c1.run(); c2.run(); }
tips:
1:在简单工厂中,我们使用的createCar是CarFactory中的静态方法,所以不需要new一个CarFactory对象,可以直接调用此方法。然而在工厂方法模式中,CarFactory是一个接口,其他的汽车构建类都是实现该接口来重写创建方法,所以每种不同汽车的createCar方法都无法设置为静态方法,在调用的时候需要依赖于一个实现类,所以需要new每一个汽车构建工厂类。
2:与此同时,如果我们现在需要新增一辆“奔驰”车的构建方式,那么我们的做法也很简单。在不改动源代码的情况下,我们分别增加一个“奔驰”车类,和一个“奔驰”车的建造工厂就可以直接实现新增车辆的生产了。实现如下:
public class Benz implements Car { @Override public void run() { System.out.println("奔驰在跑"); }}
public class BenzCarFactory implements CarFactory{ @Override public Car createCar() { return new Benz(); }}
此时,用户端的操作为
Car c3 = new BenzCarFactory().createCar();
通过这种方式,也就直接对生产工厂与不同的车种进行区分,完美的实现了OCP原则。
四、简单工厂模式和工厂方法模式对比:
1、结构复杂度
简单工厂模式占优。简单工厂模式只需要一个工厂类,而工厂模式的工厂类随着产品类个数的增加而增加,这无疑会使类的个数越来越多,从而增加了结构的复杂程度。
2、代码复杂度
简单工厂模式在结构方面相对简洁,在代码方面就会比工厂方法模式复杂。简单工厂模式的工厂类随着产品类的增加需要增加很多方法(或代码),而工厂方法模式每个具体工厂类只完成单一任务,代码简洁。
3、客户端编程难度
工厂方法模式对染在工厂结构中引入了接口从而满足了OCP ,但是在客户端编码中需要对工厂类进行实例化,而简单工厂模式的工厂类是个静态类,在客户端无需实例化。
4、实际选择
根据设计理论建议,我们应该选择工厂方法模式,但是在实际上,我们一般都用简单工厂模式。其实也很好理解,就好比通信中的七层网络结构属于理论中最优的,然而在实际的使用中,都是用的是TCP/IP四层结构,其实归根到底,理论上的最优都是需要付出一定的代价,比如代码的复杂程度,建设成本的增加等等,这些在实际的使用中都需要加以考虑。而在此处,我们通过对比也可以明显的感觉到简单工厂模式的简洁方便之处,所以一般选择简单工厂模式。
五、抽象工厂模式
1、要点
-用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
-抽象工厂模式在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。
2、具体实现
其实抽象工厂模式也是很具体的一个例子。以我们上面的建造汽车的过程而言,在我们现实生活中,我们可以粗略的将其分为低端汽车和高端汽车,每种汽车对应的发动机、座椅、轮胎等等都可以加以区分高端和低端。我们设置为高端汽车使用高端的部件,而低端汽车使用低端的部件。然后就可以通过每种汽车整体,来获取其中的相关部件。例如:
(1)首先规定发动机、座椅、轮胎均有高端和低端两种质量
public interface Engine { void run(); void start();} class LuxuryEngine implements Engine{ @Override public void run() { System.out.println("高端发动机转的快!"); } @Override public void start() { System.out.println("高端发动机可以自启动!"); }} class LowEngine implements Engine{ @Override public void run() { System.out.println("低端发动机转的慢!"); } @Override public void start() { System.out.println("低端发动机不可以自启动!"); }}
这里仅仅将发动机的构建类代码放在这里,座椅和轮胎的构建与其相同,都是先定义一个座椅或者轮胎接口,然后再具体的实现高端和低端产品类。
(2)再建立一个相应汽车构建工厂接口
public interface CarFactory { Engine createEngine(); Seat createSeat(); Tyre createTyre();}
分别实现高端汽车和低端汽车的构建
public class LuxuryCarFactory implements CarFactory { @Override public Engine createEngine() { return new LuxuryEngine(); } @Override public Seat createSeat() { return new LuxurySeat(); } @Override public Tyre createTyre() { return new LuxuryTyre(); }}
这里仅列举出高端汽车的构建工厂,低端汽车构建工厂与之类似。
(3)我们在用户端进行测试一下
public class Client { public static void main(String[] args) { CarFactory c1 = new LuxuryCarFactory(); Engine e1 = c1.createEngine(); Seat s1 = c1.createSeat(); Tyre t1 = c1.createTyre(); e1.run(); e1.start(); s1.massage(); t1.revolve(); System.out.println("####################"); CarFactory c2 = new LowCarFactory(); Engine e2 = c2.createEngine(); Seat s2 = c2.createSeat(); Tyre t2 = c2.createTyre(); e2.run(); e2.start(); s2.massage(); t2.revolve(); }}
检查一下结果

tips:
如上所示,我们通过高端汽车工厂产生一个高端汽车对象,然后可以通过这个对象获取一整个产品族。这就是我们抽象工厂模式的主要思想,这样可以帮助在我们生产一系列杂乱的对象的时候,有一个更加清晰的思路,也便于我们操作。
六、工厂模式要点
-简单工厂模式:虽然某种程度不符合设计原则,但实际使用最多。
-工厂方法模式:不修改已有类的前提下,通过增加新的工厂类实现扩展。
-抽象工厂模式:不可以增加产品,可以增加产品族!
七、对于工厂模式的一点理解
通过介绍工厂模式,其实仔细体会其中的逻辑,也就是我们社会进化的一种内在逻辑。
在最初的农耕社会,我们需要自己去了解如何种地,播种,收割,将农作物转化为面粉等等流程,每个人的知识面很宽广,但是并不利于效率的提升。而在我们现在的社会中,生产方式被不断的改进,种植人员仅需要知道种植,不负责后面的任何其他事情。这也就体现了社会分工明确的一种优势,每个人知道的很少,但却很专注其中某一项,这样可以大大的提升效率。
就好比刚刚的简单工厂模式,用户在生产汽车的时候,甚至可以不知道new方法,只要知道createCar方法就够了,在createCar方法背后发生了什么,并不属于用户需要考虑的问题。在后面的工厂方法模式中,同样是对耦合性的降低,只要做到少依赖同一个类,这样的系统才会更加稳定牢固,这种思想又恰恰是现在社会进步的一种模式,世界的多极化背后,也同样是基于这样一个理论。所以设计模式的学习,更重要的应该是去理解模式背后的逻辑是什么,应用的方面也就不止于编程上了!