生活中的设计模式之抽象工厂模式

定义

define an interface or abstract class for creating an object but let the subclasses decide which class to instantiate.

定义一个创建对象的接口或抽象类,但让子类决定实例化那个具体的类。

实列

生活中,有很多的企业、店铺、公司会为我们生产各种各样的商品,作为消费者的我们只关心如何使用这些产品,而不需要知道它们的制作过程。
比如说,包子店它会生产肉包、菜包、豆腐包等各式各样的包子,如果我们决定早上吃豆腐包中午吃豆沙包,都只需要告知店铺便可以获得生产好的包子,而不需要自己生产。
工厂模式中的工厂就好比上面的包子店,但在工厂模式中我们把这类相似的商品称为产品,它们不仅在形态上相似,而且创建过程也非常相似。

故事

前不久,我入职了一家开发射击类游戏的公司,并负责公共组件枪支库的开发。
枪支库中有手枪、冲锋枪、步枪等不同类型的枪支产品,它们都继承同一个抽象类Gun。
这个抽象类声明了三个操作,一个是shoot用于射击目标对象,一个是setBullet用于装子弹,还有一个是load用于上膛。
公司其它部门的同事,如果要将这些产品应用到不同的射击游戏中,那么他们需要先使用new实例化对象,然后给对象装上与之匹配的子弹并上膛,便可以调用shoot操作了。
伪代码如下:


public class Client {
    public static void main(String[] args) {
        String gunName = args[0];
        String target = args[1];
        if("HANDGUN".equals(gunName)){
            Handgun handgun = new Handgun();
            handgun.setBullet(new HandgunBullet());
            handgun.load();
            //射击目标
            handgun.shoot(target);
        }else if("SMG".equals(gunName)){
            SMG smg = new SMG();
            smg.setBullet(new SMGBullet());
            handgun.load();
            //射击目标
            smg.shoot(target);
        }else if("RIFLE".equals(gunName)){
            Rifle smg = new Rifle();
            smg.setBullet(new RifleBullet());
            handgun.load();
            //射击目标
            smg.shoot(target);
        }
    }
}

问题

从上面的代码中我们可以看出,这款游戏会根据用户选择的枪支来射击目标对象。
但是,在客户端使用new关键字创建产品会存在几个问题。首先是扩展问题,如果后续射击游戏需要增加新的枪支,那么就要修改客户端代码。
其次是耦合问题,因为枪支是游戏的基础类,一旦它发生变化,那么所有的客户端都很可能都要跟着变化,比如说重命名某个枪支类的名称。
最后是重复问题,装弹、上膛是射击前的初始化操作,如果枪支库被用于多款射击游戏,那么创建对象的过程就会重复。
所以,有没有一种方式可以让客户端不操心产品是如何创建的?这便是工厂模式。

方案

工厂模式是一种创建型设计模式,它会将同类产品的创建以及初始化操作封装到独立的类即工厂类(Facotry)中。
工厂会向外暴露一个创建产品的操作,客户端只需调用该操作就可以获得指定的对象,而不需要知道产品是如何创建以及如何初始化的。
这样,工厂便解藕了客户端和产品之间的直接依赖关系,以及复用了初始化过程。

实现

工厂模式分为简单工厂模式和工厂方法模式以及抽象工厂模式,下面我们来看看前两种模式。

简单工厂模式

首先,我们创建一个名为简单工厂的类,将对象的创建以及初始化操作封装起来。



public class SimpleFactory {

    public static Gun create(String gunName){
        Gun gun;
        if("HANDGUN".equals(gunName)){
            gun = new Handgun();
            gun.setBullet(new HandgunBullet());
        }else if("SMG".equals(gunName)){
            gun = new SMG();
            gun.setBullet(new SMGBullet());
        }else if("RIFLE".equals(gunName)){
            gun = new Rifle();
            gun.setBullet(new RifleBullet());
        }
        gun.load();
        return gun;
    }
}

然后,修改客户端代码,将创建对象的任务委托给简单工厂来创建,这样客户端就能多态地使用对象了。


public class Client1 {
    public static void main(String[] args) {
        String gunName = args[0];
        String target = args[1];
        Gun gun = SimpleFactory.create(gunName);
        gun.shoot(target);
    }
}

可以看出,简单工厂模式的实现非常的简单,但是它没有解决扩展的问题,当新增产品时还是需要修改简单工厂类。

工厂方法模式

在工厂方法模式中,不同的产品由不同的工厂类创建,而不是像简单工厂一样由一个工厂创建,工厂和产品之间通常是一对一的关系,这些工厂都继承自一个共同的抽象类。
下面我们看看工厂模式又是如何实现的:

首先,我们创建一个抽象工厂的类,定义产品的创建流程以及声明子类要实现的差异化操作。


/**抽象工厂类*/
public abstract class AbstractFactory {
    /**定义创建对象的标准流程*/
    public Gun create(){
        Gun gun= doCreate();
        //上膛
        gun.load();
        return gun;
    }
    /**子类差异化实现*/
    protected abstract Gun doCreate();


}

然后,创建具体的工厂类实现差异的部分,比如,我们为手枪单独创建一个工厂。

/**手枪工厂*/
public class HandgunFactory extends AbstractFactory{
    @Override
    protected Gun doCreate() {
        Handgun handgun = new Handgun();
        //装子弹
        handgun.setBullet(new HandgunBullet());
        return handgun;
    }
}

最后,我们在来看看客户端如何使用工厂方法模式创建产品。


public class Client {
    public static void main(String[] args) {
        String gunName = args[0];
        String target = args[1];

        //具体工厂的创建可以使用Java的反射技术
        AbstractFactory factory ;
        if("HANDGUN".equals(gunName)){
            factory = new HandgunFactory();
        }else if("SMG".equals(gunName)){
            factory = new HandgunFactory();
        }else if("RIFLE".equals(gunName)){
            factory = new HandgunFactory();
        }

        Gun handgun = factory.create();
        //射击目标
        handgun.shoot(target);
    }
}

很明显,工厂方法模式没有简单工厂模式那样简单,甚至看上去比不使用工厂还复杂。
但是,用一个工厂创建一种产品,能解决扩展性方面的问题。前提是具体工厂的创建得使用反射,这里我们就不再深入,读者可以自行了解一下。

结构

avatar

产品(Product):这里的产品指的是继承或实现同一接口或父类的对象,它们同属一个家族。

抽象工厂(AbstractFactory):它是对创建对象行为的抽象,声明了可相互替代产品的创建接口。如果创建涉及对象初始化,那么可以在抽象类中定义共同的创建流程。

具体工厂(ConcreteFactory):它实现自抽象工厂,负责具体产品的创建。

总结

当程序需要根据不同的条件创建不同的产品,而且产品的数量会持续增加时,为了避免客户端和具体产品的耦合,那么我们应该考虑使用工厂方法模式。
工厂方法模式可以解决简单工厂解决不了的问题:扩展性问题,它可以在增加新的产品时不修改客户端代码,做到插件式地动态扩展产品。

扩展阅读

架构设计思维篇之结构

架构设计思维篇之概念

架构设计容错篇之重试

架构设计容错篇之熔断

架构设计容错篇之限流

架构设计事务篇之Mysql事务原理

架构设计事务篇之CAP定理

架构设计事务篇之分布式事务

架构设计消息篇之消息丢失

架构设计消息篇之保证消息顺序性

你可能感兴趣的:(生活中的设计模式之抽象工厂模式)