装饰模式(Decorator),就是可以动态的给对象增加新的功能,它要求装饰者对象和被装饰者对象有着相同的抽象父类或者接口。
当然,也可以写一个新的类来继承旧的类,在新的类中增加方法或者重写父类中的方法以此来达到扩展功能的目的,但是这样做的话,依然在编译的时候就定死了这个新的子类及其对象。想要动态的达到这一目的,就得使用这个模式了。
装饰者模式有以下几个要点(来自HeadFirst):
(1)装饰者和被装饰者有着相同的超类型
(2)可以使用多个装饰者来装饰一个对象
(3)对象可以在任何时候被装饰,动态的
(4)在该模式的使用过程中,也会用到继承,但是继承的目的不是为了获得行为,而是让装饰者和被装饰者有同样的超类型
此模式的实现有如下几个角色:
(1)抽象构件角色:定义一个抽象接口,来规范准备附加功能的类。
(2)具体构件角色:将要被附加功能的类,实现抽象构件角色接口。
(3)抽象装饰者角色:持有对具体构件角色的引用并定义与抽象构件角色一致的接口。
(4)具体装饰角色:实现抽象装饰者角色,负责为具体构件添加额外功能。
我们此处的例子依然以手机生产为例,一个手机作为商品出售,除了机身之外,还有外壳和耳机等配件,价格自然不同,商家搭配成套餐出售,消费者也可以自定义买,最后有一个总价格。
首先定义个抽象的构件接口:
1 package org.scott.decorator; 2 /** 3 * @author Scott 4 * @date 2013-11-23 5 * @description 6 */ 7 public abstract class Component { 8 String description = "Unknown component"; 9 10 public String getDescription(){ 11 return description; 12 } 13 14 public abstract double getCost(); 15 }
然后是具体的手机Component:
1 package org.scott.decorator; 2 /** 3 * @author Scott 4 * @date 2013-11-23 5 * @description 6 */ 7 public class Phone extends Component { 8 9 public Phone(){ 10 description = "Phone"; 11 } 12 13 @Override 14 public double getCost() { 15 return 1000; 16 } 17 18 }
接着定义装饰者:
1 package org.scott.decorator; 2 /** 3 * @author Scott 4 * @date 2013-11-23 5 * @description 6 */ 7 public abstract class Decorator extends Component{ 8 public abstract String getDescription(); 9 }
下面是两种不同的套餐,也就是针对手机的两种装饰者类:
手机壳搭配销售的套餐:
1 package org.scott.decorator; 2 /** 3 * @author Scott 4 * @date 2013-11-23 5 * @description 6 */ 7 public class PhoneShellSet extends Decorator { 8 Component com; 9 10 public PhoneShellSet(Component com){ 11 this.com = com; 12 } 13 14 @Override 15 public String getDescription() { 16 return com.getDescription() + " + phoneShell"; 17 } 18 19 @Override 20 public double getCost() { 21 return com.getCost() + 30.0; 22 } 23 24 }
耳机搭配销售的套餐:
1 package org.scott.decorator; 2 3 /** 4 * @author Scott 5 * @date 2013-11-23 6 * @description 7 */ 8 public class EarpieceSet extends Decorator { 9 10 Component com; 11 12 public EarpieceSet(Component com) { 13 this.com = com; 14 } 15 16 @Override 17 public String getDescription() { 18 return com.getDescription() + " + Earpiece"; 19 } 20 21 @Override 22 public double getCost() { 23 return com.getCost() + 80.0; 24 } 25 }
功能代码就是这些,写个测试类跑一把:
1 package org.scott.decorator; 2 /** 3 * @author Scott 4 * @date 2013-11-23 5 * @description 6 */ 7 public class DecoratorTest { 8 public static void main(String [] args) { 9 Component component = new Phone(); 10 component = new PhoneShellSet(component); 11 component = new EarpieceSet(component); 12 13 System.out.println("所购买的的商品为:" + component.getDescription()); 14 System.out.println("一共花费:"+ component.getCost()); 15 } 16 }
运行结果:
所购买的的商品为:Phone + phoneShell + Earpiece
一共花费:1110.0
当然,套餐你想搞成什么样就搞什么样,可以动态的实现搭配组合,这就是装饰者模式最大的优点了吧。
在JDK的源码中最能体现装饰者模式的,就是IO系列类,空了再分析分析那几个东西,挺有意思~
上个图吧,装饰者模式的经典类图: