设计模式与范式 --- 创建型模式(工厂模式)

1.写在前

在介绍工厂模式之前,我们先思考如下场景:

有一天,林同学准备去买笔记本,他到商城发现有两款电脑他特别喜欢, 一款是 Macbook Pro, 另一款是 Surface Pro。

根据以上的场景,类图可以如下表示:

原始场景

代码实现:

interface Computer {
    public void printComputer();
}

class MacbookProComputer implements Computer {
    public void printComputer() {
        System.out.println("This is a macbook pro");
    }
}

class SurfaceBookComputer implements Computer {
    public void printComputer() {
        System.out.println("This is a surface book");
    }
}

public class Client {
    public void buy(Computer c){
        c.printComputer();
    }
    
    public static void main(String[] args) {
        Client c = new Client();
        c.buy(new SurfaceBookComputer());
    }
}

这时候问题就来了,客户只关心得到电脑,并不关心电脑是如何被生产出来的,假设这里电脑是由鼠标,键盘和显示器组成,那么我们还需要先创建这些类实例,才能创建电脑。

Keyboard keyboard = new Keyboard();
Display display = new Display();
Mouse mouse = new Mouse();
Client c = new Client();
c.buy(new SurfaceBookComputer(keyboard, display, mouse));

显然商家在卖电脑的时候不会要求客户自己把电脑组装好的吧。

2. 简单工厂模式

简单工厂模式:专门定义一个类用来创建其它类的实例,被创建的实例通常都具有共同的父类。

这里我们相当于是创建生产电脑的工厂,客户需要购买什么样的电脑,只要输入类型编号就可以获取该电脑,而无需知道该电脑是如何被生产出来的。

简单工厂模式

代码实现:

// 抽象产品类(接口)
interface Computer {
    public void printComputer();
}
// 具体产品类
class MacbookProComputer implements Computer {
    public void printComputer() {
        System.out.println("This is a macbook pro");
    }
}

class SurfaceBookComputer implements Computer {
    public void printComputer() {
        System.out.println("This is a surface book");
    }
}

// 创建电脑的工厂类(工厂角色)
class ComputerFactory {
    public Computer createComputer(String type) {
        Computer c = null;
        if(type.equals("macbook")) {
            c = new MacbookProComputer();
        }else if(type.equals("surface")) {
            c = new SurfaceBookComputer();
        }
        return c;
    }
}

public class Client {
    public void buy(Computer c){
        System.out.println("I buy a computer");
        c.printComputer();
    }
    
    public static void main(String[] args) {
        Client c = new Client();
        // 创建电脑的工厂类
        ComputerFactory cf = new ComputerFactory();

        Computer computer = cf.createComputer("macbook");
        c.buy(computer);
    }
}

工厂模式的角色一般包括:

  1. 工厂角色: 如上图的 ComputerFactory,它可以被客户端调用,其内部用于负责创建具体的对象。
  2. 抽象产品类:如上图的 Computer,它描述了所有实例的公共接口。
  3. 具体产品类:如上图的 MacbookProComputer,实现抽象产品的接口,是工厂角色中要创建的具体实例。

简单工厂模式的优点【工厂角色负责产生具体的实例对象,所以在工厂类中需要有必要的逻辑,通过客户的输入能够得到具体创建的实例;所以客户端就不需要感知具体对象是如何产生的,只需要将必要的信息提供给工厂即可。】

这时候负责这个工厂的产品经理说该工厂需要生产新的产品 Macbook Air, 那么该方法的缺点就暴露显现,一般工厂生产方法和具体产品实现一般是由不同工程师开发的,那么如果实现产品的工程师早早实现了新产品而工厂方法却一直没有更新,那么该产品就一直无法上架。其次代码耦合度太高,如果工厂方法由新来的工程师去修改的话,那么他又得先读懂源代码,效率显得低下。

所以简单工厂模式的缺点:简单工厂模式是违反“开闭原则”,即对扩展开放,对修改关闭;因为如果要新增具体产品,就需要修改工厂类的代码。

针对简单工厂模式暴露出来的弊端,我们需要对代码再进行改进,由此延伸出工厂方法模式。

3. 工厂方法模式

工厂方法模式:定义一个用来创建对象的接口,让子类决定实例化哪一个类,让子类决定实例化延迟到子类。

工厂方法模式是针对每个产品提供一个工厂类,在客户端中判断使用哪个工厂类去创建对象。

我们将之前的 ComputerFactory 抽象成一个接口,那么创建相应具体的工厂类去实现该接口的方法。

具体类图的实现:

工厂方法模式

代码实现:

// 抽象产品类(接口)
interface Computer {
    public void printComputer();
}
// 具体产品类
class MacbookProComputer implements Computer {
    public void printComputer() {
        System.out.println("This is a macbook pro");
    }
}

class SurfaceBookComputer implements Computer {
    public void printComputer() {
        System.out.println("This is a surface book");
    }    
}

// 工厂角色:所有实例(创建电脑)的公共接口
interface ComputerFactory {
    public Computer createComputer();
}
// 具体实例化的工厂类
class MsFactory implements ComputerFactory {
    public Computer createComputer(){
        return new SurfaceBookComputer();
    }
}

class AppleFactory implements ComputerFactory {
    public Computer createComputer() {
        return new MacbookProComputer();
    }
}

public class Client {
    public void buy(Computer c){
        System.out.println("I buy a computer");
        c.printComputer();
    }
    public static void main(String[] args) {
        Client c = new Client();
        // 用户根据需要创建对应的实例
        ComputerFactory cf = new AppleFactory();

        Computer computer = cf.createComputer();
        c.buy(computer);
    }
}

4.抽象工厂模式

这时候负责该工厂的产品经理说要生产新的一类产品操作系统 Mac Os 和 Windows 8,这时候就引申出了抽象工厂模式。

抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

工厂方法模式和抽象工厂模式基本类似,可以这么理解:当工厂只生产一个产品的时候,即为工厂方法模式,而工厂如果生产两个或以上的商品即变为抽象工厂模式。

我们在抽象工厂接口中新增创建系统的方法,并由实例工厂类去实现。

类图可由下表示:

抽象工厂模式

代码实现:

// 抽象产品类(接口)
interface Computer {
    public void printComputer();
}

// 具体产品类
class MacbookProComputer implements Computer {
    public void printComputer() {
        System.out.println("This is a macbook pro");
    }
}

class SurfaceBookComputer implements Computer {
    public void printComputer() {
        System.out.println("This is a surface book");
    }
}

// 工厂角色:所有实例(操作系统)的公共接口
interface OperatingSystem {
    public void printSystem();
}

// 实例化的工厂类
class MacOsSystem implements OperatingSystem {
    public void printSystem() {
        System.out.println("This is a mac os");
    }
}

class Windows8System implements OperatingSystem {
    public void printSystem() {
        System.out.println("This is a window 8");
    }
}

// 抽象工厂:提供一系列相关或者相互依赖对象的接口!
interface ProductionFactory {
    public Computer createComputer();
    public OperatingSystem createSystem();
}

// 实例化抽象工厂
class AppleFactory implements ProductionFactory {
    public Computer createComputer() {
        return new MacbookProComputer();
    }
    public OperatingSystem createSystem() {
        return new MacOsSystem();
    }
}

class MsFactory implements ProductionFactory {
    public Computer createComputer() {
        return new SurfaceBookComputer();
    }
    public OperatingSystem createSystem() {
        return new Windows8System();
    }
}

public class Client {
    public void buy(Computer c){
        System.out.println("I buy a computer");
        c.printComputer();
    }
    
    public void use(OperatingSystem s) {
        System.out.println("Operating System");
        s.printSystem();
    }
    
    public static void main(String[] args) {
        // 创建一个抽象工厂的实现类
        ProductionFactory pf = new AppleFactory();
        // 创建具体工厂的实现类
        Computer c = pf.createComputer();
        OperatingSystem s = pf.createSystem();

        Client client = new Client();
        client.buy(c);
        client.use(s);
    }
}

抽象工厂模式的缺点在于产品类的扩展,将会是十分费力的,假如在需要加入新的产品,那么几乎所有的工厂类都需要进行修改,所以在使用抽象工厂模式时,对产品等级结构的划分是十分重要的。

5.总结

总结工厂模式的概念:

  • 简单工厂模式:专门定义一个类用来创建其它类的实例,被创建的实例通常都具有共同的父类。即一个抽象产品类,可以派生出多个具体产品类。一个具体工厂类,通过往此工厂传入不同参数,产出不同的具体产品类实例。

  • 工厂方法模式:定义一个用来创建对象的接口,让子类决定实例化哪一个类,让子类决定实例化延迟到子类。即一个抽象产品类,可以派生出多个具体产品类。一个抽象工厂类,可以派生出多个具体工厂类。每个具体工厂类只能创建一个具体产品类的实例。

  • 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。即多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。一个抽象工厂类,可以派生出多个具体工厂类。 每个具体工厂类可以创建多个具体产品类的实例。

三种模式的对比:

  • 抽象工厂模式和工厂方法模式

工厂方法模式针对单独(一种)产品的创建,而抽象工厂模式注重一个产品系列的创建。如果产品系列只有一个产品的话,那么抽象工厂模式就是工厂方法模式了。在抽象工厂中使用工厂方法来提供具体实现,这个时候他们联合使用。

  • 工厂模式和简单工厂

两者非常类似,都是用来做选择实现的。不同的地方在于简单工厂在自身就做了选择实现。而工厂方法模式则是把实现延迟到子类执行。如果把工厂方法的选择实现直接在父类实现,那么此时就退化为简单工厂方法模式了。

  • 简单工厂和抽象工厂

简单工厂用于做选择实现,每个产品的实现之间没有依赖关系。而抽象工厂实现的一个产品系列,相互之间有关联。这是他们的区别。

无论是简单工厂模式,工厂方法模式还是抽象工厂模式,它们都具有类似的特性,适用场景也十分类似,都是封装对象的创建,最终目的都是为了【解耦】

综合上述三种工厂模式,我们可以发现,使用工厂模式时,只需要关心降低耦合度的目的是否达到了。使用工厂方法后,调用端的耦合度大大降低了。并且对于工厂来说,是可以扩展的,以后如果想组装其他的产品,只需要再增加一个工厂类的实现就可以。无论是灵活性还是稳定性都得到了极大的提高。

6.参考

https://zhuanlan.zhihu.com/p/24650116

https://www.jianshu.com/p/ea18c082b5dd

https://www.jianshu.com/p/81ceb0275c22

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容