一、引子
						
						
				
		
		
				
						装饰模式?肯定让你想起又黑又火的家庭装修来。其实两者在道理上还是有很多相像的地方。家庭装修无非就是要掩盖住原来实而不华的墙面,抹上一层华而不实的涂料,让生活多一点色彩。而墙还是那堵墙,他的本质一点都没有变,只是多了一层外衣而已。
						
						
				
		
		
				
						那设计模式中的装饰模式,是什么样子呢?
						
						
				
		
		
				
						
								 
						
				
		
		
				
						二、定义与结构
						
						
				
		
		
				
						装饰模式(
						Decorator
						)也叫包装器模式(
						Wrapper
						)。
						GOF
						在《设计模式》一书中给出的定义为:动态地给一个对象添加一些额外的职责。就增加功能来说,
						Decorator
						模式相比生成子类更为灵活。
						
						
				
		
		
				
						
								       
						
						让我们来理解一下这句话。我们来设计
						“
						门
						”
						这个类。假设你根据需求为
						“
						门
						”
						类作了如下定义:
				
		
		
				
						
						
						
								
										 
								
						
				
		
		
				
						
						
				
		
		
				
						
								
								
								
										
										
										
										
										
										
										
										
										
										
										
										
										
										
										
										
										
										
										
										
										
										
										
										
								
								
								
								
								
						
				
		
		
				
						现在,在系统的一个地方需要一个能够报警的
						Door
						,你来怎么做呢?你或许写一个
						Door
						的子类
						AlarmDoor
						,在里面添加一个子类独有的方法
						alarm()
						。嗯,那在使用警报门的地方你必须让客户知道使用的是警报门,不然无法使用这个独有的方法。而且,这个还违反了
						Liskov
						替换原则。
						
						
				
		
		
				
						也许你要说,那就把这个方法添加到
						Door
						里面,这样不就统一了?但是这样所有的门都必须有警报,至少是个
						“
						哑巴
						”
						警报。而当你的系统仅仅在一两个地方使用了警报门,这明显是不合理的
						——
						虽然可以使用缺省适配器来弥补一下。
						
						
				
		
		
				
						
								       
						
						这时候,你可以考虑采用装饰模式来给门动态的添加些额外的功能。
						
						
				
		
		
				
						
								       
						
						下面我们来看看装饰模式的组成,不要急着去解决上面的问题,到了下面自然就明白了!
						
						
				
		
		
				
						
								1)
								        
						
				
				
						抽象构件角色(
						Component
						):定义一个抽象接口,以规范准备接收附加责任的对象。
						
						
				
		
		
				
						
								2)
								        
						
				
				
						具体构件角色
						(Concrete Component)
						:这是被装饰者,定义一个将要被装饰增加功能的类。
						
						
				
		
		
				
						
								3)
								        
						
				
				
						装饰角色
						(Decorator)
						:持有一个构件对象的实例,并定义了抽象构件定义的接口。
						
						
				
		
		
				
						
								4)
								        
						
				
				
						具体装饰角色
						(Concrete Decorator)
						:负责给构件添加增加的功能。
						
						
				
		
		
				
						看下装饰模式的类图:
				
		
		
				
						
								 
						
				
		
		
				
						
						
						
						
				
		
		
				
				
		
		
				
						图中
						ConcreteComponent
						可能继承自其它的体系,而为了实现装饰模式,他还要实现
						Component
						接口。整个装饰模式的结构是按照组合模式来实现的,但是注意两者的目的是截然不同的(关于两者的不同请关注我以后的文章)。
						
						
				
		
		
				
						
								 
						
				
		
		
				
						三、举例
						
						
				
		
		
				
						这个例子还是来自我最近在研究的
						JUnit
						,如果你对
						JUnit
						还不太了解,可以参考
						
								
										
												《
										JUnit入门》
						
						、
						
								
										
												《
										JUnit源码分析(一)》
						
						、
						
								
										
												《
										JUnit源码分析(二)》
						
						、
						
								
										
												《
										JUnit源码分析(三)》
						
						。不愧是由
						GoF
						之一的
						Erich Gamma
						亲自开发的,小小的东西使用了
						N
						种的模式在里面。下面就来看看
						JUnit
						中的装饰模式。
						
						
				
		
		
				
						
								       
						
						在
						JUnit
						中,
						TestCase
						是一个很重要的类,允许对其进行功能扩展。
						
						
				
		
		
				
						
								       
						
						在
						junit.extensions
						包中,
						TestDecorator
						、
						RepeatedTest
						便是对
						TestCase
						的装饰模式扩展。下面我们将它们和上面的角色对号入座。
				
		
		
				
						
								 
						
				
		
		
				
						
						
						
						
				
		
		
				
				
		
		
				
						
								       
						
						呵呵,看看源代码吧,这个来的最直接!
						
						
				
		
		
				
						
								       //
						这个就是抽象构件角色
						
						
				
		
		
				
						
								       public interface Test {
				
		
		
				
						
								       /**
				
		
		
				
						
								       
								 * Counts the number of test cases that will be run by this test.
				
		
		
				
						
								       
								 */
				
		
		
				
						
								       public abstract int countTestCases();
				
		
		
				
						
								       /**
				
		
		
				
						
								       
								 * Runs a test and collects its result in a TestResult instance.
				
		
		
				
						
								       
								 */
				
		
		
				
						
								       public abstract void run(TestResult result);
				
		
		
				
						}
				
		
		
				
						
								 
						
				
		
		
				
						//
						具体构件对象,但是这里是个抽象类
						
						
				
		
		
				
						public abstract class TestCase extends Assert implements Test {
				
		
		
				
						
								       ……
				
		
		
				
						
								       public int countTestCases() {
				
		
		
				
						
								              return 1;
				
		
		
				
						
								       }
				
		
		
				
						
								       ……
				
		
		
				
						
								       public TestResult run() {
				
		
		
				
						
								              TestResult result= createResult();
				
		
		
				
						
								              run(result);
				
		
		
				
						
								              return result;
				
		
		
				
						
								       }
				
		
		
				
						
								       public void run(TestResult result) {
				
		
		
				
						
								              result.run(this);
				
		
		
				
						
								       }
				
		
		
				
						
								       ……
				
		
		
				
						}
				
		
		
				
						
								 
						
				
		
		
				
						//
						装饰角色
						
						
				
		
		
				
						public class TestDecorator extends Assert implements Test {
				
		
		
				
						
								       //
						这里按照上面的要求,保留了一个对构件对象的实例
						
						
				
		
		
				
						
								       protected Test fTest;
				
		
		
				
						
								 
						
				
		
		
				
						
								       public TestDecorator(Test test) {
				
		
		
				
						
								              fTest= test;
				
		
		
				
						
								       }
				
		
		
				
						
								       /**
				
		
		
				
						
								       
								 * The basic run behaviour.
				
		
		
				
						
								       
								 */
				
		
		
				
						
								       public void basicRun(TestResult result) {
				
		
		
				
						
								              fTest.run(result);
				
		
		
				
						
								       }
				
		
		
				
						
								       public int countTestCases() {
				
		
		
				
						
								              return fTest.countTestCases();
				
		
		
				
						
								       }
				
		
		
				
						
								       public void run(TestResult result) {
				
		
		
				
						
								              basicRun(result);
				
		
		
				
						
								       }
				
		
		
				
						
								       public String toString() {
				
		
		
				
						
								              return fTest.toString();
				
		
		
				
						
								       }
				
		
		
				
						
								       public Test getTest() {
				
		
		
				
						
								              return fTest;
				
		
		
				
						
								       }
				
		
		
				
						}
				
		
		
				
						
								 
						
				
		
		
				
						
								       //
						具体装饰角色,这个类的增强作用就是可以设置测试类的执行次数
						
						
				
		
		
				
						public class RepeatedTest extends  TestDecorator {
				
		
		
				
						
								    private int fTimesRepeat;
				
		
		
				
						
								 
						
				
		
		
				
						
								    public RepeatedTest(Test test, int repeat) {
				
		
		
				
						
								           super(test);
				
		
		
				
						
								           if (repeat < 0)
				
		
		
				
						
								                  throw new IllegalArgumentException("Repetition count must be > 0");
				
		
		
				
						
								           fTimesRepeat= repeat;
				
		
		
				
						
								    }
				
		
		
				
						
								    //
						看看怎么装饰的吧
						
						
				
		
		
				
						
								    public int countTestCases() {
				
		
		
				
						
								           return super.countTestCases()*fTimesRepeat;
				
		
		
				
						
								    }
				
		
		
				
						
								    public void run(TestResult result) {
				
		
		
				
						
								           for (int i= 0; i < fTimesRepeat; i++) {
				
		
		
				
						
								                  if (result.shouldStop())
				
		
		
				
						
								                         break;
				
		
		
				
						
								                  super.run(result);
				
		
		
				
						
								           }
				
		
		
				
						
								    }
				
		
		
				
						
								    public String toString() {
				
		
		
				
						
								           return super.toString()+"(repeated)";
				
		
		
				
						
								    }
				
		
		
				
						}
				
		
		
				
						
								       
						
						使用的时候,就可以采用下面的方式:
						
						
				
		
		
				
						TestDecorator test = new RepeatedTest(new TestXXX() , 3);
				
		
		
				
						让我们在回想下上面提到的
						“
						门
						”
						的问题,这个警报门采用了装饰模式后,可以采用下面的方式来产生。
						
						
				
		
		
				
						DoorDecorator alarmDoor = new AlarmDoor(new Door());
				
		
		
				
						
								 
						
				
		
		
				
						
								 
						
				
		
		
				
						四、应用环境
						
						
				
		
		
				
						
								       GOF
						书中给出了以下使用情况:
						
						
				
		
		
				
						
								1)
								        
						
				
				
						在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
						
						
				
		
		
				
						
								2)
								        
						
				
				
						处理那些可以撤消的职责。
						
						
				
		
		
				
						
								3)
								        
						
				
				
						当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
						
						
				
		
		
				
						来分析下
						JUnit
						的使用是属于哪种情况。首先实现了比静态继承更加灵活的方式,动态的增加功能。试想为
						Test
						的所有实现类通过继承来增加一个功能,意味着要添加不少的功能类似的子类,这明显是不太合适的。
						
						
				
		
		
				
						而且,这就避免了高层的类具有太多的特征,比如上面提到的带有警报的抽象门类。
						
						
				
		
		
				
						
								 
						
				
		
		
				
						五、透明和半透明
						
						
				
		
		
				
						
								       
						
						对于面向接口编程,应该尽量使客户程序不知道具体的类型,而应该对一个接口操作。这样就要求装饰角色和具体装饰角色要满足
						Liskov
						替换原则。像下面这样:
						
						
				
		
		
				
						Component c = new ConcreteComponent();
				
		
		
				
						Component c1 = new ConcreteDecorator(c);
				
		
		
				
						JUnit
						中就属于这种应用,这种方式被称为透明式。而在实际应用中,比如
						java.io
						中往往因为要对原有接口做太多的扩展而需要公开新的方法(这也是为了重用)。所以往往不能对客户程序隐瞒具体的类型。这种方式称为
						“
						半透明式
						”
						。
						
						
				
		
		
				在
				java.io
				中,并不是纯装饰模式的范例,它是装饰模式、适配器模式的混合使用。
				
				
		
		
				
						
								 
						
				
		
		
				
						六、其它
						
						
				
		
		
				
						采用
						Decorator
						模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。尽管对于那些了解这些系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困难。这是
						GOF
						提到的装饰模式的缺点,你能体会吗?他们所说的小对象我认为是指的具体装饰角色。这是为一个对象动态添加功能所带来的副作用。
						
						
				
		
		
				
						
								 
						
				
		
		
				
						七、总结
						
						
				
		
		
				
						       
				
				终于写完了,不知道说出了本意没有。请指正!