百度工程师教你玩转设计模式(工厂模式)

作者 | 北极星小组

想要写好代码,规划形式(Design Pattern)是必不可少的基本功,规划形式是对面向目标规划(Object Oriented Design)中重复出现的问题的处理方案,本篇介绍工厂形式(Factory Pattern)。

工厂形式归于创立型形式(Builder Pattern),供给了创立目标的最佳办法,在创立目标时,不会对客户端露出目标的创立逻辑,而是经过运用一起的接口来创立目标。工厂形式运用的典型场景是,期望能够创立一个目标,但创立进程比较杂乱,期望对外躲藏这些细节,比方:创立目标可能是一个pool里的,不是每次都随便创立一个新的;目标创立时会有许多参数来决议怎么创立出这个目标;创立一个目标有杂乱的依赖联系。

完结办法上,主要有简略工厂形式(Simple Factory Pattern)、工厂办法形式(Factory Method Pattern)、笼统工厂形式(Abstract Factory Pattern)。

一、 简略工厂形式在文档解析场景中的运用

在日常开发场景中,假如要创立的产品(被创立的目标类)不多,只需一个工厂类就能够完结,这种形式叫“简略工厂形式”。运用简略工厂形式的客户端(详细调用方)只需求传入需求创立产品类的参数,不需求关怀怎么创立目标的逻辑,能够很方便地创立所需产品。

以Word2007类型文档解析场景为例:文档主体在document.xml文件,解析时依据内容结构维度分别创立Paragraph、Table、Draw等详细解析类。在解析流程中,假如直接构造对应解析类的目标运用,则会导致两者的耦合过重,能够运用**“简略工厂形式”**将解析类的实践创立作业推迟到工厂类中。这也满意创立型形式中所要求的“创立与运用相别离”的特色。

详细完结上包括以下几部分:

  • 简略工厂(SimpleFactory):是简略工厂形式的核心,这里担任完结创立一切详细Parser实例的内部逻辑。

  • 笼统产品(Product):是简略工厂创立的一切目标的父类,这里担任描述一切Parser实例共有的公共接口。

  • 详细产品(ConcreteProduct):是简略工厂形式的创立目标,这里担任创立详细的解析类。

public class DocxPaser {
    //笼统产品:一切Parser共有的公共接口
    public interface IPaser {
        void process(string entity);
    }
    //详细产品:Paragraph Parser
    static class Paragraph implements IPaser {
        public void process(string entity) {
            System.out.println("解析 Paragraph...");
        }
    }
    //详细产品:Table Parser
    static class Table implements IPaser {
        public void process(string entity) {
            System.out.println("解析 Table...");
        }
    }
     //详细产品:Draw Parser
    static class Draw implements IPaser {
        public void process(string entity) {
            System.out.println("解析 Draw...");
        }
    }
    final class Const {
        static final int ENTITY_PARAGRAPHP = 0;
        static final int ENTITY_TABLE      = 1;
        static final int ENTITY_DRAW       = 2;
    }
    //简略工厂:担任完结创立一切详细Parser实例的内部逻辑
    static class ParserFactory {
        public static IPaser creatParser(int kind) {
            switch (kind) {
                case Const.ENTITY_PARAGRAPHP:
                    return new Paragraph();
                case Const.ENTITY_TABLE:
                    return new Table();
                case Const.ENTITY_DRAW:
                    return new Draw();
            }
            return null;
        }
    }
    // 简略运用示例
    public static void main(String[] args) {
        //entity 对应document.xml 此处省略详细获取进程
        ...
        //解析paragraph
        ParserFactory.creatParser(Const.ENTITY_PARAGRAPHP).process(entity)
        //解析table
        ParserFactory.creatParser(Const.ENTITY_TABLE).process(entity)
        //解析draw
        ParserFactory.creatParser(Const.ENTITY_DRAW).process(entity)
        ...
    }
}

二、工厂办法形式在自动化测验场景的运用

在简略工厂形式中,调用方只需求传入需求创立的目标类参数,就能够获得一个需求的目标,而不必关怀这个目标的详细创立细节。在需求创立的目标类型较少,并且后续不再容易增加的情况下简略工厂形式即可满意要求。可是针对需求创立的目标品种繁多,并且后续改变较为频频的场景下,简略工厂形式有两个明显的问题:

  • 由于后续每次新增一种目标都需求修正工厂类中的判别逻辑,违反代码规划中的开闭准则

  • 由于需求对输入的参数进行判别然后初始化合适的类,简略工厂类中 if-else 或者 switch-case分支过多,代码显得较为臃肿

为了处理以上问题,能够挑选工厂办法形式:界说一个创立目标的接口,然后完结不同目标的创立子类,目标的初始化交由各个工厂的子类去完结。他和简略工厂的区别点在于:

  • 开闭准则:新增目标的情况下,简略工厂形式下需求修正工厂类,而工厂办法形式只需求新增一个工厂的子类去完结新增目标的初始化即可。

  • 代码简洁:简略工厂经过参数挑选并初始化子类,一切作业都由工厂类完结,工厂办法只是界说了一个创立目标的接口,目标的创立放在子类去完结。

以前端浏览器自动化测验的场景为例,在不同的浏览器场景下必须经过对应的浏览器驱动才能完结测验的履行,例如常见的Chrome 、Firefox、Safari、IE浏览器。这个场景就能够经过工厂办法形式来进行完结。首先是界说场景下的驱动目标,Driver为各个详细驱动需求承继的笼统类,其他的XDriver为详细某个浏览器的驱动目标。接下来界说对应的笼统工厂并完结不同目标初始化的工厂子类,AbstractFactory为笼统工厂,界说了创立驱动目标的接口,其他的每个工厂完结了笼统工厂的接口,并供给对应驱动的初始化能力。最后实践的运用者依据需求挑选某个工厂类即可完结对应的驱动初始化作业。

public class FactoryDemo {
    //笼统目标:一切驱动目标的公共父类
    abstract static class Driver {
        abstract void process();
    }
    // 详细驱动目标:Chrome浏览器驱动目标
    static class ChromeDriver extends Driver {
        @Override
        void process() {
            System.out.println("ChromeDriver process"); // do something
        }
    }
    // 详细驱动目标:Firefox浏览器驱动目标
    static class FirefoxDriver extends Driver {
        @Override
        void process() {
            System.out.println("FirefoxDriver process"); // do something
        }
    }
    // 详细驱动目标:Safari浏览器驱动目标
    static class SafariDriver extends Driver {
        @Override
        void process() {
            System.out.println("SafariDriver process"); // do something
        }
    }
    // 笼统工厂 一切工厂的公共接口
    public interface AbstractFactory {
        Driver create();
    }
    // 详细工厂:担任Chrome浏览器驱动目标的创立
    static class ChromeDriverFactory implements AbstractFactory {
        @Override
        public Driver create() {
            return new ChromeDriver();
        }
    }
    // 详细工厂:担任Firefox浏览器驱动目标的创立
    static class FirefoxDriverFactory implements AbstractFactory {
        @Override
        public Driver create() {
            return new FirefoxDriver();
        }
    }
    // 详细工厂:担任Safari浏览器驱动目标的创立
    static class SafariDriverFactory implements AbstractFactory {
        @Override
        public Driver create() {
            return new SafariDriver();
        }
    }
    public static void main(String[] args) {
        // 创立 Chrome 驱动目标
        AbstractFactory chromeDriverFactory = new ChromeDriverFactory();
        Driver chromeDriver = chromeDriverFactory.create();
        chromeDriver.process();
        // 创立 Firefox 驱动目标
        AbstractFactory firefoxDriverFactory = new FirefoxDriverFactory();
        Driver firefoxDriver = firefoxDriverFactory.create();
        firefoxDriver.process();
        // ...
    }
}

三、笼统工厂形式在跨端场景下的运用

笼统工厂形式,是对工厂办法形式的进一步深化,工厂办法形式中只要一个笼统办法,要想完结多种不同的类目标,只能去创立不同的详细工厂办法的子类来实列化,而笼统工厂则是让一个工厂担任创立多个不同类型的目标。

以典型的跨端场景为例,用户可购买会员有多品种型,每品种型的会员在iOS、Android、PC下权益页稍有不同,并且实例化进程十分杂乱。假如不必工厂形式,就需求在每处需求实例的地方,去编写杂乱的实例化代码,当实例化进程产生修正,也需求每处修正,维护成本非常高。假如用简略工厂形式或者工厂办法形式,不必笼统工厂形式,就需求每个类型会员完结三个工厂,也不易于维护。

在这个场景下,咱们能够用笼统工厂形式处理,编写笼统工厂、会员笼统类,并完结iOS、Android、PC三个详细工厂,依据场景选取需求的详细工厂获取会员实例。

//笼统产品
public interface Vip {}
//详细产品
public class NormalVip implements Vip {} //一般会员
public class MonthlyVip implements Vip {} //包月会员
public class IOSNormalVip extends NormalVip {}
public class AndroidNormalVip extends NormalVip {}
public class PCNormalVip extends NormalVip {}
public class IOSMonthlyVip extends MonthlyVip {}
public class AndroidMonthlyVip extends MonthlyVip {}
public class PCMonthlyVip extends MonthlyVip {}
//笼统工厂
public interface AbstractVipFactory {
    Vip createNormalVip();
    Vip createMonthlyVip();
}
//详细工厂
public class IOSVipFactory implements AbstractVipFactory {
    @Override
    public Vip createNormalVip() {
        return new IOSNormalVip();
    }
    @Override
    public Vip createMonthlyVip() {
        return new IOSMonthlyVip();
    }
}
...
//调用示例
public class Client {
    public static void main(String[] args) {
        IOSVipFactory iosVipFactory = new IOSVipFactory();
        Vip iosNormalVip = iosVipFactory.createNormalVip();
    }
}

四、总结

经过对以上三个实践事例的讲解和详细的代码完结阅览,我们对工厂形式的运用场景和详细完结方案应该有了愈加深入的了解了。结合以上三个事例的的剖析,创立进程杂乱时,适合运用工厂形式,常用的工厂形式有:

  • 简略工厂形式:实例化进程杂乱时,能够选用

  • 工厂办法形式:产品结构较杂乱时,能够选用

  • 笼统工厂形式:产品品种结构多时,能够选用

———- END ———-

引荐阅览【技能加油站】系列:

揭秘百度智能测验在测验剖析范畴实践

百度用户产品流批一体的实时数仓实践

ffplay视频播放原理剖析

百度工程师眼中的云原生可观测性追踪技能

运用百度开发者东西 4.0 搭建专属的小程序 IDE

百度工程师教你玩转规划形式(观察者形式)