设计模式初探-创建型模式之工厂模式

上一篇文章设计模式初探-创建型模式之单例模式
中,我们引入了设计模式相关话题,开启了设计模式的新专题。篇幅所限,上一篇文章仅仅介绍了创建型模式中的单例模式。这篇文章,我们将继续介绍创建型模式之工厂模式。本文分为如下几个部分:

  1. 前言
  2. 工厂模式
    2.1 为什么要有工厂模式
    2.2 工厂模式分类
  3. 工厂模式的实现
    3.1 简单工厂
    3.1.1 简单工厂的定义
    3.1.2 新增产品
    3.2 工厂方法
    3.2.1 工厂方法的定义
    3.2.2 新增产品
    3.3 抽象工厂
    3.3.1 抽象工厂定义
    3.3.2 新增产品
  4. 总结

1. 前言

再说工厂模式先,先思考两个问题:

  1. 日常coding中使用过哪些设计模式?
  2. 除了工厂和单例还使用过哪些设计模式?
  3. 工厂模式的分类及区别?

下面让我们带着问题开始工厂模式的探讨

2. 工厂模式

2.1 为什么要有工厂模式

任何事物的出现必然是因为有相应的需求。那为什么要有工厂模式呢?
先看下这样的代码

    public void test(String type){
        Mouse mouse=null;
        if ("DELL".equalsIgnoreCase(type)){
            mouse=new DellMouse();
        }else if ("MICROSOFT".equalsIgnoreCase(type)){
            mouse=new MicrosoftMouse();
        }else if ("RAZER".equalsIgnoreCase(type)){
            mouse=new RazerMouse();
        }else {
            throw new IllegalArgumentException("no such type !");
        }
        mouse.click();
    }
public interface Mouse {
    void click();
}

public class DellMouse implements Mouse {
    @Override
    public void click() {
        System.out.println("DellMouse click");
    }
}

public class MicrosoftMouse implements Mouse {
    @Override
    public void click() {
        System.out.println("MicrosoftMouse click");
    }
}
public class RazerMouse implements Mouse{
    @Override
    public void click() {
        System.out.println("RazerMouse click");
    }
}

当然这段代码,跑起来没有任何问题。但是这段代码有个隐藏问题,如果将来我们要新增一种鼠标类型的化,那么我们就需要修改test方法,在其中的if elseif 中加一段新的逻辑控制,而且mouse的创建和mouse的使用耦合在一起,很容易改错。那么工厂模式的作用也显而易见了。

  1. 将对象的创建单独拆分,可以避免代码的重复,便于后期维护。
  2. 将对象的创建和对象的使用拆分,使其符合开闭原则。
  3. 将对象的创建和对象的使用拆分,降低系统间的耦合度。
    值得注意的是:无论工厂模式如何设计,最终的对象创建还是通过new关键字来创建的。工厂模式只是提供了一种设计模板,用来保护对象的创建,使其便于维护和已读。

2.2 工厂模式分类

类型 定义 是否符合开闭原则 是否方便新增子产品 是否方便新增产品家族
简单工厂 专门定义一个工厂类来创建其它类的实例(只是简单的将创建对象的相关代码抽离到单独的一个类中)
工厂方法 定义了一个创建对象的接口,由子类决定实例化的是哪个类,工厂方法将子类的实例化推迟到子类中
抽象工厂 定义了一个接口,用于创建相关对象和依赖的家族,而不需要指明具体类

3. 工厂模式的实现

上面我们介绍了工厂模式的分类

  • 简单工厂
  • 工厂方法
  • 抽象工厂
    下面我们来看下具体实现

3.1 简单工厂

3.1.1 简单工厂的定义

简单工厂,即单纯的将对象的创建提取到一个类中,严格意义上来说不属于23种设计模式的一种。先上类图


设计模式初探-创建型模式之工厂模式_第1张图片
简单工厂类图.png

代码实现如下

public class SimpleMouseFactory {
    public static Mouse createMouse(String type){
        Mouse mouse=null;
        if ("DELL".equalsIgnoreCase(type)){
            mouse=new DellMouse();
        }else if ("MICROSOFT".equalsIgnoreCase(type)){
            mouse=new MicrosoftMouse();
        }else if ("RAZER".equalsIgnoreCase(type)){
            mouse=new RazerMouse();
        }else {
            throw new IllegalArgumentException("no such type !");
        }
        return mouse;
    }

}
    public void test(String type){
        Mouse mouse=SimpleMouseFactory.createMouse(type);
        mouse.click();
    }
    public static void main(String[] args) {
        Test test=new Test();
        test.test("dell");
    }

上面的例子中,我们将创建Mouse的过程单独提取到类SimpleMouseFactory中,并提供一个静态方法createMouse来创建对象。这样我们就实现了简单工厂模式,做到了对象的创建和对象的使用解耦。

3.1.2 新增产品

在上面的例子中,假设我们要新增一种华硕鼠标,那我们要怎么做呢?

  • 新增新的实现,AsusMouse
  • 修改工厂类SimpleMouseFactory.createMouse,加入创建AsusMouse的逻辑
public class SimpleMouseFactory {
    public static Mouse createMouse(String type){
        Mouse mouse=null;
        if ("DELL".equalsIgnoreCase(type)){
            mouse=new DellMouse();
        }else if ("MICROSOFT".equalsIgnoreCase(type)){
            mouse=new MicrosoftMouse();
        }else if ("RAZER".equalsIgnoreCase(type)){
            mouse=new RazerMouse();
        }else if ("ASUS".equalsIgnoreCase(type)){
            mouse=new AsusMouse();
        }else {
            throw new IllegalArgumentException("no such type !");
        }
        return mouse;
    }
}

这么做显然违反了一个重要的设计原则开闭原则:程序应该对扩展开放,对修改关闭。 怎样才能符合开闭原则呢?工厂方法和抽象工厂应运而生。

3.2 工厂方法

3.2.1 工厂方法的定义

工厂方法,定义了一个创建对象的接口,由子类决定实例化的是哪个类,工厂方法将实例化推迟到子类中。
先上类图:

设计模式初探-创建型模式之工厂模式_第2张图片
工厂方法类图.png

代码实现如下

  • 新增MouseFactory 接口
public interface MouseFactory {
    Mouse createMouse();
}
  • 新增实现
public class AsusMouseFactory implements MouseFactory {
    @Override
    public Mouse createMouse() {
        return new AsusMouse();
    }
}
public class DellMouseFactory implements MouseFactory{
    @Override
    public Mouse createMouse() {
        return new DellMouse();
    }
}
public class MicrosoftMouseFactory implements MouseFactory {
    @Override
    public Mouse createMouse() {
        return new MicrosoftMouse();
    }
}
public class RazerMouseFactory implements MouseFactory {
    @Override
    public Mouse createMouse() {
        return new RazerMouse();
    }
}
  • 调用的时候传入不同的MouseFactory
    public void test(MouseFactory mouseFactory){
        Mouse mouse=mouseFactory.createMouse();
        mouse.click();
    }
    public static void main(String[] args) {
        Test test=new Test();
        test.test(new AsusMouseFactory());
    }

我们可以看到,子类决定了实例化的是哪个产品。这里的决定,并不是由程序动态的指定,而是在编码的时候由程序员决定使用的是哪个具体工厂类,决定了具体工厂类自然决定了实例化哪个子类。

3.2.2 新增产品

  1. 新增一种新的鼠标-LenovoMouse只需要做如下步骤
  • 新增LenovoMouse
public class LenovoMouse implements Mouse {
    @Override
    public void click() {
        System.out.println("LenovoMouse click");
    }
}
  • 新增LenovoMouseFactory
public class LenovoMouseFactory implements MouseFactory {
    @Override
    public Mouse createMouse() {
        return new LenovoMouse();
    }
}
  • 调用时传入不同的MouseFactory
    public void test(MouseFactory mouseFactory){
        Mouse mouse=mouseFactory.createMouse();
        mouse.click();
    }
    public static void main(String[] args) {
        Test test=new Test();
        test.test(new LenovoMouseFactory());
    }

可以看到,新增一个新的鼠标种类时,只需新增新的工厂类和新的鼠标实现即可。无需对原有的工厂和实现做任何改动,因此符合开闭原则

  1. 新增产品大类-KeyBoard
    那么当我们要在工厂中添加一种新的产品大类键盘-keyboard时,我们应该怎么做呢?
  • 添加新的键盘抽象及实现
  • 修改原有的工厂抽象及实现
  • 修改原有调用方法
    可以看到工厂方法在新增新的产品子类时无需改动原有工厂方法,只需新增新的工厂,符合开闭原则。但是在新增新的产品大类时,需要改动原有的工厂实现,不符合开闭原则。那么当我们需要新增产品大类的时候怎么办呢?抽象工厂应运而生。

3.3 抽象工厂

3.3.1 抽象工厂定义

所谓抽象工厂即定义了一个接口,来创建相关依赖的家族,而不需要指明具体的类。抽象工厂允许客户使用抽象的接口来创建一组相关产品,而不需要知道(或关心)实际产出的具体产品是什么,将客户从具体的产品中解耦。
类图如下:

设计模式初探-创建型模式之工厂模式_第3张图片
抽象工厂类图.png

代码实现如下:

  • 新增抽象PCFactory-提供一个接口用于创建依赖家族(createMouse/createKeyboard)
public interface PCFactory {
    Mouse createMouse();
    Keyboard createKeyboard();
}
  • 新增抽象Keyboard及实现
public interface Keyboard {
    void pressEnter();
}
public class AsusKeyboard implements Keyboard {
    @Override
    public void pressEnter() {
        System.out.println("AsusKeyboard pressEnter");
    }
}
  • 新增工厂实现
public class AsusPcFactory implements PCFactory{
    @Override
    public Mouse createMouse() {
        return new AsusMouse();
    }

    @Override
    public Keyboard createKeyboard() {
        return new AsusKeyboard();
    }
}
  • 调用的时候传入抽象工厂
    public void test(PCFactory pcFactory){
        Mouse mouse=pcFactory.createMouse();
        Keyboard keyboard=pcFactory.createKeyboard();
        mouse.click();
        keyboard.pressEnter();
    }
    public static void main(String[] args) {
        Test test=new Test();
        test.test(new AsusPcFactory());
    }

可以看到,与工厂方法相比,我们做了更进一步的抽象,抽象出PCfatory,Pcfactory提供接口来创建产品依赖的家族,这里指的是既能createMouse,又能createKeyboard。这样就避免了在工厂方法模式中,新增keyborad时需要修改原有工厂的尴尬,只需要新增新的PCFactory实现即可。

3.3.2 新增产品

  1. 现在要新增RazerKeyboard和RazerMouse的产线。要怎么做呢
  • 新增RazerKeyboard的实现
public class RazerKeyboard implements Keyboard {
    @Override
    public void pressEnter() {
        System.out.println("RazerKeyboard pressEnter");
    }
}
  • 新增RazerPCFactory的实现
public class RazerPCFactory implements PCFactory {
    @Override
    public Mouse createMouse() {
        return new RazerMouse();
    }

    @Override
    public Keyboard createKeyboard() {
        return new RazerKeyboard();
    }
}
  • 调用时传入RazerPCFactory
    public void test(PCFactory pcFactory){
        Mouse mouse=pcFactory.createMouse();
        Keyboard keyboard=pcFactory.createKeyboard();
        mouse.click();
        keyboard.pressEnter();
    }
    public static void main(String[] args) {
        Test test=new Test();
        test.test(new RazerPCFactory());
    }
  1. 新增新的产品类型-显示器monitor
  • 抽象Monitor
  • 修改Pcfactory及其实现

通过上面的例子,我们可以得到如下结论

  • 抽象工厂模式,可以很方便的新增产品家族(这里只需要新增新的PCFactory实现接口,无需修改原有的工厂),新增产品家族时符合开闭原则。
  • 抽象工厂模式针对是多组产品,强调的是依赖的家族(这里的Mouse和Keybord),而工厂方法模式针对的是单组产品。
  • 抽象工厂模式,同样无法很方便的新增新的产品种类(比如这里的monitor)。
  1. 总结

这里,我们探讨了工厂模式的三种实现,并对每种模式的优缺点进行介绍。

由于技术水平所限,文章难免有不足之处,欢迎大家指出。希望每位读者都能有新的收获,我们下一篇文章-设计模式初探-创建型模式之建造者模式再见.....

参考文章
head first 设计模式

你可能感兴趣的:(设计模式初探-创建型模式之工厂模式)