Factory模式 http://blog.csdn.net/tf576776047/article/details/6895545
Factory模式 http://blog.csdn.net/tf576776047/article/details/6895545
我们知道设计模式的最基本的原则是状态变化部分和不变部分尽可能地分离,比如桥接(Bridge)模式采用的是抽象和抽象的实现分离,这种分离想达到的效 果就是较好的复用,就是对开-闭原则的最大限度支持。我们学习模式,为的就是理解模式,充分重用前辈经验,站在巨人的肩膀上,为设计出好的设计找寻方法。
由GOF整理的23种设计模式共分三大类:创建模式、结构模式和行为模式。
人们往往认为最为简单的就是创建模式,也许是因为日常工作中涉及到的机会更多些,尤其是工厂类模式,若有面试时一般情况下面试官会说除工厂类模式之外请你 介绍下你较为熟悉的模式。不过,在创建模式中,单例(Singleton)模式却是常被拿来作为笔试考察的,侧重于单例模式的实现(所谓的饿汉、懒汉、注 册、双重检查等实现方式优劣以及多类加载器、多JVM情况下Singleton的表现)及其使用场景。
尽管创建模式被认为相对简单,但加深对它们的认识还是必要的。创建模式,它抽象了类的实例化过程,针对类,使用继承改变了真正被实例化的类,针对对象,则 会将对象实例化委托给另一个对象。从该角度看,创建模式利用继承和引用的方式来完成需要的各种对象的创建工作,把和对象的创建相关的事务统一进行处理,并 通过创建不同类别的对象来应对信息系统外部的变化,为系统提供灵活性。当系统的灵活性不是依赖于多态继承机制,并随着系统演化得越来越依赖于对象的复合, 创建模式变得更为重要,甚至成为系统灵活机制的核心,原因就在于创建模式可以通过委托方式来满足这一需求。
创建模式包含有简单工厂(SimpleFactory)模式、抽象工厂(AbstractFactory)模式、建造者(Builder)模式、单例 (Singleton)模式和原型(Prototype)模式,以下篇幅的介绍对象是简单工厂模式、抽象工厂模式、建造者模式以及由工厂和抽象工厂引出的 工厂方法模式,单例和原型将在以后篇幅给予阐述。
#简单工厂(SimpleFactory)模式:又称静态工厂方法模式,是由一个工厂对象决定创建出哪一种产品的实例。下图是工厂模式的一般性示意图:
从示意图中我们可以看出该模式的优点是客户端Client无需关心产品的创建,创建责任有核心的工厂类Factory负责;而缺点是核心的工厂类 Factory必须知道要创建哪一类产品及如何被创建,尤其是当产品类有不同的接口种类时,核心的工厂类Factory需要判断何时生产何种商品,使得系 统将来扩展比较困难。因此,该模式此时对“开-闭”原则的支持度为有限支持,即不影响客户端,但需要维护工厂自身逻辑。
关于实现,我们首先看Product,它应该是抽象类呢还是接口呢?
这个问题其实在关于模式中 曾提到过的,并给出了个人认为较为合适的答案。答案的核心在于是否需要多继承及是否彼此间有相同的商业逻辑需要共享进行代码上移。一般情况下推荐使用接口 (现代开发最佳实践面向接口编程),这里给出几种具体场景的个人认为该使用何种方式的答案。其一是可能需要多继承,此时,Product此时均应为接口, 若有此种情况同时兼有商业逻辑需要共享,则遵循代码上移的原则,可再抽象出一层AbstractProduct实现Product接口完成此使命,若在关于模式中举的水果和蔬菜的例子,它们各自有共享商业逻辑代码的需要,同时又存在多继承的可能性(蔬菜的西红柿变身水果);其二是不存杂多继承,但需要共享商业逻辑代码,Product应为抽象类。
其次看核心的工厂类的工厂方法,若该工厂只负责生产一种产品类型,借助java反射是完全可以做到完全支持“开-闭”原则的,即增加新产品不会影响原有系统功能,如下实现说明:
- //不完全支持开-闭原则的实现,因为if...else模式使得新加入产品时需要维护Factory
- publicclass Factory {
- publicstatic Product factory(String which) {
- if(which.equalsIgnoreCase("product1") ){
- returnnew ConcreteProduct1();
- } elseif(which.equalsIgnoreCase("product1")) {
- returnnew ConcreteProduct2();
- } else {
- thrownew RuntimeException("the product type doesn't exist");
- }
- }
- }
- //完全支持开-闭原则,因为java反射消除了新增产品对Factory的维护
- publicclass Factory {
- publicstatic Product factory(String productpath) {
- try{
- return (Product) Class.forName(productpath).newInstance();
- }catch(Exception e){
- thrownew RuntimeException("the product type doesn't exist");
- }
- }
- }
- //不完全支持开-闭原则的实现,因为if...else模式使得新加入产品时需要维护Factory
- public class Factory {
- public static Product factory(String which) {
- if(which.equalsIgnoreCase("product1") ){
- return new ConcreteProduct1();
- } else if(which.equalsIgnoreCase("product1")) {
- return new ConcreteProduct2();
- } else {
- throw new RuntimeException("the product type doesn't exist");
- }
- }
- }
- //完全支持开-闭原则,因为java反射消除了新增产品对Factory的维护
- public class Factory {
- public static Product factory(String productpath) {
- try{
- return (Product) Class.forName(productpath).newInstance();
- }catch(Exception e){
- throw new RuntimeException("the product type doesn't exist");
- }
- }
- }
若该工厂负责生产至少两种产品类型,则该简单工厂模式只能保持它对开-闭原则的不完全支持,因为,核心的工厂类Factory需要判断何时生产何种商品, 不免会因产品的增加而需要维护工厂自身逻辑。对于这种产品族工厂生产需求,为了满足对开-闭原则的支持需要使用后面将要介绍的抽象工厂模式。
这里稍微提一下针对抽象(接口)编程,针对抽象编程--利用具体产品类的超类类型,将它的真实类隐藏起来。其优点是提供了系统的可扩展性。(若将来有新的 具体子类被加入到系统中来,那么工厂类可以将交给客户端的对象换成新的子类实例,而对客户端无任何影响)这种将工厂方法的返回类型设置成抽象产品类型的做 法,叫做针对抽象编程,这是依赖倒转原则(DIP)的应用。
最后,我们来看一下针对简单工厂方法在单产品和产品族下存在的更好的模式,即工厂方法和抽象工厂。
#工厂方法(FactoryMethod):把对象的创建延迟到子类中进行,其核心是在父类和子类之间分配创建的责任,即把原先应该有父类直接完成的事情交给子类去做。
工厂方法与产品存在层对应关系,一般的工厂方法模式示意图如下:
由上面工厂方法的示意图我们可以看出工厂方法与简单工厂有如下几点关系:1、结构上明显不同。工厂方法的核心是一个抽象工厂类(XCreator),简单 工厂模式把核心放在一个具体类上。2、工厂方法模式保持了简单工厂模式的优点,且客服了它的缺点。简单工厂模式中,一个工厂类处于产品类实例化的中心位置 上,它知道每一个产品,决定哪一个产品应当被实例化,存在一定的优缺点;工厂方法模式是简单工厂模式的进一步抽象和推广。因使用了多态性,工厂方法模式保 持了简单工厂模式的优点,且客服了它的缺点。工厂方法模式中核心的工厂类不再负责所有产品的创建,而是将具体的创建工作交给了子类去做,这个核心类则摇身 变为了一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。这种进一步抽象的结果,使得工厂方法模式允 许在不修改具体工厂角色的情况下引入新的产品,遵循“开-闭”(实指单产品下相比SimpleFactory的if...else实现模式)。
为了更加直观的对该模式产生认识,给出以下示例性代码:
- //client invoke sample
- publicclass Client {
- private XCreator xConcreteCreator1,xConcreteCreator2;
- private XProudct xConcreteProduct1,xConcreteProduct2;
- publicstaticvoid main(String[] args) {
- xConcreteCreator1 = new XConcreteCreator1();
- xConcreteProduct1 = xConcreteCreator1 .factory();
- xConcreteCreator2 = new ConcreteCreator2();
- xConcreteProduct2 = xConcreteCreator2 .factory();
- }
- }
- //creator sample
- publicinterface XCreator{
- public XProduct factory();
- }
- publicclass XConcreteCreator1 implements XCreator {
- public XProduct factory() {
- retrun new XConcreteProduct1();//maybe more complisity creating the product
- }
- publicclass XConcreteCreator2 implements XCreator {
- public XProduct factory() {
- retrun new XConcreteProduct2();//maybe more complisity creating the product
- }
- //product sample
- publicinterface XProduct{}
- publicclass XConcreteProduct1 implements XProduct {}
- publicclass XConcreteProduct2 implements XProduct {}
- //client invoke sample
- public class Client {
- private XCreator xConcreteCreator1,xConcreteCreator2;
- private XProudct xConcreteProduct1,xConcreteProduct2;
- public static void main(String[] args) {
- xConcreteCreator1 = new XConcreteCreator1();
- xConcreteProduct1 = xConcreteCreator1 .factory();
- xConcreteCreator2 = new ConcreteCreator2();
- xConcreteProduct2 = xConcreteCreator2 .factory();
- }
- }
- //creator sample
- public interface XCreator{
- public XProduct factory();
- }
- public class XConcreteCreator1 implements XCreator {
- public XProduct factory() {
- retrun new XConcreteProduct1();//maybe more complisity creating the product
- }
- public class XConcreteCreator2 implements XCreator {
- public XProduct factory() {
- retrun new XConcreteProduct2();//maybe more complisity creating the product
- }
- //product sample
- public interface XProduct{}
- public class XConcreteProduct1 implements XProduct {}
- public class XConcreteProduct2 implements XProduct {}
关于工厂方法的更好学习实例推荐参考java源码中迭代子实现,其结构示意图如下图左,工厂方法的一般性调度流程示意图如下图右:
实际上,使用工厂方法FaintMethod的核心根源并不在于仅仅把对象的创建延迟到子类中,那只是处理方法。使用的原则是客户对象需要处理很多的某种 子类对象,客户对象面临两个问题,一个是选择创建哪一个子类的对象,一个是被选定的子类对象如何人被建立。但是这两个逻辑,工厂方法模式把他们分开了。在 模式之外选择建立哪一个子类对象,而在具体建立这个对象的时候,又使用工厂类的子类来屏蔽建立的这个子类对象的特殊性。这样,客户就可以独立于需要使用的 子类对象的变化而始终保持稳定了。 理解工厂方法的时候,需要认识到工厂方法的子类仅仅是屏蔽了产品子类创建的不一致性,而具体选择哪一个产品则是外部逻辑提供的。工厂方法的价值在于,产品 对象的建立和初始化的不同被屏蔽了,因此,如果子类对象的创建方法没有大的不同,那么使用工厂方法模式是无价值的。
#抽象工厂(AbstractFactory )模式: 抽象工厂引入产品簇的概念(工厂与产品存在层次结构)!该模式是所有形态的的工厂模式(简单工厂、工厂方法、抽象工厂)中最为抽象&最具一般性 (如下图,左边代表工厂,右边代表不同产品形成的产品簇,而右图是一般性的实现)。该模式用意是向客户端提供一个接口,使得客户端在不必指定产品具体类型 的情况下,创建多个产品簇中的产品对象。
上图展示了由ProductA、ProductB为类别的两类产品,存在簇1{ProductA1,ProductB1}和簇 2{ProductA2,ProductB2},并分别有工厂ConcreteCreator1和ConcreteCreator2负责创建,为了更直观 展示给朋友们形成感性认识,下面给出示例性代码:
- publicinterface Creator {
- public ProductA fatoryA();
- public ProductB factoryB();
- }
- publicclass ConcreteCreator1 implements Creator {
- public ProductA factoryA() {
- retrun new ProductA1();
- }
- public ProductB factoryB() {
- retrun new ProductB1();
- }
- }
- publicclass ConcreteCreator2 implements Creator {
- public ProductA factoryA() {
- retrun new ProductA2();
- }
- public ProductB factoryB() {
- retrun new ProductB2();
- }
- }
- publicinterface ProductA {}
- publicclass ProductA1 implements ProductA {
- public ProductA1(){}
- }
- publicclass ProductA2implements ProductA {
- public ProductA2(){}
- }
- publicinterface ProductB {}
- publicclass ProductB1 implements ProductB {
- public ProductB1(){}
- }
- publicclass ProductB2implements ProductB {
- public ProductB2(){}
- }
- public interface Creator {
- public ProductA fatoryA();
- public ProductB factoryB();
- }
- public class ConcreteCreator1 implements Creator {
- public ProductA factoryA() {
- retrun new ProductA1();
- }
- public ProductB factoryB() {
- retrun new ProductB1();
- }
- }
- public class ConcreteCreator2 implements Creator {
- public ProductA factoryA() {
- retrun new ProductA2();
- }
- public ProductB factoryB() {
- retrun new ProductB2();
- }
- }
- public interface ProductA {}
- public class ProductA1 implements ProductA {
- public ProductA1(){}
- }
- public class ProductA2implements ProductA {
- public ProductA2(){}
- }
- public interface ProductB {}
- public class ProductB1 implements ProductB {
- public ProductB1(){}
- }
- public class ProductB2implements ProductB {
- public ProductB2(){}
- }
在上面阐述简单工厂(SimpleFactory)模式时曾提及对于产品族需求的工厂,简单工厂(SimpleFactory)模式无法很好的支持开-闭 原则,那么,抽象工厂(AbstractFactory)模式对开-闭原则的支持度到底又如何呢?简单地说,是有选择的完全支持。在产品等级结构的数目不 变的情况下,增加新的产品族,对原有系统不会造成任何影响,此时是完全支持开-闭原则。然而,在产品族的数目不变的情况下,加新的产品等级结构,则原有系 统实现的FactoryMethod都应做相应的调整,此时是处于不支持开-闭原则状态的。
关于何时应该考虑使用抽象工厂,前人总结了以下几点:1、一个系统不应当依赖于产品实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要 的。2、系统拥有的产品有多余一个的产品族,而系统只消费其中某一族的产品。(与抽象工厂的起源有关)3、同属于同一个产品族的产品是在一起使用的,这一 约束必须在系统的设计中体现出来。4、系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
以上这几点在抽象工厂(AbstractFactory)模式的起源中有很好的体现,其实,抽象工厂起源于跨操作系统的产品实现,通过下图的抛砖引玉,相信大家已不言自明了:
#建造者(Builder )模式:将一个产品 的内部表象与产品的生产过程分离,从而可以使一个建造过程生成具有不同内部表象的产品对象。若我们把工厂模式看做现实世界的生产车间,负责某种产品的创建 工作,那么我们可以把建造者模式看做是组装车间,负责产品组装形成相对复杂的产品。比如采用Builder模式来生成汽车,我们便可以用工厂方法生产汽车 所需的各类部件(轮胎、车灯、方向盘、发动机等等),然后由Builder负责组装成最终产品汽车。下图是通用性的Builder模式示例图:
这样以来,我们便可以看到简单工厂模式、工厂方法、抽象工厂模式与建造者模式它们之间的一些关系与侧重点。简单工厂模式负责产品的创建,使得客户端无需关 心产品的创建过程,工厂方法是针对简单工厂模式存在的缺点(不完全支持开-闭原则,增加新产品虽不影响客户端,但需要维护简单工厂逻辑)而引入的,抽象工 厂模式引入了产品族的概念,是工厂模式的进一步扩展,而建造者模式则更侧重于复杂产品的生产,期间可以利用工厂模式完成builderPart工作。
以下是使用建造者模式几种场景以及期待达到的效果。
场景:1、需要生成的产品对象有复杂的内部结构。(工厂各车间:每个内部成分本身可以是对象,也可以仅是产品对象的一部分,自身并不构成对象概念)
2、需要生成的产品对象的属性相互依赖。(工厂流水线:builder模式可以强制实行一种分步骤进行的建造过程,因此,若产品对象的一个属性必须在另一个属性被赋值之后才可以赋值,使用builder模式便是一个很好的设计思想)
3、在对象创建过程中会使用到系统中的其它一些对象,这些对象在产品的创建过程中不易得到。
效果:使用builder模式主要有以下效果:
1、builder模式的使用使得产品的内部表象可以独立地变化。客户端不必知道产品内部组成的细节。
2、每一个builder都相对独立,与其它的builder无关。
3、模式所建造的最终产品更易于控制。
在这里我们着重看下建造者(Builder)模式与工厂(Factory)(主要是AbstractFactory)模式间的关系。两者很相似,都是用来
创建同时属于几个产品族的对象的模式。不同之处在于--AbstractFactory模式中(相对简单产品),每一次工厂对象被调用时都会返回一个完整
的对象,至于client如何处理该对象与工厂自身无关;Builder模式(相对复杂产品,侧重产品组装流水),它则是一点一点地建造出一个复杂的产
品,而这个产品的组装过程就发生在builder角色内部。这两种模式,AbstractFactory更加微观,而Builder更加宏观。一个系统可
以由一个bulider和一个AbstractFactory组成,client通过调用这个builder角色,间接地调用另一个
AbstractFactory角色,AbstractFactory模式返还不同产品族的零件,而Builder模式则把他们组装起来。
从某种角度来说,Build模式也是我们完成生产某种产品工作的策略,这不免会使我们想到策略(Strategy)模式。事实上,Builder模式是
Strategy模式的一种特殊情况,而区别在于各自的目标不同。Builder模式适用于为client客户端一点一点地建造新的对象,而不同类型的具
体Builder角色虽然都拥有相同的接口,但它们所创建出来的对象可能完全不同;Strategy模式目的是为算法提供抽象的接口,即利用里氏替换满足
“开-闭”,通过具体策略类封装某算法,不同的策略类对象为一种一般性的服务提供不同的实现。
与此同时,我们看到Builder组装产品的过程类似于模板(Template)模式,比如
builder.builderPart1()-->builder.builderPart2()-->retrieveResult()
这样一条流水生产产品,事实上,失去Director角色后的Builder模式顺理成章地发展到了Template模式。
说到这里,大家可能已经意识到模式间是存在关系的,这里便涉及到如何区别与联系这些模式间关系的问题,在这里,我给出一些个人建议。
首先要看模式的类别,一般来说,结构模式和行为模式服务于创造模式,创造模式和服务于客户所需的最终产品。创建模式侧重于产品的创建方法,结构模式侧重于
产品的结构间关系结构设计,行为模式侧重于出于创建产品的目的,在创作过程中表现出的行为。其次要看各模式的目的及使用场景与达到的效果,这个过程主要是
考察重用粒度与对开-闭原则的支持度,在关于模式中我们已经提到过,保障重用原则的是代码上移&数据下移,保障开-闭原则的是依赖倒转、接口隔离、迪米特原则、里氏替换原则和组合/聚集,不再赘述。《暂完》
Factory模式 http://blog.csdn.net/tf576776047/article/details/6895545的更多相关文章
- http://blog.csdn.net/LANGXINLEN/article/details/50421988
GitHub上史上最全的Android开源项目分类汇总 今天在看博客的时候,无意中发现了 @Trinea在GitHub上的一个项目 Android开源项目分类汇总, 由于类容太多了,我没有一个个完整地 ...
- 手把手教你如何玩转消息中间件(ActiveMQ) https://blog.csdn.net/cs_hnu_scw/article/details/81040834
#情景引入小白:起床起床起床起床....快起床~我:怎么了又,大惊小怪,吓到我了.小白:我有事有事想找你,十万火急呢~~我:你能有什么事?反正我不信..那你说说看~~小白:就是我有两个小表弟,叫大白和 ...
- RTP协议分析(转自:http://blog.csdn.net/bripengandre/article/details/2238818)
RTP协议分析 第1章. RTP概述 1.1. RTP是什么 RTP全名是Real-time Transport Protocol(实时传输协议).它是IETF提出的一个标准,对应的RFC文 ...
- 转:Java面试题集(51-70) http://blog.csdn.net/jackfrued/article/details/17403101
Java面试题集(51-70) Java程序员面试题集(51-70) http://blog.csdn.net/jackfrued/article/details/17403101 摘要:这一部分主要 ...
- Windows下QT4.8.4编译环境的搭建(转载http://blog.csdn.net/bestgonghuibin/article/details/38933141)
开始使用QT了,所以第一步就是把环境搭起来,这里小记一下,以免以后忘记. 1. 下载安装文件 要使用QT功能,那么必须要下载QT的源码,还必须要一个是用QT的编译环境,可以是VS2010,也可以是专用 ...
- 学习mongoDB的一些感受(转自:http://blog.csdn.net/liusong0605/article/details/11581019)
曾经使用过mongoDB来保存文件,最一开始,只是想总结一下在开发中如何实现文件与mongoDB之间的交互.在此之前,并没有系统的了解过mongoDB,虽然知道我们用它来存储文件这些非结构化数据,但是 ...
- RedHat如何关闭防火墙 : http://blog.csdn.net/chongxin1/article/details/76072758
版本号:RedHat6.5 JDK1.8 Hadoop2.7.3 hadoop 说明:从版本2开始加入了Yarn这个资源管理器,Yarn并不需要单独安装.只要在机器上安装了JDK就可以直接安 ...
- 解析Javascript事件冒泡机制(转) 本文转自:http://blog.csdn.net/luanlouis/article/details/23927347
本文转自:http://blog.csdn.net/luanlouis/article/details/23927347 1. 事件 在浏览器客户端应用平台,基本生都是以事件驱动的,即某个事件发生,然 ...
- mybaits入门(含实例教程和源码) http://blog.csdn.net/u013142781/article/details/50388204
前言:mybatis是一个非常优秀的存储过程和高级映射的优秀持久层框架.大大简化了,数据库操作中的常用操作.下面将介绍mybatis的一些概念和在eclipse上的实际项目搭建使用. 一.mybati ...
随机推荐
- chrome bookmarks location
.config/google-chrome/Default file: Bookmarks
- spring mongo data api learn
1 索引 1.1 单列索引 @Indexed @Field(value = "delete_flag") private Boolean deleteFlag = false; @ ...
- js if和switch,==和===
今天改插件BoxScroll的时候,因为if里面的条件判断多于两个,于是立马想着改写switch.改到一半,忽然记起来JSHint等代码质量检测工具中的一个要求,用===替换==,不用不可靠的强制转型 ...
- select2和bootstrap模态框一起使用导致select2的input获取不到焦点问题
select2和bootstrap模态框一起使用导致select2的input获取不到焦点问题 解决办法: 把页面中的 tabindex="-1" 删掉, 或者值改为1 代码片 ...
- IOS runtime动态运行时二
在C#.Java中有编译时多态和运行时多态,在OC中,只有运行时的多态,这与它的运行机制有关.OC中,方法的调用是通过消息的传递来进行的.在IOS runtime动态运行时一http://www.cn ...
- Delphi 枚举所有进程
通过进程快照枚举所有进程,关于进程快照,在Delphi5开发人员指南中有说明,当然也可以百度一下用法. 使用进程快照CreateToolhelp32Snapshot,必须uses TlHelp32单元 ...
- ie6的设置外边距margin变双倍的问题
子元素避免同时使用float和margin. 如: 需要子元素的margin-bottom:20px时,可以给用父元素设置padding-bottom:20px代替.
- Linux基础学习1--档案的属性和目录
用命令 ls -al可以列出当前所有档案,和档案的各种情况 第一块是档案属性:一共10个,第一个代表档案类型 {d:目录,-:档案,l:连接档,b:接口设备,c:串行端口设备},接下来是三个一组,第一 ...
- jQuery事件篇---事件对象
内容提纲: 1.事件对象 2.冒泡和默认行为 发文不易,转载请注明出处! JavaScript 在事件处理函数中默认传递了 event 对象,也就是事件对象.但由于浏览器的兼容性,开发者总是会做兼容方 ...
- 数据库连接池之C3P0
一.C3P0的使用步骤 1:导入相关的依赖jar包 c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar 2:代码实现 A:硬编码的实现方式 package ...