前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >第21次文章:工厂模式

第21次文章:工厂模式

作者头像
鹏-程-万-里
发布2019-09-28 18:03:30
2880
发布2019-09-28 18:03:30
举报

在我们GOFO23种设计模式中,上期着重介绍了创造型模式中的单例模式,这周我们着重介绍另一种创造型设计模式——工厂模式。

一、工厂模式

-实现了创建者和调用者的分离。

1、详细分类

-简单工厂模式

-工厂方法模式

-抽象工厂模式

2、面向对象设计的基本原则

(1)OCP(开闭原则,Open-Closed Principle)

一个软件的实体,在后期的升级改造过程中,我们不应该通过修改其源代码来完成我们的目的,而是通过在源代码的基础上增加新的模块来完成我们需要的功能。

(2)DIP(依赖倒转原则,Dependence Inversion Principle)

在完成每一个类的功能时,我们最好是通过实现接口的方式来实现每一个功能模块的设计,也就是针对接口编程,不要针对实现编程。具体的实现类可以任意的被修改,如果针对具体是实现来编程,那么很容易牵一发而动全身,使得不同类之间耦合性太强。

(3)LoD(迪米特法则,Law of Demeter)

只与你直接的朋友通信,而避免和陌生人通信。这也属于类与类之间的解耦性,其目的还是高内聚低耦合。

二、简单工厂模式

1、要点

-简单工厂模式也被称为静态工厂模式,就是工厂类一般是使用静态方法,通过接受的参数的不同来返回不同的对象实例。

-对于增加新产品无能为力不修改代码的话,是无法扩展的。

2、利用简单工厂模式进行简单的实现

为了模拟简单工厂的设计模式,我们先讲述一个生活中的简单背景吧。比如现在建造汽车,对于一个用户而言,并不需要知道建造汽车的每一个细节,仅仅需要知道自己需要什么车就好了,比如“比亚迪”,“奥迪”等等。所以我们根据这种简单工厂的设计模式,就可以将其进行实例化。具体如下所示:

(1)因为需要依据接口进行编程,所以先建立一个汽车的接口Car

代码语言:javascript
复制
public interface Car {  void run();}

接口中的run方法,在可以根据后面的需要进行具体实现。

(2)依据我们的简单假设,设计两款车——比亚迪和奥迪。所以两种不同的类就又产生了

代码语言:javascript
复制
public class Audi implements Car{  @Override  public void run() {    System.out.println("奥迪跑!");  }}
代码语言:javascript
复制
public class Byd implements Car{  @Override  public void run() {    System.out.println("比亚迪跑!");    }}

(3)假如我们不通过工厂模式进行建造这两类车,那么可以直接使用new方法进行创建

代码语言:javascript
复制
  public static void main(String[] args) {    Audi car1 = new Audi();    Byd car2 = new Byd();        car1.run();    car2.run();  }

tips:

这种情况属于没有使用工厂模式时的建造方法,用户需要自己去构建相应的汽车,反映在代码里面的就是使用new方法来构建。这如果对比在现实生活中,就类似于用户需要知道如何去构建一辆汽车。

(4)建造工厂类,通过工厂类实现用户的需求

代码语言:javascript
复制
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)利用简单工厂模式,我们再来观察一下建造一个汽车的流程。

代码语言:javascript
复制
  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从实现类改为一个接口

代码语言:javascript
复制
public interface CarFactory {  Car createCar();}

(2)针对每一款汽车,构造不一样的制造工厂

代码语言:javascript
复制
public class AudiCarFactory implements CarFactory {  @Override  public Car createCar() {    return new Audi();  }}
代码语言:javascript
复制
public class BydCarFactory implements CarFactory{  @Override  public Car createCar()     return new Byd();  }}

(3)此时我们在构建“比亚迪”和“奥迪”的时候,程序就成为下面这样

代码语言:javascript
复制
  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:与此同时,如果我们现在需要新增一辆“奔驰”车的构建方式,那么我们的做法也很简单。在不改动源代码的情况下,我们分别增加一个“奔驰”车类,和一个“奔驰”车的建造工厂就可以直接实现新增车辆的生产了。实现如下:

代码语言:javascript
复制
public class Benz implements Car {  @Override  public void run() {    System.out.println("奔驰在跑");  }}
代码语言:javascript
复制
public class BenzCarFactory implements CarFactory{  @Override  public Car createCar() {    return new Benz();  }}

此时,用户端的操作为

代码语言:javascript
复制
Car c3 = new BenzCarFactory().createCar();

通过这种方式,也就直接对生产工厂与不同的车种进行区分,完美的实现了OCP原则。

四、简单工厂模式和工厂方法模式对比:

1、结构复杂度

简单工厂模式占优。简单工厂模式只需要一个工厂类,而工厂模式的工厂类随着产品类个数的增加而增加,这无疑会使类的个数越来越多,从而增加了结构的复杂程度。

2、代码复杂度

简单工厂模式在结构方面相对简洁,在代码方面就会比工厂方法模式复杂。简单工厂模式的工厂类随着产品类的增加需要增加很多方法(或代码),而工厂方法模式每个具体工厂类只完成单一任务,代码简洁。

3、客户端编程难度

工厂方法模式对染在工厂结构中引入了接口从而满足了OCP ,但是在客户端编码中需要对工厂类进行实例化,而简单工厂模式的工厂类是个静态类,在客户端无需实例化。

4、实际选择

根据设计理论建议,我们应该选择工厂方法模式,但是在实际上,我们一般都用简单工厂模式。其实也很好理解,就好比通信中的七层网络结构属于理论中最优的,然而在实际的使用中,都是用的是TCP/IP四层结构,其实归根到底,理论上的最优都是需要付出一定的代价,比如代码的复杂程度,建设成本的增加等等,这些在实际的使用中都需要加以考虑。而在此处,我们通过对比也可以明显的感觉到简单工厂模式的简洁方便之处,所以一般选择简单工厂模式。

五、抽象工厂模式

1、要点

-用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)

-抽象工厂模式在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。

2、具体实现

其实抽象工厂模式也是很具体的一个例子。以我们上面的建造汽车的过程而言,在我们现实生活中,我们可以粗略的将其分为低端汽车和高端汽车,每种汽车对应的发动机、座椅、轮胎等等都可以加以区分高端和低端。我们设置为高端汽车使用高端的部件,而低端汽车使用低端的部件。然后就可以通过每种汽车整体,来获取其中的相关部件。例如:

(1)首先规定发动机、座椅、轮胎均有高端和低端两种质量

代码语言:javascript
复制
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)再建立一个相应汽车构建工厂接口

代码语言:javascript
复制
public interface CarFactory {  Engine createEngine();  Seat createSeat();  Tyre createTyre();}

分别实现高端汽车和低端汽车的构建

代码语言:javascript
复制
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)我们在用户端进行测试一下

代码语言:javascript
复制
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方法背后发生了什么,并不属于用户需要考虑的问题。在后面的工厂方法模式中,同样是对耦合性的降低,只要做到少依赖同一个类,这样的系统才会更加稳定牢固,这种思想又恰恰是现在社会进步的一种模式,世界的多极化背后,也同样是基于这样一个理论。所以设计模式的学习,更重要的应该是去理解模式背后的逻辑是什么,应用的方面也就不止于编程上了!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java小白成长之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、工厂模式
    • 1、详细分类
      • 2、面向对象设计的基本原则
      • 二、简单工厂模式
        • 1、要点
          • 2、利用简单工厂模式进行简单的实现
          • 三、工厂方法模式
            • 1、要点
              • 2、模式实现
              • 四、简单工厂模式和工厂方法模式对比:
                • 1、结构复杂度
                  • 2、代码复杂度
                    • 3、客户端编程难度
                      • 4、实际选择
                      • 五、抽象工厂模式
                        • 1、要点
                          • 2、具体实现
                          • 六、工厂模式要点
                          • 七、对于工厂模式的一点理解
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档