研磨设计模式之装饰模式-4(转)

3.3  装饰模式和AOP

        装饰模式和AOP在思想上有共同之处。可能有些朋友还不太了解AOP,下面先简单介绍一下AOP的基础知识。
1:什么是AOP——面向方面编程
        AOP是一种编程范式,提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。
        在面向对象开发中,考虑系统的角度通常是纵向的,比如我们经常画出的如下的系统架构图,默认都是从上到下,上层依赖于下层,如图5所示:

                                          图5  系统架构图示例图
        而在每个模块内部呢?就拿大家都熟悉的三层架构来说,也是从上到下来考虑的,通常是表现层调用逻辑层,逻辑层调用数据层,如图6所示:


图6  三层架构示意图

        慢慢的,越来越多的人发现,在各个模块之中,存在一些共性的功能,比如日志管理、事务管理等等,如图7所示:
 

                                         图7  共性功能示意图

        这个时候,在思考这些共性功能的时候,是从横向在思考问题,与通常面向对象的纵向思考角度不同,很明显,需要有新的解决方案,这个时候AOP站出来了。
        AOP为开发者提供了一种描述横切关注点的机制,并能够自动将横切关注点织入到面向对象的软件系统中,从而实现了横切关注点的模块化。
        AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任,例如事务处理、日志管理、权限控制等,封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
        AOP之所以强大,就是因为它能够自动把横切关注点的功能模块,自动织入回到软件系统中,这是什么意思呢?
        先看看没有AOP,在常规的面向对象系统中,对这种共性的功能如何处理,大都是把这些功能提炼出来,然后在需要用到的地方进行调用,只画调用通用日志的公共模块,其它的类似,就不去画了,如图8所示:


                    图8  调用公共功能示意图

        看清楚,是从应用模块中主动去调用公共模块,也就是应用模块要很清楚公共模块的功能,还有具体的调用方法才行,应用模块是依赖于公共模块的,是耦合的,这样一来,要想修改公共模块就会很困难了,牵一而发百。
        看看有了AOP会怎样,还是画个图来说明,如图9所示:


                 图9  AOP的调用示意图

        乍一看,跟上面不用AOP没有什么区别嘛,真的吗?看得仔细点,有一个非常非常大的改变,就是所有的箭头方向反过来了,原来是应用系统主动去调用各个公共模块的,现在变成了各个公共模块主动织入回到应用系统。
        不要小看这一点变化,这样一来应用系统就不需要知道公共功能模块,也就是应用系统和公共功能解耦了。公共功能会在合适的时候,由外部织入回到应用系统中,至于谁来实现这样的功能,以及如何实现不再我们的讨论之列,我们更关注这个思想。
        如果按照装饰模式来对比上述过程,业务功能对象就可以被看作是被装饰的对象,而各个公共的模块就好比是装饰器,可以透明的来给业务功能对象增加功能。
        所以从某个侧面来说,装饰模式和AOP要实现的功能是类似的,只不过AOP的实现方法不同,会更加灵活,更加可配置;另外AOP一个更重要的变化是思想上的变化——“主从换位”,让原本主动调用的功能模块变成了被动等待,甚至毫不知情的情况下被织入了很多新的功能。


2:使用装饰模式做出类似AOP的效果
        下面来演示一下使用装饰模式,把一些公共的功能,比如权限控制,日志记录,透明的添加回到业务功能模块中去,做出类似AOP的效果。
(1)首先定义业务接口
        这个接口相当于装饰模式的Component。注意这里使用的是接口,而不像前面一样使用的是抽象类,虽然使用抽象类的方式来定义组件是装饰模式的标准实现方式,但是如果不需要为子类提供公共的功能的话,也是可以实现成接口的,这点要先说明一下,免得有些朋友会认为这就不是装饰模式了,示例代码如下:

Java代码 
  1. /** 
  2. * 商品销售管理的业务接口 
  3. */  
  4. public interface GoodsSaleEbi {  
  5.     /** 
  6.      * 保存销售信息,本来销售数据应该是多条,太麻烦了,为了演示,简单点 
  7.      * @param user 操作人员 
  8.      * @param customer 客户 
  9.      * @param saleModel 销售数据 
  10.      * @return 是否保存成功 
  11.      */  
  12.     public boolean sale(String user,String customer,  
  13. SaleModel saleModel);  
  14. }  

 

顺便把封装业务数据的对象也定义出来,很简单,示例代码如下:

Java代码 
  1. /** 
  2. * 封装销售单的数据,简单的示意一些 
  3. */  
  4. public class SaleModel {  
  5.     /** 
  6.     * 销售的商品 
  7.     */  
  8.     private String goods;  
  9.     /** 
  10.     * 销售的数量 
  11.     */  
  12.     private int saleNum;  
  13.     public String getGoods() {    
  14.         return goods;     
  15.     }  
  16.     public void setGoods(String goods) {  
  17.         this.goods = goods;   
  18.     }  
  19.     public int getSaleNum() {  
  20.         return saleNum;  
  21.     }  
  22.     public void setSaleNum(int saleNum) {  
  23.         this.saleNum = saleNum;  
  24.     }  
  25.     public String toString(){  
  26.         return "商品名称="+goods+",购买数量="+saleNum;  
  27.     }  
  28. }  

 (2)定义基本的业务实现对象,示例代码如下:

Java代码 
  1. public class GoodsSaleEbo implements GoodsSaleEbi{  
  2.     public boolean sale(String user,String customer,   
  3.                       SaleModel saleModel) {  
  4.         System.out.println(user+"保存了"  
  5.                                  +customer+"购买 "+saleModel+" 的销售数据");  
  6.         return true;  
  7.     }  
  8. }  

 

(3)接下来该来实现公共功能了,把这些公共功能实现成为装饰器,那么需要给它们定义一个抽象的父类,示例如下:

Java代码 
  1. /** 
  2. * 装饰器的接口,需要跟被装饰的对象实现同样的接口 
  3. */  
  4. public abstract class Decorator implements GoodsSaleEbi{  
  5.     /** 
  6.                * 持有被装饰的组件对象 
  7.            */  
  8.     protected GoodsSaleEbi ebi;  
  9.     /** 
  10.      * 通过构造方法传入被装饰的对象 
  11.      * @param ebi被装饰的对象 
  12.      */  
  13.     public Decorator(GoodsSaleEbi ebi){  
  14.         this.ebi = ebi;  
  15.     }  
  16. }  

 (4)实现权限控制的装饰器
先检查是否有运行的权限,如果有就继续调用,如果没有,就不递归调用了,而是输出没有权限的提示,示例代码如下:

Java代码 
  1. /** 
  2.  * 实现权限控制 
  3.  */  
  4. public class CheckDecorator extends Decorator{  
  5.     public CheckDecorator(GoodsSaleEbi ebi){  
  6.         super(ebi);  
  7.     }  
  8.     public boolean sale(String user,String customer  
  9.         , SaleModel saleModel) {  
  10.         //简单点,只让张三执行这个功能  
  11.         if(!"张三".equals(user)){  
  12.             System.out.println("对不起"+user  
  13.                 +",你没有保存销售单的权限");  
  14.             //就不再调用被装饰对象的功能了  
  15.             return false;  
  16.         }else{  
  17.             return this.ebi.sale(user,customer,saleModel);  
  18.         }         
  19.     }  
  20. }  

 (5)实现日志记录的装饰器,就是在功能执行完成后记录日志即可,示例代码如下:

Java代码 
  1. /** 
  2. * 实现日志记录 
  3. */  
  4. public class LogDecorator extends Decorator{  
  5.     public LogDecorator(GoodsSaleEbi ebi){  
  6.         super(ebi);  
  7.     }  
  8.     public boolean sale(String user,String customer,   
  9.         SaleModel saleModel) {  
  10.         //执行业务功能  
  11.         boolean f = this.ebi.sale(user, customer, saleModel);  
  12.   
  13.         //在执行业务功能过后,记录日志  
  14.         DateFormat df =   
  15.             new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");  
  16.         System.out.println("日志记录:"+user+"于"+  
  17.                 df.format(new Date())+"时保存了一条销售记录,客户是"  
  18.                 +customer+",购买记录是"+saleModel);  
  19.         return f;  
  20.     }  
  21. }  

 (6)组合使用这些装饰器
        在组合的时候,权限控制应该是最先被执行的,所以把它组合在最外面,日志记录的装饰器会先调用原始的业务对象,所以把日志记录的装饰器组合在中间。
        前面讲过,装饰器之间最好不要有顺序限制,但是在实际应用中,要根据具体的功能要求来,有需要的时候,也可以有顺序的限制,但应该尽量避免这种情况。
        此时客户端测试代码示例如下:

Java代码 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值