2

情景描述

最常见的一个场景:某个客户,需要一部手机,如何优雅的满足他?

这对应这我们 java 编程中一个常见的情形:在一个类中获取另一个类的对象。

不使用工厂模式

此时,客户需要一部手机,最粗暴的方式:自己造一部手机。对应着 java 编程中就是直接去 new 一个对象出来。比如造一部小米手机:

MiPhone MiPhone = new MiPhone();

但是,某一天客户有了这样一个需求:周一、三、五、七要用小米手机;周二、四、六要用苹果手机。这时候该怎么办呢?

这对应这我们 java 编程中另一个常见的情形:在一个类中根据不同的情况中获取其他类的对象。

当然,这种情形客户也是可以直接再造一部苹果手机。对应 java 也就是在该类里接着 new

// 周一、三、五、七时
MiPhone MiPhone = new MiPhone();
// 周二、四、六时
IPhone IPhone = new IPhone();

但再想想,如果有一天,需求再变更,客户需要更多类型的手机了(7种、10种...),怎么想都觉得所有东西都自己造是不太现实的,也并不优雅。

所以,为了应对这种情形,工厂模式应运而生。

使用工厂模式

工厂顾名思义就是创建产品,根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式,根据工厂的抽象程度可分为工厂方法模式和抽象工厂模式。该模式用于封装和管理对象的创建,是一种创建型模式。

简单而言,就是将对产品(对象)的创建( new )进行封装。封装后的类就是所谓的工厂。

对上述情形而言,当我们有了工厂后,客户终于不用什么都自己造了。

简单工厂模式

简单工厂模式下是如何处理上述情形呢?

首先我们需要先引入一个抽象产品的概念:

比如,小米手机苹果手机都是产品,它们的抽象产品是手机小米电脑苹果电脑都是产品,它们的抽象产品是电脑;等等。

通常抽象产品在 java 中被定义为接口,而产品则是一个类,这个类要去实现抽象产品这个接口。

之后我们再简单看一下思路:
image.png

代码实现:

抽象产品接口(Phone)

package abstractProduct;

public interface Phone {
    void make();
}

小米手机产品类(MiPhone)

package product;

import abstractProduct.Phone;

public class MiPhone implements Phone {
    public MiPhone() {
        this.make();
    }
    @Override
    public void make() {
        System.out.println("make xiaomi phone!");
    }
}

苹果手机产品类(IPhone)

package product;

import abstractProduct.Phone;

public class IPhone implements Phone {
    public IPhone() {
        this.make();
    }
    @Override
    public void make() {
        // TODO Auto-generated method stub
        System.out.println("make iphone!");
    }
}

工厂类(PhoneFactory)

package factory;

import abstractProduct.Phone;
import product.IPhone;
import product.MiPhone;

public class PhoneFactory {
    public Phone makePhone(String phoneType) {
        if(phoneType.equalsIgnoreCase("MiPhone")) {
            return new MiPhone();
        }
        else if(phoneType.equalsIgnoreCase("iPhone")) {
            return new IPhone();
        }
        return null;
    }
}

主类中调用(客户)

import abstractProduct.Phone;
import factory.ApplyFactory;
import factory.MiFactory;
import factory.PhoneFactory;
import product.IPhone;
import product.MiPhone;

public class FactoryPattern {
    public static void main(String[] args) {
        PhoneFactory factory = new PhoneFactory();
        Phone miPhone = factory.makePhone("MiPhone");
        IPhone iPhone = (IPhone)factory.makePhone("iPhone");
}

运行结果
image.png

使用了简单工厂模式后我们可以看到:我们将创建产品的代码全都挪到了工厂中,而客户需要什么样的产品,再(带着参数)从工厂中获取即可,而非自己创建。

但是问题又来了,如果某一天,客户不再用苹果手机了,想用华为手机怎么办?

其实要实现这个改动并不困难。在建立了华为手机产品类以后,我们知道创建产品的代码全都在工厂中,那么就去工厂中把创建苹果手机的内容全都换成创建华为手机的内容就可以了。

但这个做法并不妥当,如果不止一个客户向当前工厂获取产品呢?比如客户A向当前工厂获取苹果手机,此时会因为我们上述的修改而报错,因为该工厂已经不会创建苹果手机了,而是创建华为手机和小米手机。

上述情形更专业的说法是,没有满足 “对修改关闭,对扩展开放” 的原则。

而工厂方法模式则很好的解决了这一问题。

工厂方法模式

我们还是先来引入一个抽象工厂的概念:

这和抽象产品同理,比如,小米工厂和苹果工厂都可以生产手机,它们的抽象工厂是能够生产手机的工厂;联想工厂和戴尔工厂都可以生产电脑,它们的抽象产品是能够生产电脑的工厂;等等。

同样的,通常抽象工厂在 java 中也被定义为接口,而工厂则是一个类,这个类要去实现抽象工厂这个接口。

思路:
image.png

代码实现:

抽象工厂接口(AbstractFactory)

package abstractFactory;

public interface AbstractFactory {
    Phone makePhone();
}

小米工厂类(MiFactory)

package factory;

import abstractFactory.AbstractFactory;
import product.MiPC;
import product.MiPhone;

public class MiFactory implements AbstractFactory {
    @Override
    public Phone makePhone() {
        return new MiPhone();
    }
}

苹果工厂类(ApplyFactory)

package factory;

import abstractFactory.AbstractFactory;
import product.IPhone;
import product.Mac;

public class ApplyFactory implements AbstractFactory {
    @Override
    public Phone makePhone() {
        return new IPhone();
    }
}

主类中调用(客户)

public class FactoryPattern {
    public static void main(String[] args) {
        MiFactory xiaomiFactory1 = new MiFactory();
        ApplyFactory applyFactory1 = new ApplyFactory();
        xiaomiFactory1.makePhone();
        applyFactory1.makePhone();
}

运行结果
image.png

虽然运行结果没有改变,但是我们看代码可以知道,此时创建各种手机的工作已经交由各自的工厂来完成,而不是都拘泥于一个工厂。客户获取时也都是从各个工厂获取产品。

此时再回头看上面的情形,当客户不再用苹果手机,转而用华为手机。

这时对于之前的产品和工厂我们无需修改。我们要做的是建立一个华为手机的产品类,建立一个华为工厂去继承我们的抽象工厂,在 makePhone() 中去创建我们的华为手机产品即可。最后在有需要的客户中调用华为工厂,就可以获得对应的华为手机产品。

想要增加其他种类的手机产品也是同理。

如此一来,就做到了 “对扩展开放” 。

至此,只是产品和工厂的对扩展开放。那么抽象产品接口是不是也对扩展开放呢?

答案是肯定的。于是我们就有了工厂模式的完全体:抽象工厂模式。

抽象工厂模式

比如说再增加一个抽象产品:电脑。

先看思路图:
image.png

代码实现:

增加抽象产品接口(PC)

package abstractProduct;

public interface PC {
    void make();
}

增加小米电脑产品类(MiPC)

package product;

import abstractProduct.PC;

public class MiPC implements PC {
    public MiPC() {
        this.make();
    }
    @Override
    public void make() {
        System.out.println("make xiaomi PC!");
    }
}

增加苹果电脑产品类(Mac)

package product;

import abstractProduct.PC;

public class Mac implements PC {
    public Mac() {
        this.make();
    }
    @Override
    public void make() {
        System.out.println("make Mac!");
    }
}

抽象工厂接口增加方法

public interface AbstractFactory {
    Phone makePhone();

    PC makePC();
}

小米工厂类增加方法

@Override
public PC makePC() {
    return new MiPC();
}

苹果工厂类增加方法

@Override
public PC makePC() {
    return new Mac();
}

主类中调用(客户)

public class FactoryPattern {
    public static void main(String[] args) {
        MiFactory xiaomiFactory = new MiFactory();
        ApplyFactory applyFactory = new ApplyFactory();
        xiaomiFactory.makePhone();
        xiaomiFactory.makePC();
        applyFactory.makePhone();
        applyFactory.makePC();
}

运行结果
image.png

同理,其他抽象产品的拓展也可以像上述这般操作即可,比如平板电脑、智能手表等等。

总结

再回到我们开头的场景:某个客户,需要一部手机,如何优雅的满足他?

使用工厂模式就是一个不错的选择。既不需要自己去创建对象,所需的对象还可以随着需求的变化而不断拓展。

从上述内容总结下来,我们不难发现:

  1. 从不使用工厂模式 -> 使用工厂模式:实现了将创建对象的工作从该类转移到了该类的子类中。
  2. 从简单工厂模式 -> 工厂方法模式:实现了对扩展开放。
  3. 从工厂方法模式 -> 抽象工厂模式:更大程度的对扩展开放。

希望这篇文章对你有帮助!

参考资料

https://www.cnblogs.com/yssjun/p/11102162.html

https://www.runoob.com/design-pattern/factory-pattern.html


HHepan
164 声望13 粉丝

河北工业大学