• 硬核!史上最全的工厂模式文章,从零到一全面讲解!


    文章首发于「陈树义」公众号及个人博客 shuyi.tech,欢迎访问更多有趣有价值的文章。

    夏日沙滩

    文章首发于「陈树义」公众号及个人博客 shuyi.tech,欢迎访问更多有趣有价值的文章。

    工厂模式是编程中用得最多的设计模式。本文由一个简单的生活例子触发,从工厂方法模式到简单工厂模式,再到多工厂模式,最后到抽象工厂模式。环环相扣,深入浅出,让读者看着大呼过瘾!

    小黑眼瞅着年近35岁,于是想着搞搞副业预防中年危机。有一天走在路上,看到路边一家炸鸡店在卖薯条。善于思考的小黑想着:我是否能将这个过程用面向对象的语言表达出来?于是小黑在脑海中开始构思。

    // 薯条接口
    public interface Chips {
        void peel();
        void cut();
        void fried();
    }
    // 普通薯条
    public class CommonChips implements Chips {
        @Override
        public void peel() {
            System.out.println("土豆去皮");
        }
    
        @Override
        public void cut() {
            System.out.println("土豆切条");
        }
    
        @Override
        public void fried() {
            System.out.println("炸薯条");
        }
    }
    

    那当客人点了薯条之后,炸鸡店应该怎么做一份薯条呢?小黑很快地在脑海中写下了下面的代码。

    public class Client {
        public static void main(String[] args) {
            // 店员开始炸薯条啦~
            Chips chips = new CommonChips();
            chips.peel();
            chips.cut();
            chips.fried();
        }
    }
    

    不出意外,最终结果应该是下面这样。小黑说道。

    土豆去皮
    土豆切条
    炸薯条
    

    过了几天,小黑看到肯德基推出了波纹薯条,于是炸鸡店也跟进推出新品。于是炸鸡店的店员也不得不跟着改进下切薯条的方法。小黑想:这种情况,我们的类结构应该怎么调整呢?想了一会之后,小黑说:我们只需要在 Chips 薯条类增加一个新的切波纹薯条方法,之后让店员用新的方法去切薯条即可,其它的步骤都不用变。

    // 薯条
    public class CommonChips implements Chips {
        @Override
        public void peel() {
            System.out.println("土豆去皮");
        }
    
        // 增加一个切波纹薯条的方法
        @Override
        public void cutBoWen() {
            System.out.println("土豆切成波纹");
        }
    
        @Override
        public void fried() {
            System.out.println("炸薯条");
        }
    }
    

    店员制作薯条的时候的步骤需要变换一下:

    public class Client {
        public static void main(String[] args) {
            // 店员开始炸波纹薯条啦~
            Chips chips = new CommonChips();
            chips.peel();
            chips.cutBoWen();
            chips.fried();
        }
    }
    

    如无意外,结果应该是:

    土豆去皮
    土豆切成波纹
    炸薯条
    

    看起来这样的操作完全没问题,问题可以完美解决。不过小黑总觉得哪里不对劲,但又一时没想到原因。直到他听到店员吐苦水说:我就卖薯条的,你还要让我学怎么做薯条,多麻烦啊。还不如直接把薯条做好,我直接炸薯条就行。这样我就不用关心薯条怎么做的了。 下次做旋风薯条、麻辣薯条等等的时候,我也不用关心薯条怎么做,直接炸薯条就可以了。

    工厂方法模式

    听到这里小黑焕然大悟!作为售卖的店员来说,他不需要关注原材料怎么生产的,只需要知道怎么做好卖给顾客就可以了。这不就和我们编程中的工厂方法模式类似么?

    工厂方法模式指的是使用者与创建者分离,使用者不需要知道对象是怎么创建出来的,而创建的过程封装在工厂里。 这就像这家炸鸡店一样,店员(使用者)不需要关心薯条怎么做出来的,薯条怎么做出来交给中央厨房(工厂)去做就可以了。

    于是小黑调整了一下炸薯条的实现,使用工厂方法来实现。具体实现上,增加了一个工厂类来制作薯条。

    文章首发于「陈树义」公众号及个人博客 shuyi.tech,欢迎访问更多有趣有价值的文章。

    // 工厂抽象类
    public abstract class AbstractFoodFactory {
        public abstract Chips make(String type);
    }
    // 具体工厂类
    public class ChipFactory extends AbstractFoodFactory{
        @Override
        public Chips make(String type) {
            if (type.equals("common")) {
                Chips chips = new CommonChips();
                chips.peel();
                chips.cut();
                return chips;
            } else if (type.equals("bowen")) {
                Chips chips = new CommonChips();
                chips.peel();
                chips.cutBoWen();
                return chips;
            }
            return null;
        }
    }
    

    此时店员怎么卖薯条呢?直接去工厂拿到薯条,之后炸薯条就可以了!

    public class Client {
        public static void main(String[] args) {
            // 直接告诉工厂要什么薯条
            FoodFactory foodFactory = new FoodFactory();
            Chips chips = foodFactory.make("bowen");
            // 拿到薯条后直接炸薯条
            chips.fried();
        }
    }
    

    想到这里,小黑不由得感叹:编程其实就是现实世界的投射。工厂方法模式,本质上就是将对象的实例化与使用分离开来,这使得使用方不需要去关心对象的实例化。要解决的问题是:希望能够创建一个对象,但创建过程比较复杂,希望对外隐藏这些细节。 在这个例子中,就是店员不需要去关心薯条怎么做出来的,只需要直接炸薯条就可以了。这样就可以炸更多薯条,挣更多钱了!

    简单工厂模式

    但小黑还是觉得工厂方法模式太复杂了。你看 AbstractFoodFactory 类其实只有一个实现类,那这种情况下没必要还弄一个抽象类,还弄个实现类,这样多累啊。直接弄一个提供静态方法的工厂类不就好了。于是小黑调整了一下代码。

    // 创建简单工厂类
    public class SimpleFoodFactory {
        // 提供静态方法
        public static Chips make(String type) {
            if (type.equals("common")) {
                Chips chips = new CommonChips();
                chips.peel();
                chips.cut();
                return chips;
            } else if (type.equals("bowen")) {
                Chips chips = new CommonChips();
                chips.peel();
                chips.cutBoWen();
                return chips;
            }
            return null;
        }
    }
    // 店员炸鸡更快了!
    public class Client {
        public static void main(String[] args) {
            // 不用创建食物工厂了,直接拿薯条!
            Chips chips = SimpleFoodFactory.make("bowen");
            chips.fried();
        }
    }
    

    可以看到整个类结构精简了,不需要抽象工厂类。而在使用的时候,店员也可以直接拿到薯条,不需要去创建食物工厂了!

    其实这就是我们常说的简单工厂模式!在只有一个工厂实现的时候,我们可以简化成提供静态方法的简单工厂类,从而简化使用。

    多工厂模式

    除了简单工厂类,我们还有多工厂模式,小黑说道。

    多工厂模式就是每种类型的产品单独作为一个工厂,例如:普通薯条单独作为一个工厂,波纹薯条单独作为一个工厂。为什么要这么做呢?这是因为在单种对象初始化比较复杂的时候,所有产品类的初始化都放到一个类中,会使得代码结构不清晰,这时候就用多工厂模式。 例如我们的波纹薯条非常复杂,可能需要 100 道工序,那和普通薯条放在同一个工厂制作就不太合适,于是我们单独建了一个制作波纹薯条的工厂。

    于是小黑继续对之前的代码做改造。Chips 类和 Food 接口还是没有变动,有变化的仅仅是工厂类。

    // 新的抽象工厂类
    public abstract class AbstractFoodFactory {
        // 不需要告诉我要什么类型的薯条了,一个工厂只做一种薯条
        public abstract Chips make();
    }
    // 普通薯条工厂
    public class CommonChipFactory extends AbstractFoodFactory{
        @Override
        public Chips make() {
            Chips chips = new CommonChips();
            chips.peel();
            chips.cut();
            return chips;
        }
    }
    // 波纹薯条工厂
    public class BowenChipFactory extends AbstractFoodFactory{
        @Override
        public Chips make() {
            Chips chips = new CommonChips();
            chips.peel();
            chips.cutBoWen();
            return chips;
        }
    }
    

    现在店员炸薯条变成了这样:

    // 店员炸鸡更快了!
    public class Client {
        public static void main(String[] args) {
            // 去普通薯条工厂直接拿薯条
            CommonChipFactory commonChipFactory = new CommonChipFactory();
            Chips chips = commonChipFactory.make();
            chips.fried();
            // 去波纹薯条工厂拿波纹薯条
            BowenChipFactory bowenChipFactory = new BowenChipFactory();
            chips = bowenChipFactory.make();
            chips.fried();
        }
    }
    

    抽象工厂模式

    看到多工厂模式,大家是不是已经累趴了,但其实还有抽象工厂模式!

    抽象工厂模式,其实就是工厂模式更高级的抽象。从名字可以知道,抽象二字是用来形容工厂的,那说明在抽象工厂模式中,工厂也被抽象出来了。 例如对于肯德基和麦当劳来说,他们的薯条都是由供应商提供的,那么对于供应商来说,他们如何去表示这个过程呢?

    首先,我们先创建一个工厂类,可以做普通薯条和波纹薯条。

    public interface ChipFactory {
        // 薯条族,即普通薯条,还是波纹薯条
        void makeChip();
        void makeBowenChip();
    }
    

    文章首发于「陈树义」公众号及个人博客 shuyi.tech,欢迎访问更多有趣有价值的文章。

    那么肯德基肯定有其对应的薯条工厂,麦当劳也有其薯条工厂。

    // 肯德基薯条工厂
    public class KfcChipFactory implements ChipFactory{
        @Override
        public void makeChip() {
            System.out.println("生产肯德基普通薯条");
        }
    
        @Override
        public void makeBowenChip() {
            System.out.println("生产肯德基波纹薯条");
        }
    }
    // 麦当劳薯条工厂
    public class MacDonaldChipFactory implements ChipFactory{
        @Override
        public void makeChip() {
            System.out.println("生产麦当劳普通薯条");
        }
    
        @Override
        public void makeBowenChip() {
            System.out.println("生产麦当劳波纹薯条");
        }
    }
    

    最后我们用一个场景类来表示生产过程。

    public class Client {
        public static void main(String[] args) {
            ChipFactory kfcChipFactory = new KfcChipFactory();
            kfcChipFactory.makeChip();
            kfcChipFactory.makeBowenChip();
            ChipFactory macChipFactory = new MacDonaldChipFactory();
            macChipFactory.makeChip();
            macChipFactory.makeBowenChip();
        }
    }
    

    可以看到,抽象工厂比起工厂方法,最大的区别在于:抽象工厂是两层的抽象结构,而工厂方法则只有一层抽象。这就使得抽象工厂能够表示更多的内容,而工厂方法表达的内容更少。 在这个例子中,工厂方法模式表示的是薯条类型,而表示不了肯德基、麦当劳的品牌类型。而抽象工厂不仅可以表示薯条类型,也可以表示品牌类型。

    但其实抽象工厂也有一些坏处,例如当我们要增加一种新的薯条类型时,我们需要修改 ChipFactory 工厂类,又要修改每个实现类。这就违背了我们的开闭原则,使得代码非常不稳定。但抽象工厂也有好处,即当我们有一个新的品牌时,扩展非常方便。例如当我们有德克士这个品牌时,我们可以直接增加一个 DicosChipFactory 类,实现 ChipFactory 接口就可以了。

    这要求我们要用变化的角度去分析需求,看看哪些是变化更大的,将变化的东西使用类结构来承载。例如在生产薯条的例子中,生产新薯条的场景相对较少、新品牌可能变动,那么我们就应该将薯条类型作为产品族,这样变化就不大。

    总的来说,抽象工厂一般用在多个维度,即有产品族的情况下。产品族作为用第一层的抽象类来承载,但如果产品族变化很大则不适合使用抽象工厂。

    总结

    想到这里,小黑感觉知识间好像都关联起来了。

    • 工厂方法是用来分类使用与创建的,创建对象使用工厂方法实现,创建的过程封装在工厂类的方法中,我们不需要关心对象是怎么生产的。
    • 如果工厂方法只创建一种类型的对象,那么可以将工厂类简化成带静态方法的工厂类,去掉工厂抽象类,减少类结构冗余。
    • 如果工厂方法要创建很多种类型的对象,而每种对象的创建过程都很复杂,那么就用多工种模式,即每种产品都对应一个工厂类,这就形成了多工厂模式。
    • 如果产品有多个产品族(两个维度的变量),那么可以进一步抽象成抽象工厂模式。

    总的来说,就是以工厂方法为基点,往前缩变成了简单工厂,往后扩展变成了多工厂,往上一层就变成了抽象工厂。

    注:在设计模式中,其实只有工厂方法模式和抽象工厂模式两种。简单工厂模式、多工厂模式、普通工厂方法,都属于工厂方法。

    文章首发于「陈树义」公众号及个人博客 shuyi.tech,欢迎访问更多有趣有价值的文章。

    扫描关注微信公众号
  • 相关阅读:
    11.01T3 实数二分
    11.01T2 树状数组维护动态LIS
    11.1T1打表
    10.31T4 HAOI2010最长公共子序列 计数+容斥原理
    10.31T3 其他算法思想
    10.31T2 点双联通分量+预处理前缀+二分答案
    10.31T1 二分图
    10.30T3 换根
    10.30T2 二分+前缀和(后缀和)
    10.30T1 期望DP
  • 原文地址:https://www.cnblogs.com/chanshuyi/p/head-first-of-factory-pattern.html
Copyright © 2020-2023  润新知