动态的给一个对象添加一个额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
――奖金计算
不用模式的解决方案
public class Prize { public double calcPrize(String user, Date begin, Date end) { double prize = 0.0; // 计算当月业务奖金,所有人都会计算 prize = this.monthPrize(user, begin, end); // 计算累计奖金 prize += this.calcPrize(user, begin, end); // 需要判断该人员是普通人员还是业务经理,团队奖金只有业务经理才有 if(this.isManager(user)) { prize += this.groupPrize(user, begin, end); } return prize; } private double groupPrize(String user, Date begin, Date end) { return 0; } private boolean isManager(String user) { return false; } private double monthPrize(String user, Date begin, Date end) { return 0; } }
使用装饰模式来解决问题
public abstract class Component { public abstract double calcPrize(String user, Date begin, Date end); } public abstract class Decorator extends Component { protected Component c; public Decorator(Component c) { super(); this.c = c; } public double calcPrize(String user, Date begin, Date end) { // 转调组件对象的方法 return c.calcPrize(user, begin, end); } } public class MonthPrizeDecorator extends Decorator { public MonthPrizeDecorator(Component c) { super(c); } public double calcPrize(String user, Date begin, Date end) { // 先获取前面运算出来的奖金 double money = super.calcPrize(user, begin, end); // 然后计算当月业务奖金 double prize = 0.0; return money + prize; } } public class SumPrizeDecorator extends Decorator { public SumPrizeDecorator(Component c) { super(c); } public double calcPrize(String user, Date begin, Date end) { // 先获取前面运算出来的奖金 double money = super.calcPrize(user, begin, end); // 然后计算累计奖金 double prize = 1000000 * 0.001; return money + prize; } } public class Client { public static void main(String[] args) { Component c1 = new ConcreteComponent(); Decorator d1 = new MonthPrizeDecorator(c1); Decorator d2 = new SumPrizeDecorator(d1); double userA = d1.calcPrize("a", null, null); double userB = d2.calcPrize("b", null, null); } }
案例说明:
由于奖金的计算方式经常发生变动,几乎每个季度都有小调整,每年都有大调整,这要求软件实现要足够灵活,能够很快进行相应的调整和修改,否则就不能满足实际业务的需要。把问题抽象以下:设若有一个计算奖金的对象,现在需要能够灵活的给它增加和减少功能,还需要能够动态的组合功能,每个功能就相当于在计算奖金的某个部分。
则问题就是:如何能透明地给一个对象增加功能,并实现功能的动态组合。
解决思路
所谓透明地给一个对象增加功能,即给一个对象增加功能,但不能让这个对象知道,也就不是不去改动这个对象,而实现给一个对象透明的增加功能,这就实现了功能的动态组合。
实现透明地给一个对象增加功能,即要扩展对象功能,可使用继承。
为了能够实现和原来使用被装饰对象的代码无缝结合,通过顶一个抽象类,让这个类实现与被装饰对象相同的接口,然后在具体实现类中,转调被装饰的独享,在转调前后添加新的功能,这就实现了给装饰对象增加功能。
在转调的时候,如果觉得被装饰对象的功能不再被需要,还可以直接替换,也就是不在转调,而是在装饰杜相忠完成全新的实现。
示例代码
public abstract class Component { public abstract void operation(); } public class ConcreteComponent extends Component { public void operation() { // 相应的功能处理 } } public abstract class Decorator extends Component { protected Component component; public Decorator(Component component) { super(); this.component = component; } public void operation() { // 转发请求给组件独享,可以在转发前后执行一些附加动作 component.operation(); } } public class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component component) { super(component); } private String addedState; public void operation() { // 调用父类的方法,可以在调用前后执行一些附加动作 // 在这里进行处理的时候,可以使用添加的状态 super.operation(); } } public class ConcreteDecoratorB extends Decorator { public ConcreteDecoratorB(Component component) { super(component); } private void addBehavior() { // 需要添加的职责实现 } public void operation() { // 调用父类的方法,可以在调用前后执行一些附加动作 super.operation(); this.addBehavior(); } }
应用范围
装饰模式能够实现动态添加,是从一个对象外部来给对象添加功能,相当于改变了对象的外观。当装饰后,从外部使用系统的角度,就不再是使用原来是的那个类,而是使用被一系列装饰器包装过后的对象。
对象组合
在面向对象的设计中,有一条基本规则就是"尽量使用对象组合,而不是继承"来扩展和复用功能。装饰模式就是这个规则。
假如有一个对象A,实现了a1方法,而C1对象想要扩展A的功能,给它增加一个c1的方法。
使用继承:
public class A { public void a1() {} } public class C extends A { public void c1() {} }
使用对象组合:
public class C { private A a = new A(); public void a1() { a.a1(); } public void c1() {} }
装饰器和组件类的关系
装饰器是用来修饰组件的,装饰器一定要实现和组件类一致的接口。组件类是不知道装饰器的存在的,装饰器为组件提那家功能是一种透明的包装。
装饰器的应用―― I/O流
InputStream就相当于装饰模式中的Component;
FileInputStream、ObjectInputStream、StringBufferInputStream这些对象都是直接继承InputStream,这些相当于装饰模式中的ConcreteComponent,是可以被装饰器装饰的对象;
FilterInputStream相当于装饰器模式中的Decorator,而它的子类DataInputStream、BufferedInputStream、LinkNumberInputStream和PushbackInputStream 相当于装饰器模式中的ConcreteDecorator。
装饰模式的优缺点:
优点:比继承更灵活、更容易复用功能、简化高层定义
缺点:会产生很多细粒度对象
装饰模式的本质是:动态组合
动态是手段,组合才是目的。这里组合有两个意思,一个是动态功能的组合,也就是动态进行装饰器的组合;另一个是指对象组合,通过对象组合来实现为装饰对象透明的增加功能。
何时选用装饰模式模式
如果想:在不影响其他对象的情况下,以动态、透明的方式给对象添加职责,可以使用装饰模式。
如果:不适合使用子类来扩展的时候,可以使用装饰模式。
说明:笔记内容摘自《研磨设计模式》陈臣,王斌
关联:整理了一些Java软件工程师的基础知识点