Android知识首页投稿(暂停使用,暂停投稿)程序员

设计模式(四)工厂模式

2017-12-12  本文已影响0人  Arnold_J

我们在第一次学习对象这个概念的时候,新建对象用的是这样的代码:

Product product = new Product();

但是真正到了工程上这样的代码是“生硬”的,当遇到 Product 改变,或者 Product 初始化流程更改这样的新需求时,要改变的代码量相当的大。我们需要在找出所有调用 Product 构造器的地方,并为之添加上新的代码,这样的代码设计显然是不符合设计原则的。

为了改善上面的情况,工厂模式应运而生。

工厂模式是一种创建模式。创建模式对类的实例化过程进行了抽象,能将类的创建和使用分离。工厂类将产品类的初始化封装在方法之中,对于外界而言,只要知道产品类的公共接口,就可以正常实现功能,这使得各个模块开发时只专注于自己的业务逻辑,忽略对象的创建细节。

由于场景不同,我们可以看到不同形态的工厂模式,一般可以分为以下几种:

三种模式间并没有绝对的优劣,因为各个模式各有优缺,需要根据特定的场景来选择。但是大多数开发场景中,我们并没有这样庞大的系统,因此,通常我们使用的还是前两种方法。

一、简单工厂模式

简单工厂模式是一种类创建模式,通常情况下,简单工厂的对象会有一个静态方法用于创建产品,并且这个方法通常根据传入的参数来生产指定的产品。这种工厂模式即使你不知道有设计模式这一说法,也会自行使用,因为它构造简单,实现方便,也确实能在项目中实现立竿见影的效果。

1)简单工厂模型

SimpleFactory.png

先来看看最简单的实现代码:

public class SimpleFactory {
    
    public static Product createProduct(String arg){
        Product product = null;
        switch (arg) {
            case "A":
                product = new ProductA();
                
                // init something
                
                break;
            case "B":
                product = new ProductB();

                // init something
                
                break;
            default:
                throw new RuntimeException("product not exist for arg: "+arg);
        }
        return product;
    }
    
}

2)使用场景
对于产品种类少,且产品相对固定,出现新种类产品的可能性小的时候,可以使用简单工厂模式

我们假定一个简单的场景,依旧用上一篇建造者模式中提到的电脑。

现在电脑都可以办公和学习,因此,我们定义一个公共接口:

public interface IComputer {

    void work();

    void study();
    
}

然后,现在一个人有一台电脑用来工作或者学习,但是这台电脑是有配置高低分别的,这时候我们需要两个类实现上面的接口,并且在构造器里,分别配置它的硬件信息:

public class ComputerA implements IComputer {
    private static final String TAG = "ComputerA";

    public ComputerA(){
        //初始化高端硬件配置
        Log.i(TAG, "ComputerA: 初始化高端硬件配置");
    }

    @Override
    public void work() {
        //高端机工作
        Log.i(TAG, "ComputerA: 高端机工作");
    }

    @Override
    public void study() {
        //高端机玩学习
        Log.i(TAG, "ComputerA: 高端机玩学习");
    }
}

public class ComputerB implements IComputer {
    private static final String TAG = "ComputerB";
    
    public ComputerB(){
        //初始化普通硬件配置
        Log.i(TAG, "ComputerA: 初始化普通硬件配置");
    }

    @Override
    public void work() {
        //普通机工作
        Log.i(TAG, "ComputerA: 普通机工作");
    }

    @Override
    public void study() {
        //普通机玩学习
        Log.i(TAG, "ComputerA: 普通机玩学习");
    }
}

通过构造器,我们已经可以获取到不同硬件配置的电脑,然后我们需要对不同电脑安装适合的工作学习软件:

public class SimpleFactory {

    public static IComputer createProduct(String arg){
        IComputer product = null;
        switch (arg) {
            case "A":
                product = new ComputerA();
                // 下载学习软件 M
                // 下载工作软件 N
                // init something

                break;
            case "B":
                product = new ComputerB();
                // 下载学习软件 X
                // 下载工作软件 Y
                // init something

                break;
            default:
                throw new RuntimeException("product not exist for arg: "+arg);
        }
        return product;
    }

}

下面在想要工作学习的地方,我们只需要传入所需电脑对应的名称,就可以获取我们需要的电脑并且实现我们工作学习的目的:

IComputer iComputerA = SimpleFactory.createProduct("A");
iComputerA.study();
iComputerA.work();

IComputer iComputerB = SimpleFactory.createProduct("B");
iComputerB.study();
iComputerB.work();

下面是这个过程的日志图:


效果图-g.png

对比代码和日志,我们可以看到,由于简单工厂的封装,在需要使用电脑工作学习的时候,我们只要知道当前需要的电脑名称:“A” 或 “B”,就可以获取我们的电脑,并且通过统一的接口方法实现工作学习的目的。

这样做,显然使得代码变得更加简洁,对于业务而言逻辑也变得清楚,因为无关的东西都被隐藏了起来。

3)优缺点
**优点:
构造容易、逻辑简单。只要通过一个工厂方法就可以得到需要的对象,不用关注这个对象是如何构造出来。工厂模式能略过了产品的初始化细节,并且规范统一了所有产品功能的调用方法。

**缺点:
由于简单工厂所有产品的初始化方法都在一个方法中,当产品数量过多且初始化过程复杂的时候,代码会显得过长且难以理解。同时,通过标志位分辨生产产品的类型,使得代码内部耦合严重,如果需要新添加一个产品 C ,就必须要修改这个工厂类对象,这显然不符合前面提到的 开放封闭原则(ASD) ,即对拓展开放,对修改封闭。优秀的设计应该能够在有新产品的时候,尽量保持原代码不变,只添加新代码就能实现功能。这一点,简单工厂模式无法达到。

那么这一点,我们需要怎样达到呢?于是,工厂方法模式出现了。

二、工厂方法模式

工厂方法模式也是一种类创建模式,与简单工厂不同的是,它不再通过参数来区分当前生产的产品对象。工厂方法模式将工厂抽象化,定义了一个公共父类用于规定创建产品对象的公共接口,并把所有产品实力化的时间延迟到工厂的子类。文字说明有些不好理解,先来看一下图解。

MethodFactory.png

由图可以看到,对应产品 A 有指定的 FactoryA 用于生产,对应产品 B 有指定的 FactoryB 来生产。FactoryA 和 FactoryB 都是 IFactory 的子类,如果想要生产新的产品 C ,只需要多加一个 IFactory 子类用于生产 C ,而不需要改动原有代码。因此这种模式,也叫做多态工厂模式。

还是用上面的电脑例子,现在我们要多加一个生产工厂,生产低端电脑:

MethodFactory2.png

工厂方法模式遵循[开放封闭原则(ASD)],但是它也有缺点,即完成相同的功能,对于工厂方法而言要新增一个工厂类一个产品类,文件数量多,使得系统变得复杂。

而现在问题又来了,高端、中端、低端电脑都有了,但是实际上,对于电脑我们不只有配置高低,还有品牌。现在高端、中端、低端电脑提供商有两家,一家是联想,一家是宏基。在这样的情况下, 工厂方法模式的设计并没有办法让我们获取我们需要的品牌的电脑。于是,我们用到了抽象工厂模式。

三、抽象工厂模式

抽象工厂模式是对工厂模式的抽象。事实上,抽象工厂模式是最具有一般性质的工厂模式。为了了解这个最为抽象的模式,首先,我们需要了解几个概念,我们还是会以熟悉的电脑为例子进行说明:

在这里,例子中的高端、中端和低端更换为商务本、游戏本和二合一的平板笔记本可能更合适些。不过,管他呢。

一个抽象工厂模式一般包含如下角色:
AbstractFactory:抽象工厂
ConcreteFactory:具体工厂
AbstractProduct:抽象产品
Product:具体产品

先看下图解:


AbstractFactory.png

可以看到,不同品牌的生产工厂分别实现了抽象工厂的接口,将自己的生产过程封装起来,在我们需要电脑工作学习的时候,只需要根据需要的品牌选取合适的工厂类,然后利用工厂类选择合适的机型,就可以获取我们需要的电脑对象:

public interface IComputerFactory {
    IComputer createComputerA();
    IComputer createComputerB();
}

public class LenovoComputerFactory implements IComputerFactory {
    private static final String TAG = "LenovoComputerFactory";

    public LenovoComputerFactory(){
        Log.i(TAG, "LenovoComputerFactory: 联想电脑工厂初始化");
    }
    @Override
    public IComputer createComputerA() {
        Log.i(TAG, "createComputerA: 联想电脑工厂制造高级配置电脑");
        return new ComputerA();
    }

    @Override
    public IComputer createComputerB() {
        Log.i(TAG, "createComputerB: 联想电脑工厂制造普通配置电脑");
        return new ComputerB();
    }
}

调用日志打印:


效果图-g.png

抽象工厂模式的优点在于,新添加产品族是符合[开放封闭原则(ASD)],但是新增产品的继承结构却是不符合的,在具体项目中需要权衡倾向,再作应用。

感谢:

  1. android_interview - builder_pattern
  2. UML类图与类的详解

以上。

上一篇下一篇

猜你喜欢

热点阅读