为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

JAVA面向对象编程

2009-02-06 49页 pdf 695KB 26阅读

用户头像

is_838409

暂无简介

举报
JAVA面向对象编程 第 4章 模式:Spring前行的路标 在上一章中研究了 Spring 的一些核心理念,这些理念有的是编程原则和信条,有的是自成 一派的方法论。本章将讲述 Spring与模式的知识。如果读者已经熟悉 Spring 和模式的基本 关系,可以直接略过本章,进入后续篇章。相反,如果没有听说过模式,可以通过本章彻底 了解模式核心。 4.1 模式(Pattern)入门 4.1.1 什么是模式 琼瑶小说就是一种模式,是一种“催泪模式”,让一个女读者流眼泪简单,难的是让千千万 万个女读者都流眼泪,可见模式之威力所在。...
JAVA面向对象编程
第 4章 模式:Spring前行的路标 在上一章中研究了 Spring 的一些核心理念,这些理念有的是编程原则和信条,有的是自成 一派的方法论。本章将讲述 Spring与模式的知识。如果读者已经熟悉 Spring 和模式的基本 关系,可以直接略过本章,进入后续篇章。相反,如果没有听说过模式,可以通过本章彻底 了解模式核心。 4.1 模式(Pattern)入门 4.1.1 什么是模式 琼瑶小说就是一种模式,是一种“催泪模式”,让一个女读者流眼泪简单,难的是让千千万 万个女读者都流眼泪,可见模式之威力所在。于是乎,依照琼瑶小说的催泪模式,制作了影 视作品,毋庸置疑,依照催泪模式制作的影视作品必有催泪作用,如图 4.1所示。 图 4.1 模式比喻 可以看出,琼瑶写小说,发现了很多催泪元素,通过写作经验的逐步积累,从而总结出了一 套“催泪模式”,而这样的模式在场景 1中被发明,随后可以将其应用到类似的场景中,而 不需要在其他场景中从头来过,这就是模式的魅力所在。 4.1.2 不用模式产生的问题 本书中探讨的模式是针对面向对象软件的。出可复用度高、具有足够通用性的软件绝非 易事,缺乏经验的研发者遇到每个开发环节,会给出诸如“先 A后 B 再 C”的解决方案, 依照逻辑来看诚然没错。但是如果使用这样的思维进行编码,那么抛开灵活性差、耦合度高 不说,它还存在着巨大隐患。 1.面向过程的编码 代码 4.1中关于最笨的钟点工代码,被称为非面向对象思维(一般指过程化思维)和拷贝式 复用(相同的代码到处粘贴拷贝以形成复用),如下: 代码 4.1 NonOOThinkingAndReuse.java package chapter4.pattern; public class NonOOThinkingAndReuse { /** * 一套做杂务的脚本方法 */ private static void makeOddJob() { System.out.println("洗衣服"); System.out.println("擦窗"); System.out.println("烹调"); System.out.println("洗碗"); } public static void main(String[] args) throws Throwable { System.out.println("洗衣服,烹调,洗碗,擦窗这些杂务需要钟点工们打理"); //最笨的钟点工做杂务 System.out.println("最笨的钟点工"); System.out.println("来到雇主家,首次开始做杂务..."); System.out.println("洗衣服"); System.out.println("擦窗"); System.out.println("烹调"); System.out.println("洗碗"); System.out.println("雇主付钱"); System.out.println("来到雇主家,第二次开始做杂务..."); //程序员甲通过复制上述的一大段代码,到下面,结果漏了洗碗 System.out.println("洗衣服"); System.out.println("擦窗"); System.out.println("烹调"); System.out.println("雇主吃饭使用了脏碗,一怒之下解雇了马虎的钟点工"); System.out.println("啊,被解雇了,我只是忘记了洗碗,真不值!"); System.out.println(); //稍微聪明一点的钟点工 System.out.println("稍微聪明一点的钟点工"); /* * 程序员乙通过将脚本式的 System out代码重构到一个 makeOddJob方法中, * 迫使钟点工记住所有的工作内容和次序,大大降低了出错的概率,也提高了可重用性和灵活 性,只要修改 * 一个方法就可以修改所有做杂务的方式 */ System.out.println("来到雇主家,首次开始做杂务..."); makeOddJob(); System.out.println("来到雇主家,第二次开始做杂务..."); makeOddJob(); System.out.println("雇主付钱"); System.out.println("轻松完成任务,赚到钱了"); } } 可以看到通过对笨钟点工编码方式的优化,聪明钟点工复用了 makeOddJob()方法,这是一 个不小的进步。然而存在的一个大缺点是 makeOddJob()方法作为一个类的私有内嵌方法, 被永远留在了这个 NonOOThinkingAndReuse类中。如果代码中所描述的笨钟点工某天想使 用聪明钟点工的方式做事,或者说,程序员丙想使用程序员乙已经发明过的 makeOddJob() 方法,那是不可能的。因为这些脚本已经留在了某个类中,成为了历史而很难被追溯,所以 程序员丙又必须重新思考编码。 2.面向对象的编码 现在就用面向对象的思维来组织编码,代码 4.2~4.6所示。 代码 4.2 OOThinkingAndReuse.java public class OOThinkingAndReuse { public static void main(String[] args) { //同一个雇主 Master master = new Master(); //同样的杂务 OddJob oddJob = new OddJob(); //最笨的钟点工 StupidHourWorker worker1 = new StupidHourWorker(oddJob); //聪明一点的钟点工 CleverHourWorker worker2 = new CleverHourWorker(oddJob); //雇主根据钟点工的工作表现,有不同的反应 master.process(worker1.doJob()); master.process(worker2.doJob()); } } 代码 4.3 OddJob.java /** * 杂务 */ public class OddJob { private boolean isAllDone = false; public OddJob() { description(); } public boolean isAllDone() { return isAllDone; } /** * 一套做杂务的完整方法 */ public void doItAll() { System.out.println("洗衣服"); System.out.println("擦窗"); System.out.println("烹调"); System.out.println("洗碗"); isAllDone = true; } /** * 缺少洗碗的一套做杂务的方法 */ public void doItExceptWashDish() { System.out.println("洗衣服"); System.out.println("擦窗"); System.out.println("烹调"); isAllDone = false; } public void description() { System.out.println("洗衣服,烹调,洗碗,擦窗这些杂务需要钟点工们打理"); } } 代码 4.4 Master.java import chapter4.pattern.better.Worker; /** * 雇主类 */ public class Master { public void pay() { System.out.println("雇主付钱"); } public void fire() { System.out.println("雇主吃饭使用了脏碗,解雇了钟点工"); } public void process(boolean isJobAllDone) { if (isJobAllDone) { pay(); } else { fire(); } } public void process(Worker worker) { if (worker.doJob()) { pay(); } else { fire(); } } } 代码 4.5 StupidHourWorker.java /** * 笨钟点工类 */ public class StupidHourWorker { private OddJob oddJob; private static String BAD_FEELING = "啊,被解雇了,我只是忘记了洗碗,真不值!"; public StupidHourWorker(OddJob oddJob) { this.oddJob = oddJob; } public boolean doJob() { System.out.println("最笨的钟点工"); System.out.println("来到雇主家,开始做杂务..."); oddJob.doItExceptWashDish(); return oddJob.isAllDone(); } public void feel() { System.out.println(BAD_FEELING); } } 代码 4.6 CleverHourWorker.java /** * 聪明一点的钟点工类 */ public class CleverHourWorker { private OddJob oddJob; private static String GOOD_FEELING = "轻松完成任务,赚到钱了"; public CleverHourWorker(OddJob oddJob) { this.oddJob = oddJob; } public boolean doJob() { System.out.println("聪明一点的钟点工"); System.out.println("来到雇主家,开始做杂务..."); oddJob.doItAll(); return oddJob.isAllDone(); } public void feel() { System.out.println(GOOD_FEELING); } } 代码 4.3~4.6都是记载有某种属性和行为的特定类,当需要使用的时候,Java语言通过 new 关键字分配内存并产生这些对象,如代码 4.2所示。这样使得任何一个对象实例都可以进行 独立复用。当采用了面向对象的编程方式以后,人和事物被模拟成了类的交互形式,从而使 得对事物的描述更直观易懂了,只需要短短几行代码,就可以轻松表达代码 4.1的逻辑。 如果把对象的职责分配得更贴切一些,形成一定的模式,并加以命名后(比如代码 4.2),一 旦遇上相同问题就可以进行套用,可以使笨钟点工立即变成聪明钟点工。 4.1.3 通过实例理解模式本质 本质上来说,可以复用的东西都可称之为模式。模式的一些基本要素包括:模式名称、问题 (何时使用模式)、解决方案和模式效果。 模式的粒度将决定模式的应用范围和强弱能力,可以发现,代码 4.3~4.6中,这些类的粒度 都非常细,粒度细就是所描述事物太具体,限定了这些事物的应用范围,例如只能让 StupidHourWorker 做马虎的杂务活,却不能把 StupidHourWorker 当 CleverHourWorker 来使 用,所以笔者以为这个两个模式粒度太细所以应用范围太窄,功能太弱。 思考一下,如果将 StupidHourWorker和 CleverHourWorker都作为Worker抽象类(注:此处 的抽象类不是特指 Abstract class,而是指具有抽象能力的类,因此也可以是 Interface)的实 现。依靠这样的解决方案,适用场景将会更广,模式效果就会好许多,如图 4.2所示。 图 4.2 类继承和模式抽象 列出抽象化后的代码,如代码 4.7~4.10所示。 代码 4.7 BetterReuse.java import chapter4.pattern.better.ClerverHourWorker; import chapter4.pattern.better.StupidHourWorker; import chapter4.pattern.better.Worker; public class BetterReuse { public static void main(String[] args) { //有一个笨钟点工 Worker worker = new StupidHourWorker(); //笨钟点工忽然变聪明了,或者想采用聪明钟点工的工作方式 worker = new ClerverHourWorker(); //还是同一个雇主 Master master = new Master(); //雇主并不需要知道钟点工聪明还是笨,自己会处理 master.process(worker); } } 代码 4.8 Worker.java package chapter4.pattern.better; public interface Worker { boolean doJob(); void feel(); } 代码 4.9 ClerverHourWorker.java package chapter4.pattern.better; import chapter4.pattern.Worker; public class ClerverHourWorker implements Worker { public boolean doJob() { //此处省略 return true; } public void feel() { //此处省略 } } 代码 4.10 StupidHourWorker.java import chapter4.pattern.Worker; public class StupidHourWorker implements Worker { public boolean doJob() { //此处省略 return false; } public void feel() { //此处省略 } } 如图 4.2和代码 4.7~4.10所示,从模式复用的角度出发,Worker越抽象,粒度就越粗,复用 机会也就越大。可以想象一下,代码 4.7 中,Worker 的句柄(Handler)分别被作为局部变 量和 process()方法的参数进行了复用。在其他场景需要的时候,Worker 类还可以被继承或 者组合复用。在此处,因为找到了复合依据,就可以称这个模式为 Worker模式。这些场景 依赖于 Worker模式工作,而完全不知道子类的实现方式,也意味着在历史的某个点上,比 如代码 4.7的 main函数中,通过 worker = new ClerverHourWorker()这行代码,在静态编译期 决定了Worker模式的工作方式将会由子类 ClerverHourWorker来提供。 4.1.4 做好准备,继续前行 从前面可以看出,直接利用 new 来生成一个具体类的实例不是一个好的作法,后面将要介 绍的 Factory模式和依赖倒置可以修正这个方法。 说明:本书介绍的模式主要收纳于设计模式(GOF:Design Pattern),J2EE模式和架构(框架)模式这几 个模式名录(收集了各种模式)中,它们的粒度针对它们各自的应用场景都足够粗,所以这些都是好的模 式名录,自发明以来沿用至今,堪称经典。 本章的目的只是挑选出一些比较直观的模式进行讲解,使读者将模式和 Spring 尽快地结合 在一起,这对学好 Spring 是非常有好处的。可以肯定的是,Spring 框架的很多灵感和构建 知识都来自于这些模式。 4.2 工厂模式(Design Pattern:Factory Method)的精 髓 工厂模式被收录于设计模式(Gof的著作),设计模式依照其目的可以分为如下类型: ‰ 创建型模式:与对象的创建有关。 ‰ 结构型模式:处理类或对象的组合。 ‰ 行为型模式:描述类或对象怎样交互和怎样分配职责。 工厂模式属于创建型模式,目的是定义出一个用于创建对象的接口,让子类来决定需要实例 化哪个类,使得低层细节代码中不会出现类似代码 4.7 中 Worker worker = new ClerverHourWorker()这种情况。如果像 4.7一样,那就违反了依赖倒置原则(DIP)。该原则 讲述了高层模块不应该依赖于低层模块,细节应该依赖于抽象,接下来的讲解将围绕 DIP 原则逐步过渡到工厂模式。 4.2.1 引入问题 如图 4.3所示,在软件系统中,这样的层次好像没有什么问题。但是,策略层直接使用了实 现层,实现层又直接使用了工具辅助层,如图 4.4所示。 图 4.3 依赖传递 图 4.4 依赖传递类图 给出实现,如代码 4.11~4.14所示。 代码 4.11 StrongDependency.java package chapter4.pattern.factory.strongdep; public class StrongDependency { public static void main(String[] args) { Policy policy = new Policy(); policy.doPolicy(); } } 代码 4.12 Policy.java public class Policy { private Implemention impl; public Policy() { this.impl = new Implemention(); } public void doPolicy() { impl.doImpl(); } } 代码 4.13 Implemention.java public class Implemention { private Util util; public Implemention() { util = new Util(); } public void doImpl() { util.utilMethod(); } } 代码 4.14 Util.java public class Util { public void utilMethod() {} } 可以看出,每个位于高层抽象的类,都直接依赖于低层细节类的实现,这是非常糟糕的。这 意味着将细节替换成其他实现是不可能的。而且由于这种依赖性的层间传递,任何对低层细 节的改变都将直接波及高层抽象,使整个系统僵硬、无法测试。 4.2.2 解决方法 为了解决上述问题,引入实现层抽象接口和工具辅助层抽象接口,如图 4.5所示。这样整个 系统发生了很大变化,如图 4.6所示。 图 4.5 依赖倒置和反向控制 图 4.6 依赖倒置和反向控制类图 给出实现,如代码 4.15~4.20所示。 代码 4.15 DipIocUsage.java package chapter4.pattern.factory .dipioc; public class DipIocUsage { public static void main(String[] args) { UtilService utilService = new ConcreteUtil(); ImplementionService implService = new ConcreteImplemention(utilService); Policy policy = new Policy(implService); policy.doPolicy(); } } 代码 4.16 Policy.java public class Policy { private ImplementionService implService; public Policy(ImplementionService implService) { this.implService = implService; } public void doPolicy() { implService.doImpl(); } } 代码 4.17 ImplementionService.java public interface ImplementionService { void doImpl(); } 代码 4.18 ConcreteImplemention.java public class ConcreteImplemention implements ImplementionService { private UtilService utilService; public ConcreteImplemention(UtilService utilService) { this.utilService = utilService; } public void doImpl() { utilService.utilMethod(); } } 代码 4.19 UtilService.java public interface UtilService { void utilMethod(); } 代码 4.20 ConcreteUtil.java public class ConcreteUtil implements UtilService { public void utilMethod() {} } 可以发现,当引入抽象服务接口后,高层模块将通过依赖于抽象接口来使用低层服务,从而 不再依赖于低层模块,此时低层细节反而依赖于高层抽象接口了,这使得控制权体系完全反 转了。 在代码 4.15 中,因为低层模块都是针对接口编程的(这非常重要),所以客户代码(main 函数)可以通过抽象接口,直接控制低层模块的具体实现,而不是由低层模块自己来处理依 赖关系。这样可以在高层轻松替换低层实现,并且整个系统更易于测试。虽然客户代码仍然 破坏着 DIP 原则,但客户代码往往是作为入口或者系统初始的时候执行一次,可以用下面 即将介绍的工厂模式来修正,问题不大。但是通过将依赖抽离到较高的层次,可以发现,依 赖倒置使得控制反转和依赖注射皆成为了可能。 4.2.3 工厂模式和依赖倒置的关系 接下来工厂模式和依赖倒置的关系。其实,有了上述的讨论,工厂模式已经呼之欲出了。 下面直接给出工厂模式的类图,如图 4.7所示。 图 4.7 经典工厂模式类图 对工厂模式参与者简介如下: ‰ Product:定义工厂方法所创建的对象的接口。 ‰ ConcreteProdut:实现 Product接口。 ‰ Creator:声明工厂方法,该方法返回一个 Product类型的对象;Creator也可以是抽 象类(Abstract class)或者具体类,以返回一个工厂方法。 ConcreteCreator:重定义工厂方法,以返回一个 ConcreteProduct对象。 给出工厂模式实现,如代码 4.21~4.25所示。 代码 4.21 Client.java package chapter4.pattern.factory; public class Client { public static void main(String[] args) { Creator factory = new ConcreteCreator(); Product product = factory.factoryMethod(); } } 代码 4.22 Creator.java public interface Creator { public Product factoryMethod(); } 代码 4.23 ConcreteCreator.java public class ConcreteCreator implements Creator { public Product factoryMethod() { return new ConcreteProduct(); } } 代码 4.24 Product.java public interface Product { } 代码 4.25 ConcreteProduct.java public class ConcreteProduct implements Product { } 可以看到,经典工厂模式类图的Creator体系和图 4.6相似,区别是:前者多出了产品(Product) 这个平行的继承体系。这个变化在很大程度上解决了客户代码对具体类的依赖问题。在代码 4.21中,客户代码主要使用 Creator和 Product这两个接口工作,而不会再依赖于 Product的 细节。 惟一遗憾的是,Creator 仍然需要依赖于它的子类来决定工厂的行为。注意,这是多态性工 厂无法避免的,因为依赖关系总得有一个地方进行处理。根据 DIP 原则或 IOC 原则,决定 依赖关系的场所越高层、越抽象越好。 在本例的上下文环境中,Client main函数已足够高层,所以把对 ConcreteCreator的依赖架设 在 main 函数中,而在 Creator 接口的具体子类 ConcreteCreator 中,工厂方法能够产生具体 产品,是比较高级的行为,工厂体系本身也已足够抽象,所以把对 ConcreteProduct 的依赖 架设于此。 提示:但这不是最好的方法,后面会给出更好的解决方案。 最后,在图 4.6中,通过引入两个抽象接口 ImplementionService和 UtilService,使得所有的 依赖关系都被移动到代码 4.15的 main函数里了。这样造成了一个结果,就是客户代码仍在 多处对具体类产生依赖,下面用工厂模式来修正一下,如代码 4.26~4.32所示。 代码 4.26 DipIocFactoryUsage.java package chapter4.pattern.factory.dipioc; public class DipIocFactoryUsage { public static void main(String[] args) { //工厂初始化 UtilServiceFactory utilServiceFactory = new ConcreteUtilServiceFactory(); ImplServiceFactory implServiceFactory = new ConcreteImplServiceFactory(); PolicyFactory policyFactory = new ConcretePolicyFactory(); //工厂开始制造产品 UtilService utilService = utilServiceFactory.make(); //<-工厂特例:参数化工厂 ImplementionService implService = implServiceFactory.make(utilService); Policy policy = policyFactory.make(implService); //-> //产品开始工作 policy.doPolicy(); } } 代码 4.27 UtilServiceFactory.java public interface UtilServiceFactory { public UtilService make(); } 代码 4.28 ConcreteUtilServiceFactory.java public class ConcreteUtilServiceFactory implements UtilServiceFactory { public UtilService make() { return new ConcreteUtil(); } } 代码 4.29 ImplServiceFactory.java public interface ImplServiceFactory { public ImplementionService make(UtilService utilService); } 代码 4.30 ConcreteImplServiceFactory.java public class ConcreteImplServiceFactory implements ImplServiceFactory { public ImplementionService make(UtilService utilService) { return new ConcreteImplemention(utilService); } } 代码 4.31 PolicyFactory.java public interface PolicyFactory { public Policy make(ImplementionService implService); } 代码 4.32 ConcretePolicyFactory.java public class ConcretePolicyFactory implements PolicyFactory { public Policy make(ImplementionService implService) { return new Policy(implService); } } 由于先前(代码 4.16~4.20)已经备有产品体系(Policy、ImplementionService、UtilService 及其子类),所以本例惟一要引入的,是增加一个平行的工厂体系,如代码 4.27~4.32。修改 完成后,看一下客户代码 4.26,通过使用工厂模式,使客户基本上只依赖于抽象产品和抽象 工厂,从而使工厂实现以及工厂方法成为了核心依赖点。这意味着,高层模块只要通过配置 或衍生工厂,就可以达到控管低层模块的目的。在代码 4.26 中提到的“工厂特例参数化工 厂”,属于经典工厂模式的一个变种。 4.3 单例模式(Design Pattern:Singleton) 单例模式属于创建型模式,目的是保证一个类仅有一个实例,并提供一个访问它的全局访问 点。考虑这样一种对象,这个对象应该在程序启动时被创建,并且在结束时被删除,如应用 程序的基础高层对象。通过这个对象可以得到系统中其他的对象,这些基础对象可能是前面 提到的工厂对象(Factories),用来创建其他对象;也可能是管理器对象(managers),负责 控管其他对象;或者是全局注册表(Registry)。类似这种类型的对象不该被创造出多份。 4.3.1 单例模式的实现 下面给出单例模式的实现。单例模式参与者如图 4.8 所示。其中 Singleton 定义了一个 getInstance操作,允许客户访问它的惟一实例。可能负责创建它的惟一实例。 图 4.8 经典单例模式类图 如图 4.8和代码 4.33,可以看到,经典单例模式的实现非常简单。而正是由于概念和实现上 的简单,没有顾及到逻辑概念上、测试性、全局依赖、类装载器、序列化、线程安全以及不 同 JVM之间等等方面的问题,造成了很多的误用。 代码 4.33 Singleton.java package chapter4.pattern.singleton; public class Singleton { private static Singleton instance; /** * 不允许通过构造字来实例化 */ private Singleton() { } /** * 使用 synchronized关键字,保障 Singleton的线程安全 */ public synchronized static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } 注意:Singleton模式应当谨慎使用。代码 4.33中给出了一种合理的使用方法,通过将Singleton 的构造子声明为私有,杜绝了外部或者子类的实例化倾向(当然这也是允许的)。 通过为 getInstance()方法签署 synchronized锁,保障了 Singleton是线程安全的。此处所指的 线程不安全是指在多线程环境下,可能产生不同 Singleton实例。另外,getInstance()方法实 际上是一种静态工厂方法,这是工厂模式的一个变种。考虑上节中工厂模式的例子,尝试结 合 Factory和 Singleton,如代码 4.34所示。 代码 4.34 UtilServiceSingletonFactory.java package chapter4.pattern.singleton; import chapter4.pattern.factory.dipioc.ConcreteUtil; import chapter4.pattern.factory.dipioc.UtilService; public class UtilServiceSingletonFactory { private static UtilServiceSingletonFactory instance; /** * 允许子类实例化 */ protected UtilServiceSingletonFactory() { } public synchronized static UtilServiceSingletonFactory getInstance() { if (instance == null) { instance = new UtilServiceSingletonFactory(); } return instance; } public UtilService make() { return new ConcreteUtil(); } } 客户代码可以是这样: UtilService utilService = UtilServiceSingletonFactory.getInstance().make(); 可以看到,通过单例工厂使得工厂对象实例数不超过 1个,同时给出了 Factory Method的实 现(make 方法),通过变换构造子修饰(private->protected),保留了 Factory 的派生性。如 果不考虑派生性,完全有理由使用静态工厂而不是单例工厂,静态工厂使工厂类不需要被实 例化,客户代码可以是这样: UtilService utilService = UtilServiceSingletonFactory.make(); 4.3.2 单例注册表 接下来将要介绍 Singleton模式的另一种常用实现,即单例注册表(Singleton Registry),如 代码 4.35~4.39所示。 代码 4.35 FactorySingletonRegistryUsage.java package chapter4.pattern.singleton; public class FactorySingletonRegistryUsage { public static void main(String[] args) { //实例化工厂注册表 FactorySingletonRegistry registry = FactorySingletonRegistry.getInstance(); //<- 通过反射机制,使得注册表可以依据给定的工厂全限定名返回具体工厂实例 //第一次索取 BeanFactory xmlBeanFactory1 = registry.getBeanFactory("chapter4.pattern.singleton.XmlBeanFactory"); BeanFactory listableBeanFactory1 = registry.getBeanFactory("chapter4.pattern.singleton.ListableBeanFactory") ; //第二次索取 BeanFactory xmlBeanFactory2 = registry.getBeanFactory("chapter4.pattern.singleton.XmlBeanFactory"); BeanFactory listableBeanFactory2 = registry.getBeanFactory("chapter4.pattern.singleton.ListableBeanFactory") ; //-> //比较先后两次索取的工厂实例,希望得到同一实例的工厂引用,结果正确 System.out.println(xmlBeanFactory1.hashCode() == xmlBeanFactory2.hashCode()); System.out.println(listableBeanFactory1.hashCode() == listableBeanFactory2.hashCode()); } } 代码 4.36 FactorySingletonRegistry.java import java.util.HashMap; import java.util.Map; public class FactorySingletonRegistry { private static FactorySingletonRegistry instance; private static Map factoryMap = new HashMap();① private FactorySingletonRegistry() { } public synchronized static FactorySingletonRegistry getInstance() { if (instance == null) { instance = new FactorySingletonRegistry(); } return instance; } public synchronized BeanFactory getBeanFactory(String factoryClassName) {② BeanFactory factory = (BeanFactory)factoryMap.get(factoryClassName); if (factory != null) return factory; try { factory = (BeanFactory)Class.forName(factoryClassName).newInstance(); ③ } catch (ClassNotFoundException e) { System.out.println("Couldn't find class " + factoryClassName); } catch (InstantiationException e) { System.out.println("Couldn't instantiate an object of type " + factoryClassName); } catch (IllegalAccessException e) { System.out.println("Couldn't access class " + factoryClassName); } factoryMap.put(factoryClassName, factory); return factory; } } 代码 4.37 BeanFactory.java public interface BeanFactory { } 代码 4.38 ListableBeanFactory.java public class ListableBeanFactory implements BeanFactory { public ListableBeanFactory() { System.out.println("ListableBeanFactory Created"); } } 代码 4.39 XmlBeanFactory.java public class XmlBeanFactory implements BeanFactory { public XmlBeanFactory() { System.out.println("XmlBeanFactory Created"); } } 对单例注册表的简单运作方式,做如下说明: (1)请看代码 4.36,首先这个类是一个单例类,在①处使用了Map对象,这是一个类,持 有聚集的讯息。所谓聚集通常就是在Map中以名值对(field-value)的形式存储一系列各类 对象的实例引用,通过 put(field,value)存储,通过 get(field)取得 value。在代码 4.36 的 getBeanFactory方法中,通过对 factoryMap的存取,可以使该单例注册表持有任意数量的工 厂实例,并且通过 if (factory != null)的判断,保证了返回的是对同一个工厂实例的引用。 (2)注意代码 4.36中的②处,getBeanFactory(String factoryClassName)是一个参数化的工厂 方法,和在代码 4.26中的类似。 说明:如果工厂方法依赖一个参数标识来决定产品的具体生产行为,那么可以说这就是一个参数化工厂方 法。参数化工厂的好处是,创建同一产品族系(拥有同一接口的具体产品)时,不再需要衍生具体的工厂 子类来对应。主要缺点是工厂职责过于集中,另外一个缺点仍然是所有传统工厂模式的通病,就是由于硬 编码的关系,需要使用 if/else语句来决定生产方式,无法摆脱 Product = new ConcreteProduct()这种代码带 来的强依赖性,同时限制了工厂创建产品的种类数目。这也意味着每当引入新的产品时,就需要重新变更 关联代码。 (3)在代码 4.36 中引入了反射(Reflection)技术,如③所示。在代码 4.35 中,可以以类 的全限定名作为参数传入工厂方法,工厂方法会通过反射技术来实例化那个类,而不需要预 先在工厂方法中记载有限的产品集合了。 至此,给出了单例模式的基本用法,单例模式是一种常用的模式,但也很容易被误用。 最后还提到了反射和工厂方法模式的一些结合,这是非常有用的技术。 4.4 模式和策略模式(Design Pattern:Template Method And Strategy) 本节将同时介绍模板(Template)模式(模
/
本文档为【JAVA面向对象编程】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索