`
seaizon
  • 浏览: 145172 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

研磨设计模式之简单工厂模式-3(转)

阅读更多

3  模式讲解

3.1  典型疑问

        首先来解决一个常见的疑问:可能有朋友会认为,上面示例中的简单工厂看起来不就是把客户端里面的“new Impl()”移动到简单工厂里面吗?不还是一样通过new一个实现类来得到接口吗?把“new Impl()”这句话放到客户端和放到简单工厂里面有什么不同吗?
        理解这个问题的重点就在于理解简单工厂所处的位置。
        根据前面的学习,我们知道接口是用来封装隔离具体的实现的,目标就是不要让客户端知道封装体内部的具体实现。简单工厂的位置是位于封装体内的,也就是简单 工厂是跟接口和具体的实现在一起的,算是封装体内部的一个类,所以简单工厂知道具体的实现类是没有关系的。整理一下简单工厂的结构图,新的图如图7所示:


                                        图7  整理后的简单工厂结构

        图7中虚线框,就好比是一个组件的包装边界,表示接口、实现类和工厂类组合成了一个组件,在这个封装体里面,只有接口和工厂是对外的,也就是让外部知道并使用的,所以故意漏了一些在虚线框外,而具体的实现类是不对外的,被完全包含在虚线框内。
        对于客户端而言,只是知道了接口Api和简单工厂Factory,通过Factory就可以获得Api了,这样就达到了让Client在不知道具体实现类的情况下获取接口Api。
        所以看似简单的把“new Impl()”这句话从客户端里面移动到了简单工厂里面,其实是有了质的变化的。


3.2  认识简单工厂

(1)简单工厂的功能
        工厂嘛,就是用来造东西的。在Java里面,通常情况下是用来造接口的,但是也可以造抽象类,甚至是一个具体的类实例。
         一定要注意,虽然前面的示例是利用简单工厂来创建的接口,但是也是可以用简单工厂来创建抽象类或者是普通类的实例的。
(2)静态工厂
        使用简单工厂的时候,通常不用创建简单工厂类的类实例,没有创建实例的必要。因此可以把简单工厂类实现成一个工具类,直接使用静态方法就可以了,也就是说 简单工厂的方法通常都是静态的,所以也被称为静态工厂。如果要防止客户端无谓的创造简单工厂实例,还可以把简单工厂的构造方法私有化了。
(3)万能工厂
        一个简单工厂可以包含很多用来构造东西的方法,这些方法可以创造不同的接口、抽象类或者是类实例,一个简单工厂理论上可以构造任何东西,所以又称之为“万能工厂”。
       虽然上面的实例中,在简单工厂里面只有一个方法,但事实上,是可以有很多这样创建方法的,这点要注意。
(4)简单工厂创建对象的范围
       虽然从理论上讲,简单工厂什么都能造,但对于简单工厂可创建对象的范围,通常不要太大,建议控制在一个独立的组件级别或者一个模块级别,也就是一个组件或模块一个简单工厂。否则这个简单工厂类会职责不明,有点大杂烩的感觉。
(5)简单工厂的调用顺序示意图
       简单工厂的调用顺序如图8所示:


 图8  简单工厂的调用顺序示意图


(6)简单工厂命名的建议

 

  • 类名建议为“模块名称+Factory”,比如:用户模块的工厂就称为:UserFactory
  • 方法名称通常为“get+接口名称”或者是“create+接口名称”,比如:有一个接口名称为UserEbi,那么方法名称通常为:getUserEbi 或者是 createUserEbi。
  •  当然,也有一些朋友习惯于把方法名称命名为“new+接口名称”,比如:newUserEbi,我们不是很建议。因为new在Java中代表特 定的含义,而且通过简单工厂的方法来获取对象实例,并不一定每次都是要new一个新的实例。如果使用newUserEbi,这会给人错觉,好像每次都是 new一个新的实例一样。

 

3.3  简单工厂中方法的写法

 

        虽然说简单工厂的方法多是用来造接口的,但是仔细分析就会发现,真正能实现功能的是具体的实现类,这些实现类是已经做好的,并不是真的靠简单工厂来创造出来的,简单工厂的方法无外乎就是:实现了选择一个合适的实现类来使用。
        所以简单工厂方法的内部主要实现的功能是“选择合适的实现类”来创建实例对象。既然要实现选择,那么就需要选择的条件或者是选择的参数,选择条件或者是参数的来源通常又有几种:

  • 来源于客户端,由Client来传入参数
  • 来源于配置文件,从配置文件获取用于判断的值
  • 来源于程序运行期的某个值,比如从缓存中获取某个运行期的值

         下面来看个示例,看看由客户端来传入参数,如何写简单工厂中的方法。
(1)在刚才的示例上再添加一个实现,称为Impl2,示例代码如下:

Java代码  收藏代码
  1. /**  
  2.  * 对接口的一种实现   
  3.  */   
  4. public   class  Impl2  implements  Api{  
  5.     public   void  test1(String s) {  
  6.         System.out.println("Now In Impl The input s==" +s);  
  7.     }  
  8. }  
/**
 * 对接口的一种实现 
 */
public class Impl2 implements Api{
	public void test1(String s) {
		System.out.println("Now In Impl The input s=="+s);
	}
}

 (2)现在对Api这个接口,有了两种实现,那么工厂类该怎么办呢?到底如何选择呢?不可能两个同时使用吧,看看新的工厂类,示例代码如下:

Java代码  收藏代码
  1. /**  
  2.  * 工厂类,用来创造Api的  
  3.  */   
  4. public   class  Factory {  
  5.     /**  
  6.      * 具体的创造Api的方法,根据客户端的参数来创建接口  
  7.      * @param type 客户端传入的选择创造接口的条件  
  8.      * @return 创造好的Api对象  
  9.      */   
  10.     public   static  Api createApi( int  type){  
  11.         //这里的type也可以不由外部传入,而是直接读取配置文件来获取   
  12.         //为了把注意力放在模式本身上,这里就不去写读取配置文件的代码了   
  13.       
  14.         //根据type来进行选择,当然这里的1和2应该做成常量   
  15.         Api api = null ;  
  16.         if (type== 1 ){  
  17.             api = new  Impl();  
  18.         }else   if (type== 2 ){  
  19.             api = new  Impl2();  
  20.         }  
  21.         return  api;  
  22.     }  
  23. }  
/**
 * 工厂类,用来创造Api的
 */
public class Factory {
	/**
	 * 具体的创造Api的方法,根据客户端的参数来创建接口
	 * @param type 客户端传入的选择创造接口的条件
	 * @return 创造好的Api对象
	 */
	public static Api createApi(int type){
		//这里的type也可以不由外部传入,而是直接读取配置文件来获取
		//为了把注意力放在模式本身上,这里就不去写读取配置文件的代码了
	
		//根据type来进行选择,当然这里的1和2应该做成常量
		Api api = null;
		if(type==1){
			api = new Impl();
		}else if(type==2){
			api = new Impl2();
		}
		return api;
	}
}

 (3)客户端没有什么变化,只是在调用Factory的createApi方法的时候需要传入参数,示例代码如下:

Java代码  收藏代码
  1. public   class  Client {  
  2.     public   static   void  main(String[] args) {  
  3.         //注意这里传递的参数,修改参数就可以修改行为,试试看吧   
  4.         Api api = Factory.createApi(2 );  
  5.         api.test1("哈哈,不要紧张,只是个测试而已!" );  
  6.     }  
  7. }  
public class Client {
	public static void main(String[] args) {
		//注意这里传递的参数,修改参数就可以修改行为,试试看吧
		Api api = Factory.createApi(2);
		api.test1("哈哈,不要紧张,只是个测试而已!");
	}
}

 (4)要注意这种方法有一个缺点
由于是从客户端在调用工厂的时候,传入选择的参数,这就说明客户端必须知道每个参数的含义,也需要理解每个参数对应的功能处理。这就要求必须在一定程度上,向客户暴露一定的内部实现细节。


3.4  可配置的简单工厂

        现在已经学会通过简单工厂来选择具体的实现类了,可是还有问题。比如:在现在的实现中,再新增加一种实现,会怎样呢?
        那就需要修改工厂类,才能把新的实现添加到现有系统中。比如现在新加了一个实现Impl3,那么需要类似下面这样来修改工厂类:

Java代码  收藏代码
  1. public   class  Factory {  
  2.     public   static  Api createApi( int  type){  
  3.         Api api = null ;  
  4.         if (type== 1 ){  
  5.             api = new  Impl();  
  6.         }else   if (type== 2 ){  
  7.             api = new  Impl2();  
  8.         }  
  9.   
  10.         else   if (type== 3 ){  
  11.             api = new  Impl3();  
  12.         }  
  13.         return  api;  
  14.     }  
  15. }  
public class Factory {
	public static Api createApi(int type){
		Api api = null;
		if(type==1){
			api = new Impl();
		}else if(type==2){
			api = new Impl2();
		}

		else if(type==3){
			api = new Impl3();
		}
		return api;
	}
}

        每次新增加一个实现类都来修改工厂类的实现,肯定不是一个好的实现方式。那么现在希望新增加了实现类过后不修改工厂类,该怎么办呢?
         一个解决的方法就是使用配置文件,当有了新的实现类过后,只要在配置文件里面配置上新的实现类就好了,在简单工厂的方法里面可以使用反射,当然也可以使用IoC/DI(控制反转/依赖注入,这个不在这里讨论)来实现。
         看看如何使用反射加上配置文件,来实现添加新的实现类过后,无须修改代码,就能把这个新的实现类加入应用中。
(1)配置文件用最简单的properties文件,实际开发中多是xml配置。定义一个名称为“FactoryTest.properties”的配置文件,放置到Factory同一个包下面,内容如下:

Java代码  收藏代码
  1. ImplClass=cn.javass.dp.simplefactory.example5.Impl  
ImplClass=cn.javass.dp.simplefactory.example5.Impl

 如果新添加了实现类,修改这里的配置就可以了,就不需要修改程序了。
(2)此时的工厂类实现如下:

Java代码  收藏代码
  1. /**  
  2. * 工厂类,用来创造Api对象  
  3. */   
  4. public   class  Factory {  
  5.     /**  
  6.      * 具体的创造Api的方法,根据配置文件的参数来创建接口  
  7.      * @return 创造好的Api对象  
  8.      */   
  9.     public   static  Api createApi(){  
  10.         //直接读取配置文件来获取需要创建实例的类   
  11.         //至于如何读取Properties,还有如何反射这里就不解释了   
  12.         Properties p = new  Properties();   
  13.         InputStream in = null ;  
  14.         try  {  
  15.             in = Factory.class .getResourceAsStream(  
  16. "FactoryTest.properties" );  
  17.             p.load(in);  
  18.         } catch  (IOException e) {  
  19.             System.out.println(  
  20. "装载工厂配置文件出错了,具体的堆栈信息如下:" );  
  21.             e.printStackTrace();  
  22.         }finally {  
  23.             try  {  
  24.                 in.close();  
  25.             } catch  (IOException e) {  
  26.                 e.printStackTrace();  
  27.             }  
  28.         }  
  29.         //用反射去创建,那些例外处理等完善的工作这里就不做了   
  30.         Api api = null ;  
  31.         try  {  
  32.             api = (Api)Class.forName(p.getProperty("ImplClass" ))  
  33. .newInstance();  
  34.         } catch  (InstantiationException e) {  
  35.             e.printStackTrace();  
  36.         } catch  (IllegalAccessException e) {  
  37.             e.printStackTrace();  
  38.         } catch  (ClassNotFoundException e) {  
  39.             e.printStackTrace();  
  40.         }  
  41.         return  api;  
  42.     }  
  43. }  
/**
* 工厂类,用来创造Api对象
*/
public class Factory {
	/**
	 * 具体的创造Api的方法,根据配置文件的参数来创建接口
	 * @return 创造好的Api对象
	 */
	public static Api createApi(){
		//直接读取配置文件来获取需要创建实例的类
		//至于如何读取Properties,还有如何反射这里就不解释了
		Properties p = new Properties(); 
		InputStream in = null;
		try {
			in = Factory.class.getResourceAsStream(
"FactoryTest.properties");
			p.load(in);
		} catch (IOException e) {
			System.out.println(
"装载工厂配置文件出错了,具体的堆栈信息如下:");
			e.printStackTrace();
		}finally{
			try {
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		//用反射去创建,那些例外处理等完善的工作这里就不做了
		Api api = null;
		try {
			api = (Api)Class.forName(p.getProperty("ImplClass"))
.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return api;
	}
}

 (3)此时的客户端就变得很简单了,不再需要传入参数,代码示例如下:

Java代码  收藏代码
  1. public   class  Client {  
  2.     public   static   void  main(String[] args) {  
  3.         Api api = Factory.createApi();  
  4.         api.test1("哈哈,不要紧张,只是个测试而已!" );  
  5.     }  
  6. }  
public class Client {
	public static void main(String[] args) {
		Api api = Factory.createApi();
		api.test1("哈哈,不要紧张,只是个测试而已!");
	}
}

 把上面的示例代码敲到电脑里面,测试一下,体会体会。


3.5  简单工厂的优缺点

  • 帮助封装
        简单工厂虽然很简单,但是非常友好的帮助我们实现了组件的封装,然后让组件外部能真正面向接口编程。
  • 解耦
        通过简单工厂,实现了客户端和具体实现类的解耦。
        如同上面的例子,客户端根本就不知道具体是由谁来实现,也不知道具体是如何实现的,客户端只是通过工厂获取它需要的接口对象。
  • 可能增加客户端的复杂度
        如果通过客户端的参数来选择具体的实现类,那么就必须让客户端能理解各个参数所代表的具体功能和含义,这会增加客户端使用的难度,也部分暴露了内部实现,这种情况可以选用可配置的方式来实现。
  • 不方便扩展子工厂
        私有化简单工厂的构造方法,使用静态方法来创建接口,也就不能通过写简单工厂类的子类来改变创建接口的方法的行为了。不过,通常情况下是不需要为简单工厂创建子类的。

 


3.6  思考简单工厂

1:简单工厂的本质
        简单工厂的本质是:选择实现
        注意简单工厂的重点在选择,实现是已经做好了的。就算实现再简单,也要由具体的实现类来实现,而不是在简单工厂里面来实现。简单工厂的目的在于为客户端来 选择相应的实现,从而使得客户端和实现之间解耦,这样一来,具体实现发生了变化,就不用变动客户端了,这个变化会被简单工厂吸收和屏蔽掉。
        实现简单工厂的难点就在于 “如何选择”实现,前面讲到了几种传递参数的方法,那都是静态的参数,还可以实现成为动态的参数。比如:在运行期间,由工厂去读取某个内存的值,或者是去读取数据库中的值,然后根据这个值来选择具体的实现等等。
2:何时选用简单工厂
        建议在如下情况中,选用简单工厂:

  • 如果想要完全封装隔离具体实现,让外部只能通过接口来操作封装体,那么可以选用简单工厂,让客户端通过工厂来获取相应的接口,而无需关心具体实现
  • 如果想要把对外创建对象的职责集中管理和控制,可以选用简单工厂,一个简单工厂可以创建很多的、不相关的对象,可以把对外创建对象的职责集中到一个简单工厂来,从而实现集中管理和控制


3.7  相关模式

  • 简单工厂和抽象工厂模式
        简单工厂是用来选择实现的,可以选择任意接口的实现,一个简单工厂可以有多个用于选择并创建对象的方法,多个方法创建的对象可以有关系也可以没有关系。
        抽象工厂模式是用来选择产品簇的实现的,也就是说一般抽象工厂里面有多个用于选择并创建对象的方法,但是这些方法所创建的对象之间通常是有关系的,这些被创建的对象通常是构成一个产品簇所需要的部件对象。
        所以从某种意义上来说,简单工厂和抽象工厂是类似的,如果抽象工厂退化成为只有一个实现,不分层次,那么就相当于简单工厂了。
  • 简单工厂和工厂方法模式
        简单工厂和工厂方法模式也是非常类似的。
        工厂方法的本质也是用来选择实现的,跟简单工厂的区别在于工厂方法是把选择具体实现的功能延迟到子类去实现。
        如果把工厂方法中选择的实现放到父类直接实现,那就等同于简单工厂。
  • 简单工厂和能创建对象实例的模式
        简单工厂的本质是选择实现,所以它可以跟其它任何能够具体的创建对象实例的模式配合使用,比如:单例模式、原型模式、生成器模式等等。


 简单工厂模式结束,谢谢观赏

 

转载自:http://chjavach.iteye.com/blog/834679

分享到:
评论

相关推荐

    研磨设计模式-配套源代码 UTF-8格式

    《研磨设计模式》是一本深入探讨软件设计原则与实践的经典书籍,其配套源代码提供了丰富的实例,帮助读者更好地理解和应用各种设计模式。这个UTF-8格式的压缩包包含了书中介绍的各种设计模式的实现,是学习和研究...

    研磨设计模式源码

    《研磨设计模式源码》是一份非常宝贵的资源,它提供了设计模式的实践代码,帮助开发者深入理解并应用这些模式。设计模式是软件工程中经过长期实践总结出来的一套通用解决方案,它们描述了在特定场景下如何解决常见...

    研磨设计模式(完整带书签).part2.pdf

    《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。初级内容从基本讲起,包括每个模式的定义、功能、思路、结构、基本实现、运行调用顺序、基本应用示例等,让读者能系统、完整、准确地掌握每个模式,...

    研磨设计模式之工厂方法模式.

    《研磨设计模式之工厂方法模式》是一本深入解析Java设计模式的专业书籍,特别是对工厂方法模式进行了详尽的探讨。设计模式是软件工程中的一种最佳实践,它们是解决常见问题的经验总结,使得代码更加可读、可维护和可...

    研磨设计模式-配套源代码

    "研磨设计模式-配套源代码"很显然是一份与学习和理解设计模式相关的资源,其中包含了实际的编程示例。这份压缩包可能包括了多种常见设计模式的实现,如单例模式、工厂模式、观察者模式、装饰器模式等,通过源代码的...

    研磨设计模式PDF

    《研磨设计模式》这本书是陈臣和王斌两位作者合作的成果,专注于讲解软件设计中的模式应用。设计模式是软件工程中的一种最佳实践,它总结了在特定上下文中解决问题的常见方法,使得开发者可以复用这些解决方案,提高...

    研磨设计模式--chjavach的博客文章

    研磨设计模式的过程是持续学习和实践的过程,chjavach的博客文章提供了深入探讨这些模式的宝贵资源,值得我们仔细阅读和学习。通过深入理解和运用这些设计模式,可以提升个人的编程技巧,同时也为团队合作和项目维护...

    研磨设计模式-配套源代码.7z

    《研磨设计模式》是一本深入探讨软件设计模式的书籍,其配套源代码包含了许多经典设计模式的实际应用示例。这些源代码可以帮助读者更直观地理解设计模式的原理和使用方法,进一步提升软件开发能力。 设计模式是软件...

    研磨设计模式[书签]

    《研磨设计模式》是由陈臣和王斌合著,由清华大学出版社出版的一本深入探讨设计模式的专业书籍。设计模式是软件工程中的一个重要概念,它代表了在特定上下文中解决问题的常见方法,经过时间和实践的验证,具有很高的...

    研磨设计模式全部源代码

    这个压缩包“研磨设计模式全部源代码”包含了多种设计模式的实现,这些模式可以帮助开发者写出更可维护、可扩展和可复用的代码。下面将详细讲解其中可能包含的一些重要设计模式及其应用。 1. 工厂模式:这是最简单...

    研磨设计模式 源代码

    《研磨设计模式》是一本深入探讨软件设计模式的经典书籍,源代码包含了书中所讲解的各种设计模式的实际应用示例。设计模式是软件工程中的重要概念,它们是经过反复验证、在特定情境下解决常见问题的有效解决方案。...

    研磨设计模式讲课PPT

    研磨设计模式是一本深入探讨软件设计原则与实践的书籍,其讲课PPT为我们提供了丰富的设计模式知识。设计模式是软件工程中经过实践验证的、解决常见问题的模板,是经验丰富的开发人员智慧的结晶。这些模式可以帮助...

    研磨设计模式-配套源代码.rar

    《研磨设计模式》是一本深入探讨软件设计模式的书籍,配套源代码是作者为了帮助读者更好地理解和应用书中介绍的设计模式而提供的实践示例。设计模式是软件开发中经过实践检验的、解决常见问题的模板,它为软件设计...

    研磨设计模式博文集

    这个“研磨设计模式博文集”显然是一份深入探讨设计模式的资料集合,其中可能包含了对多种设计模式的详细解析、示例代码以及实际应用中的经验分享。在软件开发中,设计模式能够帮助开发者提高代码质量、可读性和可...

    研磨设计模式之 工厂方法模式.doc

    研磨设计模式之 工厂方法模式.doc

    研磨设计模式.rar

    《研磨设计模式》这本书是软件开发领域中的经典之作,主要关注的是面向对象设计中的设计模式。设计模式是在特定上下文中解决常见问题的最佳实践,它为开发者提供了在类似情况下重复使用解决方案的模板,有助于提高...

    研磨设计模式-part3

    《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。初级内容从基本讲起,包括每个模式的定义、功能、思路、结构、基本实现、运行调用顺序、基本应用示例等,让读者能系统、完整、准确地掌握每个模式,...

    研磨设计模式(完整带书签).part1.pdf

    《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。初级内容从基本讲起,包括每个模式的定义、功能、思路、结构、基本实现、运行调用顺序、基本应用示例等,让读者能系统、完整、准确地掌握每个模式,...

    研磨设计模式 演示源代码

    "研磨设计模式 演示源代码"这个资源包含了对设计模式的详细解释和实例分析,旨在帮助学习者深入理解和应用这些模式。 1. **单例模式**:确保一个类只有一个实例,并提供一个全局访问点。在资源管理、缓存或者线程池...

    研磨设计模式-part2

    《研磨设计模式》完整覆盖GoF讲述的23个设计模式并加以细细研磨。初级内容从基本讲起,包括每个模式的定义、功能、思路、结构、基本实现、运行调用顺序、基本应用示例等,让读者能系统、完整、准确地掌握每个模式,...

Global site tag (gtag.js) - Google Analytics