SERVICE层编码规范.doc_第1页
SERVICE层编码规范.doc_第2页
SERVICE层编码规范.doc_第3页
SERVICE层编码规范.doc_第4页
SERVICE层编码规范.doc_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

SERVICE层编码规范目录1.设计思想11.1.敏捷设计概念11.2.单一职责原则(SRP)21.3.开放-封闭原则(OCP)21.3.1.关键是抽象21.4.里氏代换原则(LSP)31.5.依赖倒置原则(DIP)31.5.1.层次化41.5.2.使用spring来应用该原则41.6.接口隔离原则(ISP)62.Spring框架的使用72.1.容器72.1.1.配置元数据72.1.2.BEAN的命名规范82.1.3.实例化bean82.1.4.配置文件bean属性详解92.1.5.Bean定义的继承102.2.AOP编程112.2.1.概念112.2.2.Spring AOP的支持方式112.2.3.Spring AOP使用例子112.2.4.切入点类型支持121. 设计思想SERVICE层整个WEB系统中负责业务逻辑处理的一块,是最需要抽象的部分,这一层设计的核心就是复用性,和低耦合性,结合spring提供的依赖注入方式,让业务逻辑的处理的代码调用更方便1.1. 敏捷设计概念敏捷设计是一个过程,不是一个事件,它是一个持续的应用原则,模式以及实践来改进软件的结构和可读性的过程。敏捷开发人员应该做到的:(1)他们遵循敏捷实践去发现问题;(2)他们应用设计原则去诊断问题;(3)他们应用适当的设计模式去解决问题对于我们的营销系统,改进目前框架复杂,高耦合,低移植性的最佳方法就是抽象,在应用抽象的思想时,我们需要尽量的遵循以下几种设计原则:1.2. 单一职责原则(SRP)就一个类而言,应该仅有一个引起它变化的原因。职责简单一些,复用更加轻松。手机虽然可以拍照,但是效果很差。如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其它职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,那就需要将类的职责分离。 在SERVICE的设计时,一个接口或者类一定要仅满足一个抽象的业务需求,如果有多于一个的时候,就需要把类拆分开来1.3. 开放-封闭原则(OCP)本原则是说:软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个版本以后不断推出新的版本。无论模块是多么的“封闭”,都会存在一些无法对之封闭的变化。既然不可能完全封闭,设计人员必须对于他设计的模块应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化。在我们最初编写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离以后发生的同类变化。面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码这就是开放封闭原则的精神所在。1.3.1. 关键是抽象例如我们的划分功能,可能有多种划分方式,而根据不同的客户类型,可能会使用不同的划分方式,这时候我们就应该把划分方式抽象为一个接口,或者抽象类,这样我们使用时只通过接口来调用,而不关心具体实现,如果有新的划分方式我们只需要增加一个实现,而不需要再改变接口了注意:以后如果有这种业务的不同方式,都应该使用抽象来调用,比如营销单的派单方式,维系挽留的不同预警类型,都应该使用抽象来设计1.4. 里氏代换原则(LSP)白话:一个软件实体如果使用的是父类的话,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别ASD。也就是说,在软件里面,把父类都替换成它的子类,程序的行为没有变化。简单地说,子类型必须能够替换掉它们的父类型。只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。参见下面的代码:动物* animal = new 猫(); animal-吃(); animal-喝(); animal-叫();如果需求发生变化,需要将“猫”更换成别的动物,只需要更改第一句即可,其它地方无需改变。这就是“面向接口编程”的好处。依赖倒置,其实就是谁也不要依靠谁:除了约定的接口,大家都可以灵活自如。由于有了里氏代换原则,才使得开放-封闭成为了可能。正是由于子类型的课题唤醒才使得使用父类类型的模块在无需修改的情况下就可以扩展7。依赖倒置,其实可以说是面向对象设计的标志,用哪种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之就是过程化的设计。1.5. 依赖倒置原则(DIP)解释:抽象不应该依赖细节,细节应该依赖抽象。白话一点:针对接口编程,不要对实现编程。面向过程开发的问题:为了使得常用代码可以复用,通常将常用代码写成函数库。这就是“高层模块依赖低层模块”。然而在做新项目时,发现业务逻辑的高层模块都是一样的,但客户却希望使用不同的数据库或存储信息方式,导致我们无法复用高层模块(因为它们和底层函数库绑在一起了)。1.5.1. 层次化所有结构良好的面向对象架构都具有清晰的层次定义,每个层次通过一个定义良好的,受控的接口向外提供一组内聚的服务。我们的架构在层次化的基础上,必须要每个较高层次都为他所需要的服务声明一个抽象接口,较低的层次实现了这些抽象接口,每个高层类都通过抽象接口使用下一层,这样高层就不依赖底层。低层反而依赖高层中声明的抽象服务接口。如下图的结构:1.5.2. 使用spring来应用该原则因为有了spring的IoC注入方式,我们在调用一个类时可以不用知道具体使用了哪个实现,而是直接使用接口来调用它的方法,如新的数据库访问工具mon.db中QryFactory是一个查询构造工厂的接口,Query是查询接口,我们在访问这些方法时,通过spring中context文件的配置,就可以让具体代码在不知道具体实现类是哪个的情况下来调用了public static void setQryFactory(QryFactory factory) qryFactory = factory;/* * 所有查询 */ SuppressWarnings(all)public static List qry() throws Exception String sql = select * from cust; Map qryMap = new HashMap(); / 有参数 List list = qryFactory.getQuery(sql,qryMap).list();这个时候如果我们在配置文件中把QryFactory的实现从OracleQryFactory替换为其他数据库实现,如Db2QryFactory,对程序都不会有任何影响,就实现了高层不依赖低层1.6. 接口隔离原则(ISP)不应该强迫客户依赖他们不用的方法。如果强迫存户程序依赖于那些它们不使用的方法,那么这些客户程序就面临着由于这些未使用方法的改变所带来的变更。这无意中导致了所有客户程序之间的耦合换种说法如果一个客户程序依赖于一个含有它不使用的方法的类,但是其他客户程序却要使用该方法,那么当其他客户要求这个类改变时,就会影响到这个客户程序。我们希望尽可能地避免这种耦合,因此我们希望分离接口比如上图的设计,Door类继承了Timer Client接口,所以Timed Door类也继承了Timer Client接口,来实现超时控制,但是这时就给Door类增加了一个不必要的继承,不是所有的门都需要超时功能的,因此这违反了接口隔离原则,接口被污染了,我们应该用以下设计方法:建立一个适配器类来继承Timer Client类,因为JAVA不能支持多重继承,因此通过adapter模式来解决这个问题很合适2. Spring框架的使用Spring丰富的功能都给予控制反转(IoC)原理来实现,通过XML文件配置来实例化我们的业务逻辑类,在构建J2EE工程时,我们使用AppliactionContext接口来使用spring的丰富特性2.1. 容器在spring中beanFactory是IoC容器的核心接口。它的职责包括:实例化,定位,配置应用程序中的对象及建立这些对象间的依赖。要使用SPRING框架,首先要使用统一的配置方法,spring支持3种格式的配置文件:JAVA格式配置文件,XML和SPRING公共API实现。目前我们使用XML文件的配置方法2.1.1. 配置元数据为了使用spring2.0的新特性,我们需要把spring1.2的配置格式升级到2.0的配置格式,XML文件头定义如下 - 2.1.2. BEAN的命名规范l 我们的bean命名应该依从相关的service类或者DAO类,但是把首字母大写变为小写,如”CustManager”类在spring中的命名应该为”custManager”l 如果要为bean定义别名的话,可以使用alias属性,如,增加别名以后,也可以使用”toName”来访问2.1.3. 实例化bean通过spring可以实例化普通的JAVA类,并没有特殊的要求,spring可以自动通过无参构造函数来新建类的实例。只需要指定class参数,如下例子:customNew如果是通过工厂模式创建的类,使用类似createInstance()方法创建实例时,也可以通过spring的配置来依赖注入,如下例子,使用静态工厂方法实例化使用实例工厂方法实例化实例化的方式可以有2种。l 使用set方式传入参数来实例化,Spring开发团队提倡使用setter注入。而且setter DI在以后的某个时候还可将实例重新配置(或重新注入)!- setter injection using the nested element - l 使用构造函数传入参数实例化,这种方式一次性将所有依赖注入的做法意味着,在未完全初始化的状态下,此对象不会返回给客户代码(或被调用),此外对象也不可能再次被重新配置(或重新注入)。 !- constructor injection using the nested element - 2.1.4. 配置文件bean属性详解Bean中的属性可以有以下几种形式:l 直接量,基本类型,String类型等,元素通过字符串来指定属性或构造器参数的值。JavaBean PropertyEditor将用于把字符串从java.lang.String类型转化为实际的属性或参数类型。customNewl 引用其他的bean: 在或元素内部还可以使用ref元素。该元素用来将bean中指定属性的值设置为对容器中的另外一个bean的引用。如前所述,该引用bean将被作为依赖注入,而且在注入之前会被初始化(如果是singleton bean则已被容器初始化)。尽管都是对另外一个对象的引用,但是通过id/name指向另外一个对象却有三种不同的形式,不同的形式将决定如何处理作用域及验证。l 内部bean: 所谓的内部bean(inner bean)是指在一个bean的或 元素中使用元素定义的bean。内部bean定义不需要有id或name属性,即使指定id 或 name属性值也将会被容器忽略 l 集合:通过、及元素可以定义和设置与Java Collection类型对应List、Set、Map及Properties的值。目前这种方式使用较少2.1.5. Bean定义的继承在bean定义中包含了大量的配置信息,其中包括容器相关的信息(比如初始化方法、静态工厂方法名等等)以及构造器参数和属性值。子bean定义就是从父bean定义继承配置数据的bean定义。它可以覆盖父bean的一些值,或者添加一些它需要的值。使用父/子bean定义的形式可以节省很多的输入工作。实际上,这就是一种模板形式。我们的SERVICE层需要尽量的抽象和复用,所以配置信息可能有相同的部分,通过这种继承的方式我们可以减少配置的代码量,如下例子: 2.1.6. 在类中取得已有的context有两种方式取得1. 实现ApplicationContextAware接口import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;public class MyApplicationContextUtil implements ApplicationContextAware private static ApplicationContext context;/声明一个静态变量保存Overridepublic void setApplicationContext(ApplicationContext contex)throws BeansException this.context=contex;public static ApplicationContext getContext()return context;2在ServletContext容器中,使用如下方式取得/* * 取得WebApplicationContext * * param request * return */ public static WebApplicationContext getWebApplicationContext(ServletContext sc)WebApplicationContext ctx = (WebApplicationContext) sc.getAttribute(ContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX);if (ctx = null)ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);if (ctx = null)throw new RuntimeException(WebApplicationContext error . Cant find WebApplicationContext in any scope !);return ctx;2.2. AOP编程2.2.1. 概念面向切面编程:Aspect Oriented ProgrammingAOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程。可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。AOP代理的图示2.2.2. Spring AOP的支持方式l AspectJ风格:AspectJ使用了Java 5的注解,可以将切面声明为普通的Java类。 AspectJ 5发布的 AspectJ project 中引入了这种AspectJ风格。 Spring 2.0 使用了和AspectJ 5一样的注解,使用了AspectJ 提供的一个库来做切点(pointcut)解析和匹配。 但是,AOP在运行时仍旧是纯的Spring AOP,并不依赖于AspectJ 的编译器或者织入器(weaver)。需要引入两个包来支持:aspectjweaver.jar和aspectjrt.jarl Schema-based AOP 风格:使用XML来配置,和普通的context文件配置相似,但是对于aspect的实现有很多限制,因此在我们的系统中将使用Aspectj风格来配置2.2.3. Spring AOP使用例子(AspectJ方式)在启用AspectJ支持的情况下,在application context中定义的任意带有一个Aspect切面(拥有Aspect注解)的bean都将被Spring自动识别并用于配置在Spring AOP。 以下例子展示了为了完成一个不是非常有用的切面所需要的最小定义:1. 声明一个切面,首先定义指向切面类的bean 2. 下面是这个类要引入的切面,该例子会在每次调用我们的qryCenter之前返回调用的第一个参数内容:package mon.aop;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.ProceedingJoinPoint;Aspectpublic class MyAopTest Around(execution(* mon.qry.QryCenter.executeQuery(.)public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable / start stopwatchSystem.out.print(qryCenter args:+pjp.getArgs()0.toString();Object retVal = ceed();/ stop stopwatchreturn retVal; 2.2.4. Spring AOP使用例子(context配置方式)2.2.5. 切入点类型支持Spring AOP 支持在切入点表达式中使用如下的AspectJ切入点指定者:execution - 匹配方法执行的连接点,这是你将会用到的Spring的最主要的切入点指定者。 within - 限定匹配特定类型的连接点(在使用Spring AOP的时候,在匹配的类型中定义的方法的执行)。 this - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中bean reference(Spring AOP 代理)是指定类型的实例。 target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中目标对象(被代理的appolication object)是指定类型的实例。 args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中参数是指定类型的实例。 target - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中执行的对象的类已经有指定类型的注解。 args - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中实际传入参数的运行时类型有指定类型的注解。 within - 限定匹配特定的连接点,其中连接点所在类型已指定注解(在使用Spring AOP的时候,所执行的方法所在类型已指定注解)。 annotation - 限定匹配特定的连接点(使用Spring AOP的时候方法的执行),其中连接点的主题有某种给定的注解。2.3. 测试采用依赖注射的一个主要好处是你的代码对容器的依赖将比传统J2EE开发小的多。无需Spring或任何其他容器,只要简单地通过 new 操作符即可实例化对象,通过这种方式组成你应用的POJO对象就可以充分利用JUnit进行测试了。你可以使用模拟对象或者其他很多有价值的测试技术将你的代码隔离起来进行测试。如果你的应用在架构上遵循了Spring的建议,那么你的代码将会有清晰的层次和高度的模块化,这些都将大大方便单元测试。例如,在单元测试中你可以通过测试框架或者模拟DAO接口的方式来测试服务层对象而无需访问持久化数据。真正的单元测试运行起来通常都非常迅速,因为没有应用服务器,数据库,ORM工具等运行设施需要设置。因此加强正确的单元测试可以大大提高你的生产力。2.3.1. 使用spring工具简化测试要使用spring提供的测试工具,需要引入包spring-mock.jar.使用它提供的一个超类AbstractDependencyInjectionSpringContextTests。包 org.springframework.test 为使用Srping容器进行集成测试提供了有价值的超类,而且同时不用依赖于任何应用服务器或者其他部署环境。这些测试可以在Junit中甚至是某个IDE中运行而不需要特别的部署步骤。他们通常比单元测试要慢,但是比Cactus测试或者需要部署到一个应用服务器上的远程测试要快。这个包里的各种抽象类提供了如下的功能: 各测试案例执行期间的Spring IoC容器缓存。 测试fixture自身的依赖注入。 适合集成测试的事务管理。 继承而来的对测试有用的各种实例变量。AbstractDependencyInjectionSpringContextTests 有一个子类必须实现的 abstract protected 方法来提供contexts的位置protected abstract String getConfigLocations();在提供了contexts文件位置后,它将自动装载需要的SERVICE,只需要提供set方法,下面看CM模块中测试的一个类的例子:package ims.cm.util;import ims.cm.model.Offer;import ims.cm.service.CampDefManager;import java.io.File;import java.io.FilenameFilter;import java.util.ArrayList;import java.util.List;import org.springframework.test.AbstractDependencyInjectionSpringContextTests;public class SaleCampManagerTest extends AbstractDependencyInjectionSpringContextTestspublic SaleCampManagerTest()super(); /缺省是AUTOWIRE_BY_TYPE,当Bean文件有2个以上的同类型 /Bean定义时就应该采用AUTOWIRE_BY_NAME方式。 setAutowireMode(AUTOWIRE_BY_NAME);private CampDefManager campDefManager;public void setCampDefineManagerTarget(CampDefManager campDefManager)this.campDefManager=campDefManager;public void testSaveCamp() throws ExceptionOffer offer=campDefManager.getOffer(new Long(20288);this.assertEquals(经典1_80元保底;超80元5折_农村住宅_免呼限, offer.getOfferName();protected String getConfigLocations()return new Stringcontext/applicationContext.xml,context/context-dataSource.xml,context/context-cm.xml,context/context-attachment.xml;注意:spring默认支持的是接口注入,如果在测试时要注入实现类,会报错2.4. WEB服务的应用2.5. Spring中的定时调度2.6. 事务处理Spring框架引人注目的重要因素之一是它全面的事务支持。Spring框架提供了一致的事务管理抽象,在我们的框架中主要用到的就是它的数据库事务处理,有两种情况:l 查询框架中的DML语句执行的数据操作事务l Hibernate中的事务处理Spring支持两种事务处理的实现方式:l 声明式事务管理大多数Spring用户选择声明式事务管理。这是对应用代码影响最小的选择,因此也最符合 非侵入式 轻量级容器的理念。Spring的声明式事务管理是通过Spring AOP实现的,因为事务方面的代码与Spring绑定并以一种样板式风格使用,简单的说就是我们以前在context中配置的方式,只是 spring2.0的配置方式有一些变化,为了实现纯POJO,低侵入性的事务处理,强烈建议使用声明式的事务处理l 编程式事务管理直接在代码中调用事务处理类的方法,除非很有必要否则不建议使用2.6.1. 使用CONTEXT配置声明式事务管理先来看一下目前系统中使用的spring1.2的事务配置方式: PROPAGATION_REQUIRED,-CustMagException 其他事务处理声明.PROPAGATION_REQUIRED,-Exception 这种方式有一点不好的,就是需要在实现类中定义一个对象PlatformTransactionManager,这增加了SERVICE类的侵入性,另外对于事务处理,除了设置回滚外不能做其他的操作,而spring2.0的事务处理是基于AOP的,在我们了解AOP处理方式以后,我们除了在配置回滚外,还能实现一些其他的处理方式,比如增加一个拦截器打印出错的log信息等。下面看一下spring2.0中的context配置方式: !- the transactional advice (i.e. what happens; see the bean below) - 这个配置分为3部分:l campTrasactionManager是我定义的事务测试接口,第一部分是这个类的bean配置,和以前一样l txAdvice是事务处理处理方式的配置,注意它引用了事务处理bean,是在我们applicationContext中配置的transactionManager,txAdvice中可以配置如下几种情况:Table9.1. 有关的设置属性是否需要?默认值描述name是与事务属性关联的方法名。通配符(*)可以用来指定一批关联到相同的事务属性的方法。 如:get*、handle*、on*Event等等。 propagation不REQUIRED事务传播行为isolation不DEFAULT事务隔离级别timeout不-1事务超时的时间(以秒为单位)read-only不false事务是否只读?rollback-for不将被触发进行回滚的 Exception(s);以逗号分开。 如:com.foo.MyBusinessException,ServletException no-rollback-for不不 被触发进行回滚的 Exception(s);以逗号分开。 如:com.foo.MyBusinessException,ServletException l fooServiceOperation,这里是通过spring AOP把事务处理织入我们的测试类的地方。首先定义了一个pointcut指向我们的测试类,pointcut配置方式请参见2.2AOP编程,然后通过标签引用,这个标签内,我们除了引用基本的事务处理外,还可以加入其他的AOP类来做除了提交,回滚外的其他处理下面再看一下CampTrasactionManager接口的实现类,这是一个普通的SERVICE处理类:package ims.cm.service.impl;import ims.cm.service.CampTrasactionManager;import mon.qry.QryCenter;import mon.qry.QryException;import java.util.HashMap;import mons.logging.Log;import mons.logging.LogFactory;/* * rem:测试qryCenter的事务处理机制 * aut:刘先 * ver:1.0 * log:2010-1-19 */public class CampTrasactionManagerImpl implements CampTrasactionManagerprotected final Log log = LogFactory.getLog(getClass();private QryCenter qryCenter;public void setQryCenter(QryCenter qryCenter)this.qryCenter=qryCenter;public void insertCustSort() throws QryExceptionString sql1=insert into cust_sort(area_id,cu

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论