`
ahuaxuan
  • 浏览: 640722 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

pojos in action 第三章 翻译第三部分

阅读更多
 

jMock来实现这一点,然而,我们至少需要定义PendingOrderRepository接口并且写一个PendingOrderstub(桩)实现。PendingOrderRepository接口定义了一个findOrCreatePendingOrder()方法:

<o:p> </o:p>

       public  interface  PendingOrderRepository{

<o:p> </o:p>

              PendingOrder  findOrCreatePendingOrder(String  pendingOrderId);

      }

<o:p> </o:p>

PendingOrder类为updateDeliveryInfo()方法定义了一个桩,该桩将在后面一点实现:

<o:p> </o:p>

       public  class  PendingOrder{

<o:p> </o:p>

              public   boolean  updateDeliveryInfo(Address   deliveryAddress,

                                                                                     Date   deliveryTime){

             return  false;

              }

       }

<o:p> </o:p>

updateDeliveryInfo()方法的桩返回假。

<o:p> </o:p>

完成测试

<o:p> </o:p>

       我们已经编写了updateDeliveryInfo()方法并且定义了PendingOrderRepository接口和PendingOrder类,但是这时我们需要修正一下我们之前编写的测试程序。它不需要再编译一次因为PlaceOrderServiceImpl的构造方法通过被传递了一个PendingOrderRepository参数而具有了我们预期的行为。另外,测试程序必须创建和配置模拟的(假的)PendingOrderPendingOrderRepository。这个模拟的(mockPendingOrderRepository被期望来调用它的findOrCreatePendingOrder()方法并且返回一个模拟的(mockPendingOrderPendingOrder被期望来调用它自己的updateDeliveryInfo()方法。表3.4显示了一个更新测试。

<v:shapetype id="_x0000_t75" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" filled="f" stroked="f" coordsize="21600,21600" o:spt="75"><v:stroke joinstyle="miter"></v:stroke><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f></v:formulas><v:path o:connecttype="rect" o:extrusionok="f" gradientshapeok="t"></v:path><o:lock aspectratio="t" v:ext="edit"></o:lock></v:shapetype><v:shape id="_x0000_i1027" style="WIDTH: 6in; HEIGHT: 396pt" type="#_x0000_t75"><v:imagedata o:title="3" src="file:///C:\DOCUME~1\azhang\LOCALS~1\Temp\msohtml1\03\clip_image001.jpg"></v:imagedata></v:shape>

<v:shape id="_x0000_i1028" style="WIDTH: 6in; HEIGHT: 243pt" type="#_x0000_t75"><v:imagedata o:title="3" src="file:///C:\DOCUME~1\azhang\LOCALS~1\Temp\msohtml1\03\clip_image003.jpg"></v:imagedata></v:shape>

<v:shape id="_x0000_i1026" style="WIDTH: 6in; HEIGHT: 225pt" type="#_x0000_t75"><v:imagedata o:title="3" src="file:///C:\DOCUME~1\azhang\LOCALS~1\Temp\msohtml1\03\clip_image005.jpg"></v:imagedata></v:shape>

<o:p> </o:p>

让我们来看看这段测试程序做了哪些事

<o:p> </o:p>

1  测试用例继承了jMockMockObjectTestCase类,它能自动的检验mock对象的预期行为是否能被满足。

<o:p> </o:p>

2  setUp()方法创建了模拟的(mock)PendingOrderRepositoryPendingOrderPlaceOrderService

<o:p> </o:p>

3  测试程序定义了mock对象的预期行为和返回结构。模拟的PendingOrderRepository被计划用发货信息来调用findOrCreatePendingOrder()并且返回模拟的(mock)PendingOrder。模拟的(mock)PendingOrder被计划通过以发货信息为参数来调用其updateDeliveryInfo()方法。它返回true则表示发货信息是有效的而且PendingOrder被更新了

<o:p> </o:p>

4  在配置了预期行为之后,测试程序调用了PlaceOrderServeupdateDeliveryInfo()方法。

<o:p> </o:p>

5  然后测试程序断言调用是否成功,并且检验PlaceOrderService返回的模拟的(mock)PendingOrder。它不再检验PendingOrder是否包含了正确的发货信息因为它假设PendingOrder.updateDeliveryInfo()的行为是正确的。

<o:p> </o:p>

正如你能看到的,这个方法和它的测试程序有着很简单的关联关系,因为大多数的业务方法只是很简单的调用其他领域模型的对象(或者是领域模型中的其他对象)。因为利用了这些mock对象,我们就可以在不投入到它们(PlaceOrderService用到的类)的实现细节中也能开发和测试PlaceOrderService

<o:p> </o:p>

<o:p> </o:p>

3.3.2 实现一个领域实体的方法

<o:p> </o:p>

       我们已经实现了我们的第一个方法,它通过了测试并且能够正常工作!但是我们仍然还有很多事要做。当实现PlaceOrderService.updateDeliveryInfo()时,我们决定把它的职责分配给PendingOrderPendingOrder是一个领域模型中的实体(entity),然后调要它的updateDeliveryInfo()方法。这个方法检验发货信息并且更新PendingOrder。它返回一个能表明发货信息是否有效的布尔值。让我们来看看如何来实现这个方法。

<o:p> </o:p>

写一个测试

<o:p> </o:p>

       像之前那样,我们先写一个测试程序。表3.5是一个简单的测试,通过传递给该方法一个有效的发货信息来测试该方法是否能通过。它使用了不可见的RestaurantTestData来创建一些测试数据。

<v:shape id="_x0000_i1025" style="WIDTH: 486pt; HEIGHT: 435pt" type="#_x0000_t75"><v:imagedata o:title="3" src="file:///C:\DOCUME~1\azhang\LOCALS~1\Temp\msohtml1\03\clip_image006.jpg"></v:imagedata></v:shape>

<o:p> </o:p>

该测试用例用一个有效的发货信息调用了PendingOrder.updateDeliveryInfo()方法并且检验它是否更新了PendingOrder,返回值是否为真。

<o:p> </o:p>

实现方法

<o:p> </o:p>

       因为PendingOrder已经定义了一个桩方法,那这个测试编译的时候就不会有问题。但是为了使之运行通过,我们需要用一个真实的实现来代替这个桩,从而可以使之可以检验发货信息和更新PendingOrder

<o:p> </o:p>

       PendingOrder首先用Calendar类来检查发货信息中的发货时间是否在一小时以后。然后查询数据库来检验发货信息。最简单的方法是在RestaurantRepository里封装这个查询并且定义isRestaurantAvailable()方法。updateDeliveryInfo()方法调用isRestaurantAvailable,如果其返回值为真就把发货信息存储到数据库。

<o:p> </o:p>

<v:shape id="_x0000_i1029" style="WIDTH: 6in; HEIGHT: 410.25pt" type="#_x0000_t75"><v:imagedata o:title="3" src="file:///C:\DOCUME~1\azhang\LOCALS~1\Temp\msohtml1\03\clip_image007.jpg"></v:imagedata></v:shape>

一个我们没有解决的很重要的设计上的问题是,PendingOrder如何访问RestaurantRepository。让我们来看看如何解决这个问题。

<o:p> </o:p>

选择如何访问一个repository

<o:p> </o:p>

       Repositories主要是被领域中的service类来使用,但是他们也被一些实体调用,比如说entities。为调用一个repository对象的方法,调用者必须显示的拥有整个对象的引用。之前你看到如何把repository作为一个参数传递给PlaceOrderService的。然而,在领域模型的entity中有时候这却是行不通的,且听我娓娓道来。让我们把问题揭示出来然后再给大家讲讲几种解决方案。

<o:p> </o:p>

       最方便的方法是把repository作为参数传递给entities,就像传递给service一样。这样可以使用轻量级容器的构造方法注入机制来初始化实体。把repository作为参数传递给构造方法比传递给一般的方法要简单得多而且也没有使用单例模式时的缺点了,我将在后面一点讲解单例模式。然而使用这种方法来初始化entity不是易懂的,因为它不同于serviceservice由轻量级容器来负责初始化,而entity则由持久化框架在从数据库里读取数据的后创建。

<o:p> </o:p>

       默认情况下,持久化框架是直接使用类的默认构造方法来创建对象的,所以不可能把任何需要的对象传递进它(将被创建的entity)构造方法。一些(不是所有的)持久框架有配置对象初始化的机制,这样可以允许程序来控制如何初始化entity。程序可以用具有依赖注入的轻量级框架来配置持久化框架以便于创建对象。比如说使用hibernate 的构造器注入。然而,因为该方法并不是一种普遍的方法,所以在本书中我不会使用它。

<o:p> </o:p>

       另一个选择是用静态方法和变量来实现repository。举个例子,你可以用ThreadLocal模式或者单例模式来实现一个repository。这种方法可以使用在任何的持久化框架中,而且不需要把repository传来传去,但是有时会把代码弄得太复杂。使用静态方法和变量的问题是它们会把代码弄得难以测试。比如说,它们阻止你使用一个可变的实现如一个mock对象,因为你不能改变程序对一个静态方法的调用,也不能改变它来访问另一个类的静态变量。它们也引入了隐藏的依赖关系,因为代码取决于那些必须被初始化的静态变量。因此,最好避免使用静态方法和静态变量。

<o:p> </o:p>

       知道了只有一些持久化框架允许你使用构造方法注入来初始化entity并且使用静态方法和变量有很多严重的缺点,那么把repository作为参数传递给方法就有意义了。它避免了使用单例模式的缺点也不设计到所有的持久化框架的特点。然而,采用这种方法的一个缺点是,它在代码中有一个一石激起千层浪的效果(ripple effect)。我们也许不得不改变很多方法来把repository作为一个参数传递给该方法使用,这个repositoryservice传递过来,service是通过构造器注入来拥有了repository的引用的。

<o:p> </o:p>

       在这个例子中,把RestaurantRepository传递给PendingOrder只需要作很小的改动。我们只需要改动PlaceOrderService来把RestaurantRepository作为一个参数传递给PendingOrderupdateDeliveryInfo()方法,而RestaurantRepository应该是PlaceOrderServie的构造方法的一个参数。

3.6显示了PendingOrder.updateDeliveryInfo()方法。

<v:shape id="_x0000_i1030" style="WIDTH: 450pt; HEIGHT: 372pt" type="#_x0000_t75"><v:imagedata o:title="3" src="file:///C:\DOCUME~1\azhang\LOCALS~1\Temp\msohtml1\03\clip_image009.jpg"></v:imagedata></v:shape>

<o:p> </o:p>

这个方法调用了RestaurantRepository.isRestaurantAvailable()方法,如果返回true,那么就把发货信息更新到PendingOrder

<o:p> </o:p>

       为了使这个类通过编译,我们需要定义isRestaurantAvailable()方法;

<o:p> </o:p>

       public  interface   RestaurantRepository{

            boolean   isRestaurantAvailable (Address   deliveryAddress,

                                                 Date   deliveryTime)

       }

<o:p> </o:p>

如果至少有一家餐馆提供了发货信息中指定的服务则返回true。我们也不得不改变PlaceOrderService来把RestaurantRepository传递给其构造方法,并且也要把RestaurantRepository传递给PendingOrder   ,同时要用模拟的(mockRestaurantRepository来改变PendingOrder的测试类。

分享到:
评论

相关推荐

    spring in action Thrid 第二章重点内容

    综上所述,《Spring in Action 第三版》第二章的核心内容是展示了Spring如何通过轻量级设计、依赖注入、声明式编程和减少冗余代码的策略,来实现Java开发的简化和优化。这些概念和实践贯穿整个Spring框架,为开发者...

    spring in action Thrid 第一章重点内容

    总的来说,Spring in Action第三版第一章的重点在于介绍Spring如何通过上述策略简化Java开发,提升代码质量,使开发者能更专注于业务逻辑,而不是底层的基础设施。依赖注入是实现这些目标的核心工具,它使得松耦合...

    Spring.in.Action

    ### Spring in Action 第二版 — 关键知识点概览 #### 一、Spring框架简介与核心概念 - **Spring 框架概述**:Spring 是一个开源的轻量级 Java 应用程序框架,旨在简化企业级应用开发。它提供了一个全面的编程模型...

    struts2框架ppt

    **第3章:Action与结果** 在这一章中,你会了解到Action类是业务逻辑的载体,它是控制器层的主要组成部分。同时,还会讲解Result类型,如dispatcher、stream等,以及如何根据不同的逻辑跳转到不同的页面。 **第4章...

    struts2教材课件

    第三部分可能深入讲解Struts2的拦截器。拦截器是Struts2框架的一大特色,它们可以按照预定义的顺序在Action执行前和执行后执行,实现诸如日志记录、事务管理、权限控制等功能。通过自定义拦截器,你可以进一步扩展...

    Struts API官方英文版

    10. **Ajax支持**:虽然Struts1本身并不直接支持Ajax,但可以通过Struts-Plugins或其他第三方库(如DWR或Prototype/Scriptaculous)集成Ajax功能,实现页面的异步更新。 总之,Struts API官方文档对于理解和使用...

    JavaEE ssm 十七十八源码

    学习这部分内容,你需要理解MVC架构、掌握Spring的核心特性,如IoC和AOP,熟悉Struts的配置和Action机制,以及MyBatis的SQL映射和结果映射。同时,对JavaEE的Servlet和JSP有基本认识,能熟练使用Eclipse这样的IDE...

    Mybatis3+Spring4 +Struts2整合源码

    Mybatis3是轻量级的持久层框架,Spring4则是一个全面的企业级应用框架,而Struts2则作为MVC架构的一部分,负责处理前端请求和后端业务逻辑的交互。 【描述】提到这个源码是共享学习的资源,意味着它包含了完整的...

    Struts2+MyBatis+Spring3.2的一个完整整合例子+详解

    短信服务通常通过第三方API实现,Spring的RestTemplate或者Feign客户端可以用来调用这些API,而Spring的配置和事务管理同样适用于短信服务的错误处理和事务一致性。 **TelePurdoWebQuery** 这个文件名可能是项目中...

    springmybatis

    mybatis实战教程mybatis in action之三实现数据的增删改查 mybatis实战教程mybatis in action之四实现关联数据的查询 mybatis实战教程mybatis in action之五与spring3集成附源码 mybatis实战教程mybatis in action之...

    struts2+spring3+mybatis框架最全jar包

    同时,Spring3集成了大量的第三方库,如MyBatis,使得整合各种技术变得更加容易。 **MyBatis** 是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数...

    J2EE开发常用框架.pdf

    - **模块化**:Spring包含多个子框架,如数据访问/集成、Web、AOP(面向切面编程)、上下文和测试等,这些子框架可以独立使用,也可以与其他第三方框架集成。 - **非侵入式设计**:Spring的应用程序可以尽量减少对...

    动力节点超完整详细的ssh和ssm讲义

    - 无缝集成各种第三方库。 - **使用场景**:几乎适用于所有的Java应用开发场景。 #### 3. Hibernate框架解析 Hibernate是一个开放源码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java开发者...

    Struts_2完全学习手册

    - **低耦合**:各组成部分之间相互独立,可以在不影响其他部分的情况下修改其中一部分。 - **高重用性**:模型可以被多个视图使用,提高了代码的复用性和维护性。 - **可扩展性**:易于添加新的视图或修改现有视图。...

    webwork学习资料

    在Web开发中,MVC模式是一种常用的设计模式,它将应用程序分为三个主要部分:模型(Model)、视图(View)和控制器(Controller)。WebWork框架通过这种方式,使得开发者可以更好地组织和管理代码,提高开发效率和可...

    【Java web实战员工管理系统】第十三讲.zip

    在本课程"【Java web实战员工管理系统】第十三讲"中,我们将深入探讨如何使用Java Web技术构建一个功能完备的员工管理系统。这个系统的核心目标是实现对企业内部员工信息的高效管理和便捷操作。Java Web作为企业级...

    Flex+JAVA+BlazeDS开发环境配置(Java工程和Flex工程独立)

    可以从官方网站或第三方网站下载。 2. 解压并部署 BlazeDS。将 `blazeds.war` 文件解压到 Tomcat 的 `webapps` 目录下。 3. 在 MyEclipse 中配置 Tomcat 服务器,指定 Tomcat 的安装路径,并启用服务器。 4. 创建...

    MiPrimerAppWebStruts:http

    1. **Action类**:这是控制器的部分,负责处理来自用户的请求,并调用模型进行业务处理。 2. **配置文件**:主要包括struts.xml,用于定义Action类、Action映射以及结果页面等。 3. **视图**:通常由JSP页面构成,...

Global site tag (gtag.js) - Google Analytics