每天一个设计模式之工厂方法模式

什么是“工厂方法模式”

工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

我们来看看工厂方法模式的UML图:

工厂方法类图

从类图中我们可以看到,工厂方法有两个平行的接口(抽象类),一个是Product,一个是Factory

他们具体什么意思呢, 我们可以先来看看例子,然后再理解这个类图。

为什么要使用“工厂方法模式”

背景:假如我们需要使用 Java 生产很多款手机, 那么我们最传统最简单的做法似乎是这样子:

1
2
3
4
5
6
7
public Phone createPhone(String name){
if (name.equals("小米")){
phone = new XiaoMiPhone();
}else if (name.equals("华为")){
phone = new HuaWeiPhone();
}else if (...)
}

这样做貌似没什么不对,但是我们可以仔细想想,假如我们需要生产更多品牌的手机呢?再加入我们需要不同国家生产适合该国家的手机呢(比如港版、美版等等)? 出现这类需求的时候, 我们这么做就显得有些力不从心了。

这个时候, 工厂方法模式就该登场了。

如何使用“工厂方法模式”

  1. 根据类图,我们首先需要两个顶层的接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /**
    * 手机的抽象类
    * */
    public abstract class Phone {

    public String name;
    public double price;

    public abstract void showPhone();
    }

    /**

    • 工厂的抽象类
    • */
      public abstract class PhoneFactory {

      public abstract Phone createPhone(String name);

    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48



    2. 有了顶层接口,我们需要**具体的手机类**

    ```java
    //华为
    public class HuaWeiPhone extends Phone {
    public HuaWeiPhone(String name, double price) {
    super.name = name;
    super.price = price;
    }

    @Override
    public void showPhone() {
    System.out.println("华为被生产出来啦=============");
    System.out.println(name + ": " + price);
    }
    }


    //小米
    public class XiaoMiPhone extends Phone {
    public XiaoMiPhone(String name, double price) {
    super.name = name;
    super.price = price;
    }

    @Override
    public void showPhone() {
    System.out.println("小米被生产出来啦=============");
    System.out.println(name + ": " + price);
    }
    }

    //Iphone
    public class IPhone extends Phone {
    public IPhone(String name, double price) {
    super.name = name;
    super.price = price;
    }

    @Override
    public void showPhone() {
    System.out.println("苹果被生产出来啦=============");
    System.out.println(name + ": " + price);
    }
    }
  2. 有了具体的手机类之后,我们就可以把具体的工厂创建出来了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /**
    * 某个国家的工厂
    */
    public class PhoneFactoryImpl extends PhoneFactory {
    Phone phone;

    @Override
    public Phone createPhone(String name) {
    if (name.equals("华为")){
    phone = new HuaWeiPhone(name,3999.99);
    }else if (name.equals("小米")){
    phone = new XiaoMiPhone(name,2999.99);
    }else if (name.equals("苹果")){
    phone = new IPhone(name,6999.99);
    }
    return phone;
    }
    }

    在这个方法中,其实可以使用反射技术实现在运行时动态地创建相应的对象,这样就可以在新增某款手机的时候避免修改代码。但是为了简单起见,我们在这里不使用反射技术,直接使用原始的方法。

  3. 所有东西都准备齐全了, 我们可以生产手机了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class CreatePhoneTest {

    public static void main(String[] args){
    Phone phone;

    PhoneFactory factory = new PhoneFactoryImpl();
    phone = factory.createPhone("小米");
    phone.showPhone();

    phone = factory.createPhone("苹果");
    phone.showPhone();

    phone = factory.createPhone("华为 ");
    phone.showPhone();
    }
    }
  4. 运行结果如下:

    1
    2
    3
    4
    5
    6
    小米被生产出来啦=============
    小米: 2999.99
    苹果被生产出来啦=============
    苹果: 6999.99
    华为被生产出来啦=============
    华为: 3999.99

    在这个例子中, 我们只实现了一种具体的工厂, 假如需要不同的工厂生产不同类型的手机, 工厂方法模式无疑提供了很大的便利和可拓展性。

那么, 总结来了

在上面的例子中, 我想各位同学也都理解了UML类图的具体含义。

工厂方法模式是一种很实用、 拓展性强的模式。

优点:

  1. 封装性强: 我们只需要知道某个手机类或者手机名字的约束字符串,就可以进行生产。(本例中还需要去工厂实现类中添加一个判断分支,在具体的应用场景中我想没人会使用判断分支在工厂类中,一般都是使用反射)

  2. 拓展性好:比如我们还需要增加一款魅族手机,那么我们只需要实现 MeiZuPhone 类就可以了。

  3. 屏蔽了产品类:这一点非常有用,屏蔽了手机内部的具体实现, 只需要知道他们实现了 Phone 接口就行了。

使用场景:

工厂方法模式是 new 一个对象的替代品, 所以在需要新建一个对象的时候都可以使用, 但是要考虑使用后代码的复杂度。

与工厂方法模式类似的抽象工厂模式将在下一篇文章发表。欢迎关注


感谢阅读本博客。

欢迎关注我的博客:https://li-weijian.github.io/

欢迎关注我的CSDN:https://blog.csdn.net/qq352642663

需要联系请加QQ:352642663

欢迎联系我共同交流