面向对象编程内功心法系列八(聊一聊工厂模式)

72 阅读8分钟

1.引子

工厂我们都很熟悉,哪怕对于不会写程序的同学来说,因为我们现实生活中就有很多工厂。你比如说玩具工厂、服饰工厂、五金工厂等。因此对于工厂,我们不需要解释,你都知道它是专门从事生产的,对吧。

同样的,对于写程序的同学来说,工厂模式的含义不言而喻了,它就是用于搞生产的,只不过在程序中我们叫做创建对象即工厂模式,是一种创建型,用于创建对象的设计模式,其中它又细分为简单工厂、工厂方法、抽象工厂

另外工厂模式的代码实现比较简单,我通过一个水果商的案例,分别用简单工厂、工厂方法、抽象工厂的方式,实现三个版本案例。期望看完今天的文章后,你能彻底理解工厂设计模式,并在实际项目中去应用它。

2.案例

在具体看代码前,我们先铺垫一下案例背景。生活中有这样的场景,假设我们都喜欢吃水果(其实不用假设,都喜欢吃对吧)。既然有人喜欢吃,就得有人去生产,有需求自然就会有供给,这是生意的本质。

那么我们假设,现在有一家水果商(水果工厂),专门从事生产并向市场供给各种水果,与果汁类产品。我们来看一下这家水果商的发展轨迹,如何一步一步从简单工厂、到工厂方法,最后到抽象工厂,成为一家水果界的巨无霸的。

2.1.简单工厂

2.1.1.背景描述

刚开始创业,小本买卖,品类单一,只卖苹果和梨。不过老板比较有远见,一开始就做好了长远准备,所以我们也配备了类图,更加方便你理解整个设计实现。

2.1.2.类图

image.png

2.1.3.代码实现

2.1.3.1.水果接口
/**
 * 水果接口
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 9:22
 */
public interface Fruit {
    /**
     * 吃水果
     */
    void eat();
}
2.1.3.2.苹果
/**
 * 苹果
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 9:37
 */
public class Apple implements Fruit {
​
    /**
     * 吃水果
     */
    @Override
    public void eat() {
        System.out.println("新鲜的苹果,好吃......");
    }
}
2.1.3.3.梨
/**
 * 梨
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 9:38
 */
public class Pear implements Fruit {
​
    /**
     * 吃水果
     */
    @Override
    public void eat() {
        System.out.println("新鲜的梨,好吃......");
    }
}
2.1.3.4.水果工厂(简单工厂)
/**
 * 工厂设计模式之:简单工厂
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 9:40
 */
public class FruitFactory {
​
    /**
     * 简单工厂,生产水果
     * 0:生产苹果
     * 1:生产梨
     * @param fruitType
     * @return
     */
    public Fruit produce(int fruitType){
        if(fruitType == 0){
            return new Apple();
        }else if(fruitType == 1){
            return new Pear();
        }else{
            // 不支持的水果类型
            System.out.println("本店暂时没有对应的水果品类......");
            return null;
        }
    }
}
2.1.3.5.上帝类
/**
 * 上帝类,测试执行简单工厂
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 9:43
 */
public class God {
​
    public static void main(String[] args) {
        FruitFactory factory = new FruitFactory();
        // 吃苹果
        Fruit apple = factory.produce(0);
        apple.eat();
​
        System.out.println("----------------------------------");
​
        // 吃梨
        Fruit pear = factory.produce(1);
        pear.eat();
    }
}
2.1.3.6.总结分析

通过工厂类FruitFactory中的produce方法,我们可以完成苹果、梨的创建。你看这就是简单工厂设计模式,真的很简单!相信你很容易就能理解了。

那么你能想一想,简单工厂设计模式存在什么问题吗

我们看到,最主要的是produce方法中的实现逻辑,通过if...else进行条件判断,进而选择生产创建对应的水果,比如0是苹果,1是梨。非常不优雅!主要表现在:

  • 如果要创建的水果种类多,会有大量的if...else。代码可读性非常差
  • 如果要引进新的水果品类,需要修改produce方法,违反了我们前面分享的设计原则中的开闭原则(对新增开放,对修改关闭)。代码扩展性非常差

基于简单工厂存在可读性、可扩展性的不友好,我们有什么好的解决方法吗?这个时候,工厂方法模式默默的站了起来,并说:我可以

2.2.工厂方法

2.2.1.背景描述

生意越来越红火,老板喜上眉梢!想要扩充新的品类,比如说卖点桃子什么的。可是突然发现内部管理挺混乱的,引进新品类后,关于水果的摆放啊,账目啊之类的都需要调整。

老板心想,这不行,我们是时候建立一套好的管理体系了,言外之意:引入新的品类,尽量不要影响到原有的品类(开闭原则)

2.2.2.类图

image.png

2.2.3.代码实现

2.2.3.1.水果工厂(接口或抽象类)
/**
 * 工厂方法(水果工厂接口)
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 10:14
 */
public interface FruitFactory {
​
    /**
     * 生产水果
     * @return
     */
    Fruit produce();
​
}
2.2.3.2.苹果工厂(工厂方法)
/**
 * 工厂方法(苹果工厂实现)
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 10:16
 */
public class AppleFactory implements FruitFactory{
​
    /**
     * 生产水果(苹果)
     *
     * @return
     */
    @Override
    public Fruit produce() {
        return new Apple();
    }
​
}
2.2.3.3.梨工厂(工厂方法)
/**
 * 工厂方法(梨工厂实现)
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 10:15
 */
public class PearFactory implements FruitFactory {
​
    /**
     * 生产水果(梨)
     *
     * @return
     */
    @Override
    public Fruit produce() {
        return new Pear();
    }
}
2.2.3.4.上帝类
/**
 * 上帝类,测试执行工厂方法
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 10:17
 */
public class God {
​
    public static void main(String[] args) {
        // 吃苹果
        FruitFactory appleFactory = new AppleFactory();
        Fruit apple = appleFactory.produce();
        apple.eat();
​
        System.out.println("-----------------------------------");
​
        // 吃梨
        FruitFactory pearFactory = new PearFactory();
        Fruit pear = pearFactory.produce();
        pear.eat();
    }
}
2.2.3.5.总结分析

通过定义水果工厂FruitFactory接口,以及具体的苹果工厂实现类AppleFactory,梨工厂实现类PearFactory。

  • 我们发现在工厂生产方法produce中,不再有if...else条件判断代码,提升了代码的可读性。
  • 另外如果需要增加新的品类,比如桃子。我们只需要增加一个桃子工厂实现类PeachFactory即可,原有的苹果工厂、梨工厂都不需要改动。满足开闭原则,提升了代码的可扩展性。

你看这就是工厂方法设计模式,实现也很简单对吧。

那么到这里,你还能再想一想,工厂方法设计模式还有什么问题不能解决吗

这个时候抽象工厂设计模式,早已安耐不住,大声说:我要登场了!

2.3.抽象工厂

2.3.1.背景描述

水果生意一直都还不错,红红火火。某天老板发现,人们日常生活中不单喜欢吃水果,还喜欢喝果汁。这真是一门好生意!心想我们能不能除了卖水果以外,把卖果汁的生意也做起来呢。

当有了卖果汁的想法以后,老板再次梳理了内部管理体系,发现准备不足。因为我们一直都只准备了卖水果,单从卖水果来说,整套体系运转顺畅,没有任何问题。但是扩充了果汁产品线后,原有的体系又都需要调整了,赚钱真是个头疼的问题!

于是老板找到了咨询公司,咨询公司顾问团队给出建议:上抽象工厂吧!

2.3.2.类图

image.png

2.3.3.代码实现

2.3.3.1.水果汁接口
/**
 * 水果汁接口
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 10:47
 */
public interface Juice {
​
    /**
     * 喝果汁
     */
    void drink();
}
2.3.3.2.苹果汁
/**
 * 苹果汁
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 10:47
 */
public class AppleJuice implements Juice{
​
    /**
     * 喝果汁
     */
    @Override
    public void drink() {
        System.out.println("鲜榨的苹果汁,好喝......");
    }
}
2.3.3.3.梨汁
/**
 * 梨汁
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 10:48
 */
public class PearJuice implements Juice{
​
    /**
     * 喝果汁
     */
    @Override
    public void drink() {
        System.out.println("鲜榨的梨汁,好喝......");
    }
}
2.3.3.4.水果工厂(接口或抽象类)
/**
 * 抽象工厂(水果,果汁接口)
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 10:50
 */
public interface FruitFactory {
​
    /**
     * 生产水果
     * @return
     */
    Fruit produceFruit();
​
    /**
     * 生产果汁
     * @return
     */
    Juice produceJuice();
}
2.3.3.5.苹果工厂(抽象工厂)
/**
 * 抽象工厂(苹果,苹果汁工厂接口实现类)
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 10:52
 */
public class AppleFactory implements FruitFactory {
​
    /**
     * 生产苹果
     *
     * @return
     */
    @Override
    public Fruit produceFruit() {
        return new Apple();
    }
​
    /**
     * 生产苹果汁
     *
     * @return
     */
    @Override
    public Juice produceJuice() {
        return new AppleJuice();
    }
}
2.3.3.6.梨工厂(抽象工厂)
/**
 * 抽象工厂(梨,梨汁工厂接口实现类)
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 10:53
 */
public class PearFactory implements FruitFactory {
​
    /**
     * 生产梨
     *
     * @return
     */
    @Override
    public Fruit produceFruit() {
        return new Pear();
    }
​
    /**
     * 生产梨汁
     *
     * @return
     */
    @Override
    public Juice produceJuice() {
        return new PearJuice();
    }
}
2.3.3.7.上帝类
/**
 * 上帝类,测试执行抽象工厂
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2021/2/27 10:54
 */
public class God {
​
    public static void main(String[] args) {
        // 吃苹果,和苹果汁
        FruitFactory appleFactory = new AppleFactory();
        Fruit apple = appleFactory.produceFruit();
        apple.eat();
​
        Juice appleJuice = appleFactory.produceJuice();
        appleJuice.drink();
    }
}
2.3.3.8.总结分析

增加果汁产品线后,我们通过定义FruitFactory接口,在接口中分别有两个方法

  • 生产水果:Fruit produceFruit()
  • 生产果汁:Juice produceJuice()

实现了多产品线的运作,从代码实现上来说,可以用简单工厂,也可以使用工厂方法,我们的案例使用了工厂方法。

这里你需要注意的是关于抽象工厂设计模式,它是从更大的一个角度看待问题,即一个工厂不单生产单一品类的商品,而是生产多品类商品。简单理解为一个工厂类中,通过多个方法分别生产不同的产品。针对的还是从更大的一个角度,解决满足开闭原则,扩展性的问题。

最后,我们整个工厂设计模式的内容就到这里了。最后一句话简单总结回顾一下。工厂设计模式,是一种创建型的设计模式,它细分类为简单工厂、工厂方法、抽象工厂。你都get到了吗?