Liuqichun's Blog

设计模式|工厂模式

工厂模式属于创建型模式,它很好地解耦了对象的创建依赖。什么是对象的依赖呢?在 Java 中,如果在一个类里 new 创建了一个对象,就说明这个类依赖了被创建对象的类。

工厂模式主要关注如何创建对象。在简单工厂模式下,我们传入所需要创建类的关键字就可以得到我们需要的对象;在工厂方法中,我们可以通过一个个单独的工厂创建不同的对象(一个面包工厂可以生产面包对象,一个披萨工厂可以生产披萨对象);而在抽象工厂中,一个工厂具有多个功能,相比于工厂方法模式,它的工厂既可以生产面包对象又可以生产披萨对象。

在自己动手实践后,我们可以清晰地发现:简单工厂就像一个杂货铺,你只要给“物品”贴一个标签(key),就可以在这个“杂货铺”售卖它了。这个杂货铺有什么东西,我们需要自己看有哪些标签(不像工厂方法和抽象工厂,对象的创建有具体的方法和接口),这也就说明通过简单工厂创建对象时,它有可能不存在(传入 key,对象可能为 null)。对于工厂方法,它是比较专一的工厂——它只生产与它相关的对象(面包工厂只专注生产面包,披萨工厂只专注生产披萨)。抽象工厂可以认为是一个超级工厂,我既可以生产这个也可以生产那个;抽象工厂的抽象体现在工厂继承抽象工厂类或实现抽象工厂接口(这个顶层的抽象定义了工厂可以生产的产品)。这个“超级工厂”对比简单工厂的“杂货铺”并不乱,这方面要归功于接口。

下面让我们结合代码和类的结构来看一下。

简单工厂

类图 在这里插入图片描述 在这个类关系图中,BlackBread 和 HoneyBread 继承了 BreadMaker,在 BreadFactory 中创建 BlackBread、HoneyBread 对象。这里的 BreadMaker 可以使用接口代替,因为 BreadMaker 只是实现了制作面包的功能;在面向对象中,功能最好使用接口来定义,可以更好地解耦依赖。

BreadMaker

1public abstract class BreadMaker {
2    public abstract void getBread();
3}

BlackBread

1public class BlackBread extends BreadMaker {
2    // 重写
3    @Override
4    public void getBread() {
5        System.out.println("考出黑面包");
6    }
7}

HoneyBread

1public class HoneyBread extends BreadMaker {
2    @Override
3    public void getBread() {
4        System.out.println("烤出蜂蜜面包");
5    }
6}

BreadFactory

 1public class BreadFactory {
 2    public static BreadMaker makeBread(int breadType) {
 3        switch (breadType) {
 4            case 0:
 5                return new BlackBread();
 6            case 1:
 7                return new HoneyBread();
 8            default: {
 9                // 当用户输入一个不存在的breadType, 工厂不能创建对象, 将返回null对象
10                System.out.println("工厂没有这种类型的面包");
11                return null;
12            }
13        }
14    }
15}

Main 客户端

 1public class Main {
 2    public static void main(String[] args) {
 3        BreadMaker breadMaker;
 4
 5        breadMaker = BreadFactory.makeBread(0);
 6        breadMaker.getBread();
 7
 8        breadMaker = BreadFactory.makeBread(1);
 9        breadMaker.getBread();
10    }
11}
12
13output:
14考出黑面包
15烤出蜂蜜面包

有的人说简单工厂模式并不算一种工厂方法,这里不反驳也不赞同,使用一种设计模式最重要的是适合自己的业务,适合是最重要的! 回想一下 Spring 中的 IoC,通过 key 来创建一个对象或者拿到对象的引用,也是很好的设计。

工厂方法

工厂方法——我只生产我专注的产品!这是工厂方法的优点,但是有时候确实没有必要再为一个简单对象写一个工厂类,这是在生产的“产品”比较简单的时候。

在这里插入图片描述 HoneyBreadFactory 和 BlackBreadFactory 都实现了 IFactory 接口,二者生产的目标不一样:HoneyBreadFactory 专注于生产蜂蜜面包,而 BlackBreadFactory 专注于生产黑面包。图中中间部分,BlackBread 和 HoneyBread 继承 BreadMaker 是为了统一接收对象(工厂方法返回的对象统一使用 BreadMaker 接收)。

IFactory

1public interface IFactory {
2    BreadMaker creatMaker();
3}

HoneyBreadFactory

1public class HoneyBreadFactory implements IFactory {
2    @Override
3    public BreadMaker creatMaker() {
4        return new HoneyBread();
5    }
6}

BlackBreadFactory

1public class BlackBreadFactory implements IFactory{
2    @Override
3    public BreadMaker creatMaker() {
4        return new BlackBread();
5    }
6}

BreadMaker

1public abstract class BreadMaker {
2    public abstract void getBread();
3}

BlackBread

1public class BlackBread extends BreadMaker {
2    // 重写 这样并不好
3    @Override
4    public void getBread() {
5        System.out.println("考出黑面包");
6    }
7}

HoneyBread

1public class HoneyBread extends BreadMaker {
2    @Override
3    public void getBread() {
4        System.out.println("烤出蜂蜜面包");
5    }
6}

Main 客户端

 1public class Main {
 2    public static void main(String[] args) {
 3        BreadMaker breadMaker;
 4
 5        // 黑面包工厂
 6        BlackBreadFactory blackBreadFactory = new BlackBreadFactory();
 7        breadMaker = blackBreadFactory.creatMaker();
 8        breadMaker.getBread();
 9
10        // 蜂蜜面包工厂
11        HoneyBreadFactory honeyBreadFactory = new HoneyBreadFactory();
12        breadMaker = honeyBreadFactory.creatMaker();
13        breadMaker.getBread();
14    }
15}
16output:
17考出黑面包
18烤出蜂蜜面包

抽象工厂一

抽象工厂——升级版“超级工厂”,一个工厂可以生产多种商品。

在这里插入图片描述 IFactory

1public interface IFactory {
2    BreadMaker creatBreadMaker();
3    PizzaMaker creatPizzaMaker();
4}

HoneyFactory

 1public class HoneyFactory implements IFactory {
 2    @Override
 3    public BreadMaker creatBreadMaker() {
 4        return new HoneyBread();
 5    }
 6
 7    @Override
 8    public PizzaMaker creatPizzaMaker() {
 9        return new HoneyPizza();
10    }
11}

PizzaMaker

1public abstract class PizzaMaker {
2    public abstract void getPizza();
3}

BlackPizza

1public class BlackPizza extends PizzaMaker {
2    @Override
3    public void getPizza() {
4        System.out.println("黑烤披萨做好了");
5    }
6}

HoneyPizza

1public class HoneyPizza extends PizzaMaker {
2
3    @Override
4    public void getPizza() {
5        System.out.println("蜂蜜披萨做好了");
6    }
7}

BlackBread、HoneyBread 同 BlackPizza、HoneyPizza 类

Main 客户端

 1public class Main {
 2    public static void main(String[] args) {
 3        HoneyFactory honeyFactory = new HoneyFactory();
 4
 5        BreadMaker breadMaker;
 6        breadMaker = honeyFactory.creatBreadMaker();
 7
 8        PizzaMaker pizzaMaker;
 9        pizzaMaker = honeyFactory.creatPizzaMaker();
10
11        breadMaker.getBread();
12        pizzaMaker.getPizza();
13    }
14}

抽象工厂二

这里对抽象工厂进一步模拟。你可以看到抽象工厂如何实现产品的多样化,以及使用聚合来设计对象的优点。

工厂设定。有两个饮料工厂(河南工厂、北京工厂),它们生产的饮料主要有红茶(BlackTea)、百事可乐(PosiCoke)、可口可乐(CocaCoke)。虽然它们都生产这三种饮料,但是生产的饮料产品配料不同(包装/水/瓶子…)。下面看一下它们是如何组织配合的。

在这里插入图片描述

产品 ↓ 在这里插入图片描述

组成饮料的材料 ↓ 在这里插入图片描述

为了方便看,这里贴张包结构图。 在这里插入图片描述

这里仅列出主要类的代码

饮料代表类 BlackTea

 1public class BlackTea implements IDrink {
 2    private IBottle bottom;
 3    private ILiquid liquid;
 4    private IPackaging packaging;
 5
 6    public IBottle getBottom() {
 7        return bottom;
 8    }
 9
10    public void setBottom(IBottle bottom) {
11        this.bottom = bottom;
12    }
13
14    public ILiquid getLiquid() {
15        return liquid;
16    }
17
18    public void setLiquid(ILiquid liquid) {
19        this.liquid = liquid;
20    }
21
22    public IPackaging getPackaging() {
23        return packaging;
24    }
25
26    public void setPackaging(IPackaging packaging) {
27        this.packaging = packaging;
28    }
29
30    @Override
31    public void getDrinkInfo() {
32        System.out.println("------Black Tea 红茶------");
33        bottom.getBottleInfo();
34        liquid.getLiquidInfo();
35        packaging.getPackagingInfo();
36        System.out.println("---------------------------\n");
37    }
38}

IDrinkFactory

1public interface IDrinkFactory {
2    BlackTea createBlackTea();
3    CocaCoke createCocaCoke();
4    PosiCoke createPosiCoke();
5}

BeijingDrinkFactory

 1import com.elltor.designpattern.factory.test.prod.BlackTea;
 2import com.elltor.designpattern.factory.test.prod.CocaCoke;
 3import com.elltor.designpattern.factory.test.prod.PosiCoke;
 4import com.elltor.designpattern.factory.test.prod.material.*;
 5
 6public class BeiJingDrinkFactory implements IDrinkFactory {
 7
 8    @Override
 9    public BlackTea createBlackTea() {
10        BlackTea blackTea = new BlackTea();
11        blackTea.setBottom(new PlasticBottle());
12        blackTea.setLiquid(new NormalLiquid());
13        blackTea.setPackaging(new TFBoysPackaging());
14        return blackTea;
15    }
16
17    @Override
18    public CocaCoke createCocaCoke() {
19        CocaCoke cocaCoke = new CocaCoke();
20        cocaCoke.setBottom(new PlasticBottle());
21        cocaCoke.setLiquid(new NormalLiquid());
22        cocaCoke.setPackaging(new TFBoysPackaging());
23        return cocaCoke;
24    }
25
26    @Override
27    public PosiCoke createPosiCoke() {
28        PosiCoke posiCoke = new PosiCoke();
29        posiCoke.setBottom(new PlasticBottle());
30        posiCoke.setLiquid(new NormalLiquid());
31        posiCoke.setPackaging(new TFBoysPackaging());
32        return posiCoke;
33    }
34}

HeNanDrinkFactory

 1import com.elltor.designpattern.factory.test.prod.BlackTea;
 2import com.elltor.designpattern.factory.test.prod.CocaCoke;
 3import com.elltor.designpattern.factory.test.prod.PosiCoke;
 4import com.elltor.designpattern.factory.test.prod.material.*;
 5
 6public class HeNanDrinkFactory implements IDrinkFactory {
 7    @Override
 8    public BlackTea createBlackTea() {
 9        BlackTea blackTea = new BlackTea();
10        blackTea.setBottom(new AluminumBottom());
11        blackTea.setLiquid(new NongFuSpringLiquid());
12        blackTea.setPackaging(new AngelababyPackaging());
13        return blackTea;
14    }
15
16    @Override
17    public CocaCoke createCocaCoke() {
18        CocaCoke cocaCoke = new CocaCoke();
19        cocaCoke.setBottom(new AluminumBottom());
20        cocaCoke.setLiquid(new NongFuSpringLiquid());
21        cocaCoke.setPackaging(new AngelababyPackaging());
22        return cocaCoke;
23    }
24
25    @Override
26    public PosiCoke createPosiCoke() {
27        PosiCoke posiCoke = new PosiCoke();
28        posiCoke.setBottom(new AluminumBottom());
29        posiCoke.setLiquid(new NongFuSpringLiquid());
30        posiCoke.setPackaging(new AngelababyPackaging());
31        return posiCoke;
32    }
33}

Main 及打印

 1public class Main {
 2    public static void main(String[] args) {
 3        IDrinkFactory beiJingFactory = new BeiJingDrinkFactory();
 4        IDrinkFactory heNanFactory = new HeNanDrinkFactory();
 5
 6        System.out.println("顾客购买河南工厂生产的饮料:");
 7        heNanFactory.createCocaCoke().getDrinkInfo();
 8        heNanFactory.createPosiCoke().getDrinkInfo();
 9        heNanFactory.createBlackTea().getDrinkInfo();
10
11        System.out.println("顾客购买北京生产的饮料:");
12        beiJingFactory.createCocaCoke().getDrinkInfo();
13        beiJingFactory.createPosiCoke().getDrinkInfo();
14        beiJingFactory.createBlackTea().getDrinkInfo();
15
16    }
17}
18
19output:
20顾客购买河南工厂生产的饮料:
21--------Coca Coke 可口可乐--------
22this is a aluminum bottom.
23the water is nong fu sping.
24this is Angelababy advocacy packaging
25--------------------------------
26------Posi Coke 百事可乐------
27this is a aluminum bottom.
28the water is nong fu sping.
29this is Angelababy advocacy packaging
30---------------------------
31
32------Black Tea 红茶------
33this is a aluminum bottom.
34the water is nong fu sping.
35this is Angelababy advocacy packaging
36---------------------------
37
38顾客购买北京生产的饮料:
39--------Coca Coke 可口可乐--------
40this is a Plastic bottle
41the water is normal water.
42this is TFBoys advocacy packaging
43--------------------------------
44------Posi Coke 百事可乐------
45this is a Plastic bottle
46the water is normal water.
47this is TFBoys advocacy packaging
48---------------------------
49
50------Black Tea 红茶------
51this is a Plastic bottle
52the water is normal water.
53this is TFBoys advocacy packaging
54---------------------------

到这里,通过主要的类和打印结果我们可以看到:每一种饮料通过接口聚合了各种材料。
在这里插入图片描述 然后在工厂创建过程中根据需要放入相应材料,虽然是一种名称的产品,但 传入材料的不同也就构成了不同的产品。 在这里插入图片描述 在这里插入图片描述

在客户端进行相同的调用,也就打印出了不同的结果。 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

总结

工厂方法模式的适用性

使用 对于工厂方法的使用要取决于具体的应用。对比简单工厂和工厂方法,可以看出:对于一些简单的应用,“产品”较少时,可以使用简单工厂模式;对于产品比较丰富的应用,过多的分支则不利于维护,这时应当选择工厂方法模式以降低维护量。在有些场景,类的初始化并不希望使用者知道,这时使用工厂方法就是一种很好的选择。

<< Previous Post

|

Next Post >>

#设计模式