23种设计模式-工厂模式(2)

102 阅读14分钟

本章源码地址:github.com/Technicolor…

定义

        工厂模式是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

        工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。

 

工厂模式根据抽象程度的不同分为三种:

简单工厂模式(也叫静态工厂模式)

工厂方法模式(也叫多形性工厂)

抽象工厂模式(也叫工具箱)

 

简单工厂模式

        实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。

1、分步说明引子:从无到有。如果不使用工厂,客户将自己创建宝马车,然后拿来用。

 

public class BMW320 {
	public BMW320(){
		System.out.println("制造-->BMW320");
	}
}
 
public class BMW523 {
	public BMW523(){
		System.out.println("制造-->BMW523");
	}
}
 
public class Customer {
	public static void main(String[] args) {
		BMW320 bmw320 = new BMW320();
		BMW523 bmw523 = new BMW523();
	}
}

客户需要知道怎么去创建一款车,客户和车就紧密耦合在一起了.为了降低耦合,就出现了工厂类,把创建宝马的操作细节都放到了工厂里面去,客户直接使用工厂的创建工厂方法,传入想要的宝马车型号就行了,而不必去知道创建的细节.这就是工业革命了:简单工厂模式。

2、简单工厂模式:我们建立一个工厂类方法来制造新的对象。如图:

产品类:

abstract class BMW {
    public BMW(){
        
    }
}
 
public class BMW320 extends BMW {
    public BMW320() {
        System.out.println("制造-->BMW320");
    }
}
public class BMW523 extends BMW{
    public BMW523(){
        System.out.println("制造-->BMW523");
    }
}

工厂类:

public class Factory {
    public BMW createBMW(int type) {
        switch (type) {
        
        case 320:
            return new BMW320();
 
        case 523:
            return new BMW523();
 
        default:
            break;
        }
        return null;
    }
}

客户类:

public class Customer {
    public static void main(String[] args) {
        Factory factory = new Factory();
        BMW bmw320 = factory.createBMW(320);
        BMW bmw523 = factory.createBMW(523);
    }
}

简单工厂模式存在的目的很简单,即定义一个用于创建对象的接口。将对象的创建和对象本身的业务处理分离了,可以降低系统的耦合度,使得两者修改起来相对容易些,当以后实现改变时,只需要修改工厂类即可。

3、组成:

(1)工厂类角色: 这是本模式的核心,含有一定的商业逻辑和判断逻辑,用来创建产品

(2)抽象产品角色:它一般是具体产品继承的父类或者实现的接口。   

(3)具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。

4、优点:

(1)简单工厂模式实现了对责任的分割,提供了专门的工厂类用于创建对象,实现对象的创建和对象的使用分离。

(2)客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。

(3)通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。

5、缺点:

 (1)由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。

 (2)对产品部分来说,它是符合开闭原则的,但是工厂部分不符合,系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。

6、使用场景:

(1)工厂类负责创建的对昂比较少;

(2)客户端只知道传入工厂类的参数,对于如何创建对象不关心。

为了解决简单工厂模式的问题,于是工厂方法模式出现了。工厂类定义成了接口,而每新增的车种类型,就增加该车种类型对应工厂类的实现,这样工厂的设计就可以扩展了,而不必去修改原来的代码。

工厂方法模式

        工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式让实例化推迟到子类。并使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的工厂类。分担了对象承受的压力;而且这样使得结构变得灵活起来——当有新的产品产生时,只要按照抽象产品角色、抽象工厂角色提供的接口来生成,那么就可以被客户使用,而不必去修改任何已有的代码。可以看出工厂角色的结构也是符合开闭原则的!  

1、工厂方法模式组成: 
(1)抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。 
(2)具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。 
(3)抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。 
(4)具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。 

2、代码如下:

产品类:

abstract class BMW {
    public BMW(){
        
    }
}
public class BMW320 extends BMW {
    public BMW320() {
        System.out.println("制造-->BMW320");
    }
}
public class BMW523 extends BMW{
    public BMW523(){
        System.out.println("制造-->BMW523");
    }
}


工厂类:

interface FactoryBMW {
    BMW createBMW();
}
 
public class FactoryBMW320 implements FactoryBMW{
 
    @Override
    public BMW320 createBMW() {
 
        return new BMW320();
    }
 
}
public class FactoryBMW523 implements FactoryBMW {
    @Override
    public BMW523 createBMW() {
 
        return new BMW523();
    }
}


客户类:

public class Customer {
    public static void main(String[] args) {
        FactoryBMW320 factoryBMW320 = new FactoryBMW320();
        BMW320 bmw320 = factoryBMW320.createBMW();
 
        FactoryBMW523 factoryBMW523 = new FactoryBMW523();
        BMW523 bmw523 = factoryBMW523.createBMW();
    }
}


3、优点:

(1) 在工厂方法中,用户只需要知道所要产品的具体工厂,无须关系具体的创建过程,甚至不需要具体产品类的类名。

(2)在系统增加新的产品时,我们只需要添加一个具体产品类和对应的实现工厂,无需对原工厂进行任何修改,很好地符合了“开闭原则”。

4、缺点:

(1)每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

5、小结:

(1)工厂方法模式完全符合“开闭原则”。

(2)工厂方法模式使用继承,通过子类实现工厂方法来创建对象。将对象的创建和实例化委托给子类。在这里我们要明白这并不是工厂来决定生成哪种产品,而是在编写抽象工厂类时,不需要知道实际创建的产品是哪个;但是当选择了使用哪个子类,就已经决定了实际创建的产品时哪个了。

 

抽象工厂模式

        当有多个抽象角色时使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品对象。它有多个抽象产品类,每个抽象产品类可以派生出多个具体产品类,一个抽象工厂类,可以派生出多个具体工厂类,每个具体工厂类可以创建多个具体产品类的实例。

随着客户的要求越来越高,宝马车需要不同配置的空调和发动机等配件。于是这个工厂开始生产空调和发动机,用来组装汽车。这时候工厂有两个系列的产品:空调和发动机。宝马320系列配置A型号空调和A型号发动机,宝马230系列配置B型号空调和B型号发动机。

 

一、概念:
在工厂方法模式中,我们使用一个工厂创建一个产品,也就是说一个具体的工厂对应一个具体的产品。但是有时候我们需要一个工厂能够提供多个产品对象,而不是单一的对象,这个时候我们就需要使用抽象工厂模式。

在讲解抽象工厂模式之前,我们需要厘清两个概念:

(1)产品等级结构:产品的等级结构也就是产品的继承结构。例如一个为空调的抽象类,它有海尔空调、格力空调、美的空调等一系列的子类,那么这个抽象类空调和他的子类就构成了一个产品等级结构。

(2)产品族:产品族是在抽象工厂模式中的。在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。比如,海尔工厂生产海尔空调。海尔冰箱,那么海尔空调则位于空调产品族中。

产品等级结构和产品族结构示意图如下:

 

二、基本定义:
抽象工厂模式提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。抽象工厂允许使用抽象的接口来创建一组相关的产品,而不需要关心实际产出的具体产品是什么,这样一来,客户就可以从具体的产品中被解耦。

如宝马320系列使用空调型号A和发动机型号A,而宝马230系列使用空调型号B和发动机型号B,那么使用抽象工厂模式,在为320系列生产相关配件时,就无需制定配件的型号,它会自动根据车型生产对应的配件型号A。

UML结构图如下:

AbstractFactory:抽象工厂。抽象工厂定义了一个接口,所有的具体工厂都必须实现此接口,这个接口包含了一组方法用来生产产品。

ConcreteFactory:具体工厂。具体工厂是用于生产不同产品族。要创建一个产品,客户只需要使用其中一个工厂完全不需要实例化任何产品对象。

AbstractProduct:抽象产品。这是一个产品家族,每一个具体工厂都能够生产一整组产品。

Product:具体产品。

三、代码:
结合本例如下:

当每个抽象产品都有多于一个的具体子类的时候(空调有型号A和B两种,发动机也有型号A和B两种),工厂角色怎么知道实例化哪一个子类呢?比如每个抽象产品角色都有两个具体产品(产品空调有两个具体产品空调A和空调B)。抽象工厂模式提供两个具体工厂角色(宝马320系列工厂和宝马230系列工厂),分别对应于这两个具体产品角色,每一个具体工厂角色只负责某一个产品角色的实例化。每一个具体工厂类只负责创建抽象产品的某一个具体子类的实例。

产品类:

//发动机以及型号  
public interface Engine {  
 
}  
public class EngineA extends Engine{  
    public EngineA(){  
        System.out.println("制造-->EngineA");  
    }  
}  
public class EngineBextends Engine{  
    public EngineB(){  
        System.out.println("制造-->EngineB");  
    }  
}  
 
//空调以及型号  
public interface Aircondition {  
 
}  
public class AirconditionA extends Aircondition{  
    public AirconditionA(){  
        System.out.println("制造-->AirconditionA");  
    }  
}  
public class AirconditionB extends Aircondition{  
    public AirconditionB(){  
        System.out.println("制造-->AirconditionB");  
    }  
} 


创建工厂类:

//创建工厂的接口  
public interface AbstractFactory {  
    //制造发动机
    public Engine createEngine();
    //制造空调 
    public Aircondition createAircondition(); 
}  
 
 
//为宝马320系列生产配件  
public class FactoryBMW320 implements AbstractFactory{  
      
    @Override  
    public Engine createEngine() {    
        return new EngineA();  
    }  
    @Override  
    public Aircondition createAircondition() {  
        return new AirconditionA();  
    }  
}  
//宝马523系列
public class FactoryBMW523 implements AbstractFactory {  
  
     @Override  
    public Engine createEngine() {    
        return new EngineB();  
    }  
    @Override  
    public Aircondition createAircondition() {  
        return new AirconditionB();  
    }  
} 


客户:

public class Customer {  
    public static void main(String[] args){  
        //生产宝马320系列配件
        FactoryBMW320 factoryBMW320 = new FactoryBMW320();  
        factoryBMW320.createEngine();
        factoryBMW320.createAircondition();
          
        //生产宝马523系列配件  
        FactoryBMW523 factoryBMW523 = new FactoryBMW523();  
        factoryBMW320.createEngine();
        factoryBMW320.createAircondition();
    }  
}


四、优缺点:
1、优点:

(1) 抽象工厂隔离了具体类的生成,是的客户端不需要知道什么被创建。所有的具体工厂都实现了抽象工厂中定义的公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。

(2)当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。

2、缺点:

(1)添加新的行为时比较麻烦。如果需要添加一个新产品族对象时,需要更改接口及其下所有子类,这必然会带来很大的麻烦。

 

五、使用场景:
(1)一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。

(2)系统中有多于一个的产品族,而每次只使用其中某一产品族。

(3)属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。

(4)系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。

 

工厂模式的优点:

        1、一个调用者想创建一个对象,只要知道其名称就可以了,降低了耦合度。

        2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。使得代码结构更加清晰。

        3、屏蔽产品的具体实现,调用者只关心产品的接口。

 

工厂模式的缺点:

        每次增加一个产品时,都需要增加一个具体类和对象实现工厂(这里可以使用反射机制来避免),使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。所以对于简单对象来说,使用工厂模式反而增加了复杂度。

 

工厂模式的适用场景:

        1,  一个对象拥有很多子类。

        2,  创建某个对象时需要进行许多额外的操作。

        3,  系统后期需要经常扩展,它把对象实例化的任务交由实现类完成,扩展性好。

 

关于Java中的工厂模式的一些常见问题:

        利用父类的向下转型(使用父类类型的引用指向子类的对象)是可以达到类似于工厂模式的效果的,那为什么还要用工厂模式呢?

        把指向子类对象的父类引用赋给子类引用叫做向下转型,如: 

 

Class Student extends Person     
Person s = new Student();    
s = (Student)person ;

 

        使用向下转型在客户端实例化子类的时候,严重依赖具体的子类的名字。当我们需要更改子类的构造方法的时候,比如增加一个参数,或者更改了子类的类名,所有的new出来的子类都需要跟着更改。    

        但如果我们使用工厂模式,我们仅仅需要在工厂中修改一下new的代码,其余项目中用到此实例的都会跟着改,而不需要我们手动去操作。(???)

 

总结:

        无论是简单工厂模式、工厂模式还是抽象工厂模式,它们本质上都是将不变的部分提取出来,将可变的部分留作接口,以达到最大程度上的复用。究竟用哪种设计模式更适合,这要根据具体的业务需求来决定。

 

参考文章:

blog.csdn.net/a745233700/…

blog.csdn.net/jason0539/a…

blog.csdn.net/chenssy/art…