2.2.    工厂模式

基于手工构建组件的诸多弱点,1995年“大师4人组”(GoF)在其经典著作《DesignPatterns》一书中提出了“工厂模式”,这种模式在一定程度上有效的解决了之前所遇到的问题,时至今日仍然被大量应用于软件工程的设计当中。

我们先来看之前的例子,首先来创建一个银行工厂和一个存折工厂,用于创建这两个依赖对象。

public class BankFactory {

public Bank createBankICBC() {

return new BankICBC("Tianjin", 100);

}

}

 

public class DepositBookFactory {

public DepositBook createDepositICBC() {

return new DepositBookICBC("62202", "current", "CNY");

}

}

有了这两个能够创建依赖对象的工厂,我们就可以在储户类Depositor中利用它们方便的构建出银行和存折这两个依赖。

public class Depositor {

private Bank bank;

private DepositBook depositBook;

public Cash withDraw(BigDecimal amount) {

bank = new
BankFactory().createBankICBC(); // 工厂创建依赖

depositBook = new
DepositBookFactory().createDepositICBC();// 工厂创建依赖

return bank.withDraw(depositBook,
amount);

}

}

我们可以看到,用工厂创建依赖对象,可以将创建对象的过程隐蔽,Depositor类的开发者利用这种透明创建对象的方式则可避免手工构建复杂依赖关系图所带来的诸多不便。并且,当依赖的构造方法接口发生变更时,影响到的也只是工厂类,而不会影响到调用工厂类的依赖者类本身,从而降低了系统的维护成本。

在实际的开发中,工厂往往是被定义为静态的单例模式的,即在一个JVM中有且仅有一个工厂的实例。但由于静态单例模式有很多的缺陷,因此在依赖注入框架流行之后这种模式已经渐渐的被抑制使用了。

但是随着软件工程的不断复杂化和规模化,工厂模式同样的遇到了很多问题而被开发人员诟病。围绕着这个话题的讨论有很多,我们不再一一列举,只举一个比较常见的问题来说明一下。

假设我们的银行不只有工商银行,还有招商银行、中国银行等等,这样就需要我们维护一个越来越庞大的工厂类。之前的Bank工厂就要逐渐这样增加create方法:

public class BankFactory {

public Bank createBankICBC() {

return new BankICBC("Tianjin", 100);

}

public Bank createBankCMB() { // …… };

public Bank createBankBOC() { // …… };

// ……

}

这样工厂类自身的维护成本逐渐增大,就成为了工厂模式的一个很大的障碍。因此很多开发者想办法用参数化的形式,来减少工厂类的维护量。

public class BankFactory {

public Bank createBank(BankVender bankVendor)
{ // 参数化的工厂方法

switch (bankVendor) {

case ICBC:

return createBankICBC();

case CMB:

return createBankCMB();

default:

return createBankICBC();

}

}

// ……

}

但是这样一来又会回到手工new对象的一个问题,即如果工厂方法的参数发生了变化,可能会引起全部调用到该工厂方法的代码都需要修改及做相应的回归测试。

2.3.    ServiceLocator模式

基于上面工厂模式的诸多问题,一些聪明的开发者提出了改进版的工厂模式:ServiceLocator,即服务定位器。这种模式的核心思想是,将构建依赖的接口彻底与依赖者分离,并将此依赖作为“服务”绑定到一个标识符(通常是一个字符串),而后依赖者则可通过这个标识符获取被绑定的依赖。例如我们的例子中,可以这样获得Bank和DepositBook的依赖:

public class Depositor {

private Bank bank;

private DepositBook depositBook;

public Cash withDraw(BigDecimal amount) {

bank = (Bank) new
ServiceLocator().get("BankICBC");

depositBook = (DepositBook) new
ServiceLocator().get("DepositBookICBC");

// 从ServiceLocator容器中获取依赖对象

return bank.withDraw(depositBook,
amount);

}

}

此处ServiceLocator#get(String)这个API的String型参数即为“服务”的标识符。那么这个标识符是如何被绑定到依赖的对象上的呢?答案是开发者需要实现一个能够管理这些依赖(即“服务”)的、类似于工厂类的一个单独的类,这也常常被称作“ServiceLocator容器”,这个容器负责各种依赖的注册、标识符绑定、提供依赖对象等工作。

说到此处大家一定会觉得开发这样一个ServiceLocator容器花的代价一定不小。不错,正因为如此,JCP组织联合各IT供应商制订了统一的ServiceLocator容器标准,开发者可以只管开发自己的依赖类,然后IT供应商提供的ServiceLocator容器实现(即各种应用服务器级中间件)通过统一的命名规则将某个标识符绑定到这些依赖类并注册到容器中,等待开发者的请求命令。

而ServiceLocator模式作为业界标准统一化后,一个最典型的应用便诞生了:JNDI。直到今天JNDI都是组件、包括分布式组件开发中不可或缺的耦合方式。例如DataSource资源的获取、RemoteEJB对象的获取等等,都是通过JNDI查找的方式进行的。例如获取RemoteEJB的时候,我们可以执行这样的JNDI查找:

RemoteBean rb = (RemoteBean)
initialContext.lookup("java:comp/env/TEST/RemoteBeanImpl");

但是ServiceLocator模式最大的弱点就是,依赖对象的获取强依赖于ServiceLocator容器,除非开发者愿意花费大量成本自己实现ServiceLocator容器,否则就必须要中间件提供商的支持,这就给我们的开发和测试带来了很大困扰。拿JNDI容器的实现来说,各提供商,比如Tomcat、JBoss、WebLogic、WebSphere等,对于JNDI的统一命名标识符的规则就不尽相同,使得开发者往往必须通过各自应用服务器的控制台才能准确找到需要的JNDI名,并且在单元测试时如果没有应用服务器启动下的环境,单元测试是难以顺利进行的,给维护带来了不小的困扰。

依赖注入及AOP简述(二)——工厂和ServiceLocator .的更多相关文章

  1. 依赖注入及AOP简述(十二)——依赖注入对象的行为增强(AOP) .

    四.依赖注入对象的行为增强(AOP) 前面讲到,依赖注入框架的最鲜明的特点就是能够提供受容器管理的依赖对象,并且可以对对象提供行为增强(AOP)功能,所以这一章我们来讨论有关AOP的话题. 1.    ...

  2. 依赖注入及AOP简述(五)——依赖注入的方式 .

    二.依赖注入的应用模式 前面我们了解了依赖注入的基本概念,也对一些依赖注入框架进行了简单的介绍,这一章我们主要来讨论作为开发者如何利用依赖注入框架来实现依赖注入的设计思想. 1.     依赖注入的方 ...

  3. 依赖注入及AOP简述(四)——“好莱坞原则”和依赖注入框架简介 .

    3.2.    “好莱坞原则” 看了前面关于依赖注入概念的描述,我们来提炼出依赖注入的核心思想.如果说传统的组件间耦合方式,例如new.工厂模式等,是一种由开发者主动去构建依赖对象的话,那么依赖注入模 ...

  4. 依赖注入及AOP简述(三)——依赖注入的原理

    3.     “依赖注入”登场 于是诸多优秀的IT工程师开始想出了更加轻量便利.更加具有可测试性和可维护性的设计模式——IoC模式.IoC,即Inversion of Control的缩写,中文里被称 ...

  5. 依赖注入及AOP简述(十三)——AOP应用举例(完结) .

    2.     AOP应用举例 在一般的应用程序开发中,有一些典型的AOP应用,使得开发者可以专注于业务逻辑本身,而不是与之完全无关的一些“方面”. l        首先就是关于前面介绍过的日志输出类 ...

  6. 依赖注入及AOP简述(九)——单例和无状态Scope .

    三.依赖注入对象的Scope及其生命周期 在前面的章节我们讲到,依赖注入容器之所以能够区别于以往的ServiceLocator等容器,是在于其不但能够自动构建多层次的.完整的依赖关系图,并且可以管理依 ...

  7. 依赖注入及AOP简述(七)——FQCN请求模式

    2.2.    FQCN请求模式 为了弥补纯字符串请求模式中的类型安全问题,全类名(FQCN)请求模式就应运而生了.其思想便是,在向容器请求依赖对象的时候,不是通过字符串的标识符.而是通过被请求的依赖 ...

  8. 依赖注入及AOP简述(六)——字符串请求模式 .

    2.     依赖注入对象的请求模式 前一节我们讨论了关于声明注入点的几种方法,这一节主要来介绍在注入点上如何定位到所需要的标识符的话题.基本上,我们可以用字符串为标识符来请求依赖对象.或者用全类名( ...

  9. 依赖注入及AOP简述(一)——“依赖”的概念 .

    一.入门:依赖注入 作为一种全新的设计模式理念,“依赖注入”这个词汇在软件设计开发中已经是越来越耳熟能详了,而各种流行于开源社区的“依赖注入框架”,也越来越多的被当作软件工程开发过程中使用的基础框架. ...

随机推荐

  1. ubuntu下的Samba配置:使每个用户可以用自己的用户名和密码登录自己的home目录

    http://blog.csdn.net/fly_qj/article/details/21744797 1.先要安装Samba sudo apt-get install samba openssh- ...

  2. yum安装epel库后,安装软件总是提示Error: Cannot retrieve metalink for repository: epel. Please verify its path and try again(无法检索epel仓库)

    解决方法: vi /etc/yum.repos.d/epel.repo 将baseurl中的注释取消,将mirrorlist该列注释掉即可. 附图如下:

  3. VS2003,安装程序检测到另一个程序…

    昨天在安装Visual Studio .Net 2003时,出现一个对话框   "安装程序检测到另一个程序要求计算机重新启动.必须重新启动计算机后才能安装visual studio.net系 ...

  4. win10下安装通过Hyper-v安装Ubuntu

    一直也来在做C#的开发,Winform及Web都有所涉及,想在闲暇之余学习下Python,拓展一下自己的知识.既然决定学习Python那么就直接在Linux下进行吧,由于Ubuntu最近很火而且也有方 ...

  5. sizeof 和 strlen

    1. sizeof 1.1 sizeof是一个独立的运算符,不是函数.sizeof给我们提供有关数据项目所分配的内存的大小.例如: 1 2 cout << sizeof(long) < ...

  6. oracle函数trunc的使用

    1.日期比较时精确到日,可以使用 TRUNC(sysdate,'dd')函数.函数支持格式有:yyyy MM  dd  hh Mi可以用   select TRUNC(sysdate,'yyyy') ...

  7. [转载]浅析Windows安全相关的一些概念

    Session 我们平常所说的Session是指一次终端登录, 这里的终端登录是指要有自己的显示器和鼠标键盘等, 它包括本地登录和远程登录.在XP时代每次终端登录才会创建一个Session,但是在Vi ...

  8. php 代码重用

    <?php /* 21.php * 代码重用 * include() required()载入文件 * include() 如果载入文件不存在,提示警告,还可以继续执行 * required() ...

  9. Python中yield深入理解

    众所周知,python中的yield有这样的用法: def test(alist): for i in alist: yield i 这样,这个test函数就变成了一个生成器,当每次调用的时候,就会自 ...

  10. C语言基础09

    指向结构体变量的指针叫做结构体指针: typedef struct { int num; char name[30];    // char *name; 程序会崩溃,*name本身是指针,没有什么空 ...