装饰器模式
装饰器模式的定义:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式比生成子类更为灵活。
我们先来看一下装饰器模式的结构图:
装饰器模式遵守的编程原则之一就是“开放-关闭原则”,倘若ConcreteComponent的operation方法是一个很复杂的且经常变更的方法,我们不可能在每次变更的时候都去更改这个operation方法,在里面写一大堆if...else...,这样将来兼职是不堪入目。于是我们想通过外部的其他类(装饰器类)来透明的为ConcreteComponent的operation方法扩展功能。我们要给ConcreteComponent这个对象增加功能,但是不能让这个对象知道,也就是不能去改动这个对象。
接下来我们来看下装饰器模式的代码(我们假设的应用场景是算工资,假设 应发工资=基本工资-克扣工资+奖金):
public class Salary:ISalary { public double CalSalary(string userName) { //原有的计算工资的代码 } }
public abstract AbstractSalary:ISalary { private ISalary salary=null; public AbstractSalary(ISalary salary) { salary=salary; } public double CalSalary(string userName) { salary.CalSalary(userName); } }
public class TakeoffSalary:AbstractSalary { public TakeoffSalary(ISalary salary) { base(salary); } public double CalSalary(string userName) { //在调用原有代码之前也可以在这里做一些功能处理 //调用原有的计算工资的代码算出基本工资 double mySalary = base.CalSalary(userName); //然后再在这里调用克扣的工资代码 double takeOffSalary=... return mySalary-takeOffSalary } }
public class BonusSalary:AbstractSalary { public BonusSalary(ISalary salary) { base(salary); } public double CalSalary(string userName) { double salary=base.CalSalary(userName); //这里执行奖金的计算 double bonusSalary=... return salary+bonusSalary } }
最后我们再来看看客户端是怎么用的:
public class Client { public static void Main(string[] args) { string userName="保罗·乔治"; //先实例化要被装饰的类——原本计算基本工资的类 Salary salary=new Salary(); //然后用计算克扣工资的类装饰它 TakeOffSalary takeOffSalary=new TakeOffSalary(salary); //继续装饰,用计算奖金的类装饰 BonusSalary bonusSalary=new BonusSalary(takeOffSalary); //最后开始计算 double paulgeorgeSalary=bonusSalary.CalSalary(); } }
看过前面的代码,应该可以看出这里的调用其实是递归调用,从 BonusSalary.CalSalary 到 TakeOffSalary.CalSalary 最后到 Salary.CalSalary 然后一层层返回,最终算出应发工资。所以这里的装饰是有顺序的,但最好的装饰器模式是各装饰器之间最好相互独立,这样才能最灵活。
这样就能很灵活的给被装饰类 Salary 增加或减少装饰器的功能了,这些功能就达到了动态组合的
一个类的扩展方式,可以是继承,也可以是对象组合。尽量使用对象组合,而不是继承来扩展和复用功能。
举个例子:对象A,实现了a1方法和a2方法。而B只想使用A的a1方法,使用继承自然是不合适的,这里就可以用到对象组合了。
public class A { public void a1(){} public void a2(){} } public class B { A a=new A(); public void a1() { a.a1(); } public void b1(){} }
装饰器模式是把被装饰对象的一系列复杂的功能,分散到各个装饰器中,并且做到了对被装饰对象的完全透明化。这很容易让人想起AOP(面向切面编程),它们共同的思想是“在一段逻辑代码不知情的情况下,在它前面或后面插入一段代码逻辑,就像插入一个横切面一样”。最容易理解的的“切面“就是像log和验证这种散落在各模块中但其实又与具体业务逻辑关系不大的功能。我们如果使用装饰器模式来实现AOP思想的话,比如,购物系统里用户购买东西这个业务,具体的购买的业务逻辑的对象可以看作被装饰对象,装饰它的装饰器有log装饰器,和身份验证装饰器,把这3装饰在一起,最终就能实现:用户购买前先验证身份,然后购买,购买完了再记录购买log。而购买东西的这个对象完全不知道身份验证和log这两块代码是何时何地加入的。
更多关于AOP的文章:Spring AOP详解 轻松理解AOP(面向切面编程) 专治不会看源码的毛病--spring源码解析AOP篇
装饰模式的本质:动态组合