研磨设计模式之装饰模式-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。注意这里使用的是接口,而不像前面一样使用的是抽象类,虽然使用抽象类的方式来定义组件是装饰模式的标准实现方式,但是如果不需要为子类提供公共的功能的话,也是可以实现成接口的,这点要先说明一下,免得有些朋友会认为这就不是装饰模式了,示例代码如下:
?
(3)接下来该来实现公共功能了,把这些公共功能实现成为装饰器,那么需要给它们定义一个抽象的父类,示例如下:
?
?????? ?好好体会一下,是不是也在没有惊动原始业务对象的情况下,给它织入了新的功能呢?也就是说是在原始业务不知情的情况下,给原始业务对象透明的增加了新功能,从而模拟实现了AOP的功能。
??????? 事实上,这种做法,完全可以应用在项目开发上,在后期为项目的业务对象添加数据检查、权限控制、日志记录等功能,就不需要在业务对象上去处理这些功能了,业务对象可以更专注于具体业务的处理。
3.4? 装饰模式的优缺点
比继承更灵活
??? 从为对象添加功能的角度来看,装饰模式比继承来得更灵活。继承是静态的,而且一旦继承是所有子类都有一样的功能。而装饰模式采用把功能分离到每个装饰器当中,然后通过对象组合的方式,在运行时动态的组合功能,每个被装饰的对象,最终有哪些功能,是由运行期动态组合的功能来决定的。更容易复用功能
??? 装饰模式把一系列复杂的功能,分散到每个装饰器当中,一般一个装饰器只实现一个功能,这样实现装饰器变得简单,更重要的是这样有利于装饰器功能的复用,可以给一个对象增加多个同样的装饰器,也可以把一个装饰器用来装饰不同的对象,从而复用装饰器的功能。简化高层定义
??? 装饰模式可以通过组合装饰器的方式,给对象增添任意多的功能,因此在进行高层定义的时候,不用把所有的功能都定义出来,而是定义最基本的就可以了,可以在使用需要的时候,组合相应的装饰器来完成需要的功能。会产生很多细粒度对象
??? 前面说了,装饰模式是把一系列复杂的功能,分散到每个装饰器当中,一般一个装饰器只实现一个功能,这样会产生很多细粒度的对象,而且功能越复杂,需要的细粒度对象越多。?
3.5? 思考装饰模式1:装饰模式的本质
??????? 装饰模式的本质:动态组合。
??????? 动态是手段,组合才是目的。这里的组合有两个意思,一个是动态功能的组合,也就是动态进行装饰器的组合;另外一个是指对象组合,通过对象组合来实现为被装饰对象透明的增加功能。
??????? 但是要注意,装饰模式不仅仅可以增加功能,也可以控制功能的访问,可以完全实现新的功能,还可以控制装饰的功能是在被装饰功能之前还是之后来运行等。
??????? 总之,装饰模式是通过把复杂功能简单化,分散化,然后在运行期间,根据需要来动态组合的这么一个模式。2:何时选用装饰模式
如果需要在不影响其它对象的情况下,以动态、透明的方式给对象添加职责,可以使用装饰模式,这几乎就是装饰模式的主要功能如果不合适使用子类来进行扩展的时候,可以考虑使用装饰模式,因为装饰模式是使用的“对象组合”的方式。所谓不适合用子类扩展的方式,比如:扩展功能需要的子类太多,造成子类数目呈爆炸性增长。
?????? 建议在如下情况中,选用装饰模式:?
3.6? 相关模式装饰模式与适配器模式
??? 这是两个没有什么关联的模式,放到一起来说,是因为它们有一个共同的别名:Wrapper。
??? 这两个模式功能上是不一样的,适配器模式是用来改变接口的,而装饰模式是用来改变对象功能的。装饰模式与组合模式
??? 这两个模式有相似之处,都涉及到对象的递归调用,从某个角度来说,可以把装饰看成是只有一个组件的组合。
??? 但是它们的目的完全不一样,装饰模式是要动态的给对象增加功能;而组合模式是想要管理组合对象和叶子对象,为它们提供一个一致的操作接口给客户端,方便客户端的使用。装饰模式与策略模式
??? 这两个模式可以组合使用。
??? 策略模式也可以实现动态的改变对象的功能,但是策略模式只是一层选择,也就是根据策略选择一下具体的实现类而已。而装饰模式不是一层,而是递归调用,无数层都可以,只要组合好装饰器的对象组合,那就可以依次调用下去,所以装饰模式会更灵活。
??? 而且策略模式改变的是原始对象的功能,不像装饰模式,后面一个装饰器,改变的是经过前一个装饰器装饰过后的对象,也就是策略模式改变的是对象的内核,而装饰模式改变的是对象的外壳。
??? 这两个模式可以组合使用,可以在一个具体的装饰器里面使用策略模式,来选择更具体的实现方式。比如前面计算奖金的另外一个问题就是参与计算的基数不同,奖金的计算方式也是不同的。举例来说:假设张三和李四参与同一个奖金的计算,张三的销售总额是2万元,而李四的销售额是8万元,它们的计算公式是不一样的,假设奖金的计算规则是,销售额在5万以下,统一3%,而5万以上,5万内是4%,超过部分是6%。
??? 参与同一个奖金的计算,这就意味着可以使用同一个装饰器,但是在装饰器的内部,不同条件下计算公式不一样,那么怎么选择具体的实现策略呢?自然使用策略模式就好了,也就是装饰模式和策略模式组合来使用。装饰模式与模板方法模式
??? 这是两个功能上有相似点的模式。
??? 模板方法模式主要应用在算法骨架固定的情况,那么要是算法步骤不固定呢,也就是一个相对动态的算法步骤,就可以使用装饰模式了,因为在使用装饰模式的时候,进行装饰器的组装,其实也相当于是一个调用算法步骤的组装,相当于是一个动态的算法骨架。
??? 既然装饰模式可以实现动态的算法步骤的组装和调用,那么把这些算法步骤固定下来,那就是模板方法模式实现的功能了,因此装饰模式可以模拟实现模板方法模式的功能。
??? 但是请注意,仅仅只是可以模拟功能而已,两个模式的设计目的、原本的功能、本质思想等都是不一样的。
??装饰模式结束,谢谢观赏?
![]()
先将这个模式的每一篇给顶个然后再看,我怕我看完了忘记了顶。这样就太对不起博主了public class Client { public static void main(String[] args) { //得到业务接口,组合装饰器 GoodsSaleEbi ebi = new CheckDecorator( new LogDecorator( new GoodsSaleEbo())); //准备测试数据 SaleModel saleModel = new SaleModel(); saleModel.setGoods("Moto手机"); saleModel.setSaleNum(2); //调用业务功能 ebi.sale("张三","张三丰", saleModel); ebi.sale("李四","张三丰", saleModel); } }
以下您的注释:
好好体会一下,是不是也在没有惊动原始业务对象的情况下,给它织入了新的功能呢?也就是说是在原始业务不知情的情况下,给原始业务对象透明的增加了新功能,从而模拟实现了AOP的功能。
事实上,这种做法,完全可以应用在项目开发上,在后期为项目的业务对象添加数据检查、权限控制、日志记录等功能,就不需要在业务对象上去处理这些功能了,业务对象可以更专注于具体业务的处理。
我是在想,业务对象的初始化应该也属于业务代码的编写,就是这个:
GoodsSaleEbi ebi = new CheckDecorator(
new LogDecorator(
new GoodsSaleEbo()));
按照您的意思,用装饰者模式来实现AOP是不是意味着我所有调用业务实体的地方初始话方式都需要类似上面的这种包装new的方式了??这个还是耦合进业务代码里了啊,假如我的应用已经上线了,如果有新的需要添加一个新的AOP功能,是不是需要修改所有的new方式加一层装饰类呢??
目前的许多AOP实现是在不更改原有业务代码的基础上用配置文件或是修改CLASS来实现的,这样对于开发人员来说,他才真正只需要专注业务代码了.
对于装饰者模式您感觉您讲解的很清晰,就是对于上面您的这个说法还有点疑惑???
确实如你所写的那样,客户端需要“包装new的方式”,所以是模拟AOP实现。
现在真正AOP的实现,多是采用了动态代理的方式。
其实你也可以把这个“客户端包装new的方式”做成动态的,比如通过读取配置文件来组装的方式,也可以避免在后期添加功能的时候去修改代码。