不是学习工厂模式最简单的指南

1,137 阅读4分钟
原文链接: yuanjunli.github.io

引言

工厂模式,简单的理解,就是封装通过new方式创建对象的代码。工厂模式可分为三类:

简单工厂(Simple Factory)
工厂方法(Factory Method)
抽象工厂(Abstract Factory)

本文的目的,就是通过举例理解区分三种工厂模式。

没有工厂模式

场景

如果用户要购买Iphone手机,在没有工厂模式的情况下,用户只能自己根据手机型号来创建手机,客户代码如下:

public class Customer {

    public Iphone getIphone(String type) {
         switch (type) {
            case "iphone5":
                return new Iphone5();
            case "iphone6":
                return new Iphone6();
        }
        return null;
    }
}

问题

当现在需要把ipone5下架,推出iphone6时,Customer代码如下:

public class Customer {

    public Iphone getIphone(String type) {
         switch (type) {
            case "iphone6":
                return new Iphone6();
             case "iphone7":
                 return new Iphone7();
        }
        return null;
    }
}

简单的修改Customer端的代码,就能满足新的需求,但是,这违背了一个原则:

设计应该”对扩展开发,对修改关闭”

每次有新的型号,都需要改变Customer的代码,这明显是不合理,于是该普通工厂模式出现了。

普通工厂

把创建手机变化的部分封装到一个新的类SimpleIphoneFactory,Customer代码如下:

public class Customer {

    public Iphone getIphone(String type) {
        SimpleIphoneFactory simpleIphoneFactory = new SimpleIphoneFactory();
        return  simpleIphoneFactory.creatIphone(type);
    }
}

SimpleIphoneFactory的代码如下:

public class SimpleIphoneFactory {

    public Iphone creatIphone(String type) {
        switch (type){
            case "iphone6":
                return new Iphone6();
            case "iphone7":
                return new Iphone7();
        }
        return null;
    }
}

改变后,感觉代码并没有太大的变化。当iphone6下架,iphone8上架,还是得改变SimpleIphoneFactory的代码。
但是,此时Customer的代码无须改动,简单工厂方法的目的,就是把具体实例化的代码,从客户端删除

问题

当Iphone的型号越来越多时,SimpleIphoneFactory的代码依然需要改变,Customer类符合开闭原则,SimpleIphoneFactory不符合开闭原则。下面,我们采用工厂方法,把获取手机的方法getIphone()移回Customer,解决SimpleIponeFactory依赖过多Iphone实体对象的问题。

工厂方法

把用户变成抽象类,他的子类决定实例化什么类型的手机:

public abstract class Customer {
    public abstract Iphone getIphone();
}

Iphone5消费者:

public class Iphone5Customer extends Customer{

    @Override
    public Iphone getIphone() {
        return new Iphone5();
    }
}

Iphone6消费者:

public class Iphone6Customer extends Customer{

    @Override
    public Iphone getIphone() {
        return new Iphone6();
    }e
}

下面来看不同用户获取手机的代码:

//购买5的用户
Customer iphone5Customer = new Iphone5Customer();
iphone5Customer.getIphone();
//购买6的用户
Customer iphone6Customer = new Iphone6Customer();
iphone5Customer.getIphone();

问题

用户获取手机,是为了使用,我们给手机添加一个startUp()方法启动手机:

public abstract class Iphone {
    /**
     * 电池毫安数
     */
    protected int power;
    /**
     * 电池
     */
    protected Battery battery;

    /**
     * 设置电池
     * @param battery
     */
    public void setBattery(Battery battery){
        this.battery = battery;
    };

    /**
     * 开机
     */
    public abstract void startUp();
}

Iphone抽象类提供一个开机的抽象方法,由子类实现。我们开看Iphone5的实体类:

public class Iphone5 extends Iphone{
    private static final String TAG = "Iphone5";

    @Override
    public void startUp() {
        if(battery.power() == 5000){
            Log.d(TAG,"startUp success");
        }else{
            Log.d(TAG,"Boom!!!!");
        }
    }
}

可以看到Iphone5实体类,当调用startUp方法时,需要判断电池的毫安数,当等于5000时,成功启动;否则会爆炸。Iphone依赖Battery,下面来看Battery抽象类:

public abstract class Battery {
    public abstract int power();
}

抽象类定义了一个抽象power()方法,调用此方法返回电池的毫安数。来看Iphone5Battery和Iphone6Battery的类:

public  class Iphone5Battery extends Battery{
    @Override
    public int power() {
        return 5000;
    }
}

public  class Iphone6Battery extends Battery{
    @Override
    public int power() {
        return 10000;
    }
}

假设,用户把iphone5的手机配上iphone6的电池(假设电池外形一样,只是毫安数不一样),代码如下:

Customer iphone5Customer = new Iphone5Customer();
Iphone iphone5 = iphone5Customer.getIphone();
iphone5.setBattery(new Iphone6Battery());
iphone5.startUp();

毫无疑问,这会发生爆炸。Log.d(TAG,"Boom!!!!")。为了防止爆炸,生产手机时,必须要配套生产同类型的电池。当需要约束产品类之间的关系时,抽象工厂出场了。

抽象工厂

Iphone稳定的产能,需要各个代工厂的生产,苹果公司制定了一套生产手机的框架来保证手机的质量,例如Iphone6的手机只能使用Iphone6的电池。苹果公司可不想像三星手机那样因电池原因发生爆炸事件。
我们修改Customer类如下:

public  class Customer {
public void startUp(IphoneFactory iphoneFactory){
        iphoneFactory.startUp();
    }
}

Customer类提供了一个启动手机的方法,传入一个IphoneFactory对象,由IphoneFactory创建手机和对应的电池,防止因电池型号不对导致的爆炸意外。
IphoneFactory类如下:

public abstract class IphoneFactory {
    public abstract Iphone creatIphone();
    public abstract Battery creatBattery();
    public void startUp(){
         Iphone iphone = createIphone();
         Battery battery = createBattery();
         iphone.setBattery(battery);
         iphone.startUp();
    }
}

IphoneFactory是一个抽象类,startUp方法确定了Iphone和Battery的关系,子类实现创建Iphone和Battery的方法。看Iphone5Factory的类:

public class Iphone5Factory extends IphoneFactory {

    @Override
    public Iphone creatIphone() {
        return new Iphone5();
    }
    @Override
    public Battery creatBattery() {
        return new Iphone4Battery();
    }
}

最后我们来看启动Iphone5手机的代码:

IphoneFactory iphone5Factory = new Iphone5Factory();
Customer customer = new Customer(); 
customer.startUp(iphone5Factory);

可以看到,iphone5手机成功启动的Log。因为IphoneFactory封装了startUp的方法,明确了Iphone和Battery的关系,用户不能自主组装Iphone和Battery,防止了因装错电池导致事故的发生。

类图

简单工厂

简单工厂

工厂方法

工厂方法

抽象工厂

抽象工厂

一句话小结

简单工厂:将创建代码从客户端移至工厂类。
工厂方法:用继承的方式实现,一种产品对应一个工厂类。
抽象工厂:系统存在产品族,且产品之间存在关系。

引用

设计模式(一)工厂模式Factory(创建型)
抽象工厂模式和工厂模式的区别?