在面试中,候选人常常会被问到,你在项目里用到过哪些设计模式?对此,你能够按本文给出的步骤,系统地经过工厂模式展现本身在设计思想方面的能力。java
工厂模式(Factory Method)是用来向使用者屏蔽建立对象的细节。以前咱们在讲SAX解析XML文件时,已经用到过工厂模式,当时咱们是经过以下代码用SAXParserFacotry这个工厂对象来建立用于解析的parse对象,代码以下所示。面试
做为使用者,咱们只要能获得parser对象进行后继的解析动做,至于parser对象是如何建立的,咱们不须要,也不该管。若是不用工厂模式,那么咱们还得亲自关注如何建立parser对象,好比得考虑建立时传入的参数,以及是否改用“池”的方式来建立从而提高效率。数据库
这样亲力亲为的后果是,会让使用和建立parser对象的代码耦合度很高,这样一旦建立parser的方法发生改变,好比往后须要传入不一样的参数,那么使用parser的代码也须要对应修改。设计模式
你们别觉得增长修改量没什么大不了,若是咱们在某个模块里修改了代码,哪怕这个修改点再小,也得通过完整的测试才能把这段代码放入生产环境,这是须要工做量的。若是咱们把“使用”和“建立”对象放在一个模块里,那么“使用”部分的代码也得测试(虽然没改),但咱们经过了工厂模式分离了二者,那么只须要测“建立”模块,就能够减小工做量了。架构
下面咱们先来看下工厂模式的实现代码,好比咱们要编写(建立)Java和数据库方面的两本书,先在第1行构建一个Book的基类,在第4和第7行建立两个子类,并且咱们能够把一些通用性的方法(好比“查资料”)放入Book类里。 app
随后咱们经过如第10行的接口来定义建立动做,根据需求,咱们能够在第11和17行实现这个接口,在其中分别实现“编写Java书”和“编写数据库书”的代码。 ide
在上述代码里,咱们提供了“建立”的方法,下面咱们给出了“调用”的代码,从第2和第4行的代码中咱们能看到,这里外部对象能够经过两种不一样的createBook方法分别获得Java和数据库书。 测试
你们在经过上文,举例讲清楚工厂模式后,能够当即说出这个结论。具体举例以下。ui
在上述的案例中,若是遇到新需求,须要再建立C语言的书,首先能够在Book父类下再建立一个CBook子类,随后能够在BookFactory接口下再建立一个新的工厂来建立,代码以下。 spa
对于这个修改需求,咱们并无修改原有的建立Java和数据库书籍相关的代码,而是经过添加新的模块来实现,这种作法很好地符合了“开闭原则”。
开闭原则(Open Closed Principle,也叫OCP)和设计模式无关,它是一种设计架构的原则,其核心思想是,系统(或模块或方法)应当对扩展开放,对修改关闭,好比对于上述案例,遇到扩展了,咱们没有修改现有代码,从而能够避免测试不相干的模块。
咱们就用简单工厂为例,来看下没采用开闭原则的后果,好比咱们仍是要建立Java和数据库方面的书,那么是在一个方法里根据参数的不一样来返回不一样种的类型。
若是要加新类型的书,只能是新加一个case,一旦有修改,那么咱们得改动第2行的create方法,这样一来,create方法(乃至BookFactory类)对修改就不关闭了。若是你们对此不理解,能够回顾下工厂模式的案例,当时遇到这个需求,咱们是经过添加CFactory类来实现的,原来的BookFactory和DBFactory并无改动(它们对修改关闭了)。
对比一下二者的差异,因为简单工厂模式没遵循开闭原则,那么一旦添加C语言的书籍,那么就影响到其它不相干的Java和DB书籍了(这两部分的case代码也得随之测试),这也是为何简单工厂模式适用场景比较少的缘由。
抽象工厂是对通常工厂模式的扩展,好比咱们在写java和数据库方面的书籍时,须要添加录制讲解视频的方法,也就是说,在Java书和数据库书这两个产品里,咱们不只要包含文稿,还得包含视频。
具体到生产Java书和数据库书的这两个工厂里,咱们要生产多类产品,不只得包括文稿,还得包括代码,此时就可使用抽象模式,示例代码以下。
在第1行里,咱们建立了视频的基类,在第4和第5行里,建立了针对Java和数据库书视频的两个类。
在第6行里,咱们定义了一个抽象工厂,在其中定义了建立视频和书籍的两个方法,在第11和16行,咱们经过继承这个抽象工厂,实现了生产两个具体Java和数据库书籍的工厂。
和通常工厂相比,抽象工厂的顶层类通常是抽象类(也就是抽象工厂名称的来源),但和通常工厂模式相比,没有优劣之分,只看哪一种模式更能适应需求。好比要在同一类产品(好比书)里生产多个子产品(好比文稿和视频),那么就能够经过抽象工厂模式,而若是须要生产的产品里只有主部件(好比文稿),而不须要附属产品(好比视频),那么就能够用通常工厂模式。
建造者模式和工厂模式都是关注于“建立对象”,在面试时,咱们通常会问它们的差异。经过工厂模式,咱们通常都是建立一个(或一类)产品,而不关心产品的组成部分,建造者模式也是用来建立一个产品,但它不只建立产品,更专一这个产品的组件和组成过程。
经过下面的代码,咱们来看下建造者模式的用法,你们能够对比下建造者和工厂模式的差异。
在第8行里,咱们定义了一个抽象的创造者类Builder,在第13和29这两行里,咱们经过继承Builder这个创造者类建立了两个实体创造者,分别用来创造Java和数据库的书籍。
在每个创造者里,咱们经过了setPaperBook方法创造文稿,经过setVideo建立视频,并把创造好的文稿和视频分别赋予bookWithVideo对象里的两个文稿和视频组件。
看到这里,彷佛和工厂模式差很少,因为建造者模式会偏重于组件的建立过程,因此会经过以下的总控类来组装对象,而工厂模式偏重于“建立产品“的这个结果,而不关注产品中组装各组件的过程,因此通常不会有总控类。
在总控类里的第46行里,咱们定义了用来建立书的productBook方法,请注意这个方法是抽象的builder类,经过下面的代码,咱们能看到如何经过上述定义的总控类和建造者类来动态地建立不一样种类的对象。
在第1行里,咱们定义了一个总控类,在第2和第3行里,咱们定义了具体的建立Java和数据库书籍的建造者对象,在第4和第5行里,分别传入了javaBookBuilder和dbBookBuilder这两个建造者对象,这样在总控类的productBook方法里,会根据传入参数类型的不一样,分别建造java和数据库书籍。
咱们常常经过建造者模式来建立项目里的业务对象,因此候选人在他们的项目里通常都会用到这种模式,在面试中也常常听到候选人用这种模式来举例,这里列一种比较好的回答。
第一,这位候选人用电商平台的订单来举例,首先他建立了一个订单的基类,在其中包括了商品列表、总价钱、总积分和发货地址这四个组件。
第二,经过继承这个订单基类,建立了两类订单,分别是“通常用户的订单”和“VIP客户的订单”,它们的算总价和算总积分的业务逻辑是不一样的。
第三,定义了一个抽象的建造者对象,在其中定义了诸如“统计商品”和“算总价”等的方法。
第四,经过继承抽象的建造者,定义了两个具体的建造者,分别用来建造“通常订单”和“VIP订单”,在每一个具体的建造者对象里,建立商品列表、总价钱、总积分和发货地址,并把它们组装成一个订单。
第五,也是关键点,须要建立一个总控类(这也是建造者模式的核心,也是和工厂模式的差异点),在其中提供一个productOrder(Builder builder)方法,它的参数是抽象的建造者。
至此构造了建造者模式的所有代码,在须要建立订单时,则能够经过productOrder(VIP订单的建造者对象)的调用方式,经过传入的具体的建造者对象(不是抽象的建造者对象)来完成建造。
上述的叙述是给你们作个参考,其实根据实际的项目需求叙述建造者模式并不困难,通常来讲,不少面试官会多问句,建造者模式和工厂模式有什么差异?这在前文里也说过了,你们能够经过项目需求详细说明。