Java 设计模式之 -- 装饰者模式

对于装饰模式我们先看官方的介绍

在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。由于java的封装继承多态三个特性使得我们充分扩展父类的功能,装饰者模式就是建立在这样的基础上的。设计初衷:通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的。为了更好的说明装饰模式的使用,下面引用下面博主的举例,相关链接

http://blog.csdn.net/jason0539/article/details/22713711

实际上Java 的I/O API就是使用Decorator实现的。

[java] view plain copy

 print ?
  1. //定义被装饰者  
  2. public interface Human {  
  3.     public void wearClothes();  
  4.   
  5.     public void walkToWhere();  
  6. }  
  7.   
  8. //定义装饰者  
  9. public abstract class Decorator implements Human {  
  10.     private Human human;  
  11.   
  12.     public Decorator(Human human) {  
  13.         this.human = human;  
  14.     }  
  15.   
  16.     public void wearClothes() {  
  17.         human.wearClothes();  
  18.     }  
  19.   
  20.     public void walkToWhere() {  
  21.         human.walkToWhere();  
  22.     }  
  23. }  
  24.   
  25. //下面定义三种装饰,这是第一个,第二个第三个功能依次细化,即装饰者的功能越来越多  
  26. public class Decorator_zero extends Decorator {  
  27.   
  28.     public Decorator_zero(Human human) {  
  29.         super(human);  
  30.     }  
  31.   
  32.     public void goHome() {  
  33.         System.out.println("进房子。。");  
  34.     }  
  35.   
  36.     public void findMap() {  
  37.         System.out.println("书房找找Map。。");  
  38.     }  
  39.   
  40.     @Override  
  41.     public void wearClothes() {  
  42.         // TODO Auto-generated method stub  
  43.         super.wearClothes();  
  44.         goHome();  
  45.     }  
  46.   
  47.     @Override  
  48.     public void walkToWhere() {  
  49.         // TODO Auto-generated method stub  
  50.         super.walkToWhere();  
  51.         findMap();  
  52.     }  
  53. }  
  54.   
  55. public class Decorator_first extends Decorator {  
  56.   
  57.     public Decorator_first(Human human) {  
  58.         super(human);  
  59.     }  
  60.   
  61.     public void goClothespress() {  
  62.         System.out.println("去衣柜找找看。。");  
  63.     }  
  64.   
  65.     public void findPlaceOnMap() {  
  66.         System.out.println("在Map上找找。。");  
  67.     }  
  68.   
  69.     @Override  
  70.     public void wearClothes() {  
  71.         // TODO Auto-generated method stub  
  72.         super.wearClothes();  
  73.         goClothespress();  
  74.     }  
  75.   
  76.     @Override  
  77.     public void walkToWhere() {  
  78.         // TODO Auto-generated method stub  
  79.         super.walkToWhere();  
  80.         findPlaceOnMap();  
  81.     }  
  82. }  
  83.   
  84. public class Decorator_two extends Decorator {  
  85.   
  86.     public Decorator_two(Human human) {  
  87.         super(human);  
  88.     }  
  89.   
  90.     public void findClothes() {  
  91.         System.out.println("找到一件D&G。。");  
  92.     }  
  93.   
  94.     public void findTheTarget() {  
  95.         System.out.println("在Map上找到神秘花园和城堡。。");  
  96.     }  
  97.   
  98.     @Override  
  99.     public void wearClothes() {  
  100.         // TODO Auto-generated method stub  
  101.         super.wearClothes();  
  102.         findClothes();  
  103.     }  
  104.   
  105.     @Override  
  106.     public void walkToWhere() {  
  107.         // TODO Auto-generated method stub  
  108.         super.walkToWhere();  
  109.         findTheTarget();  
  110.     }  
  111. }  
  112.   
  113. //定义被装饰者,被装饰者初始状态有些自己的装饰  
  114. public class Person implements Human {  
  115.   
  116.     @Override  
  117.     public void wearClothes() {  
  118.         // TODO Auto-generated method stub  
  119.         System.out.println("穿什么呢。。");  
  120.     }  
  121.   
  122.     @Override  
  123.     public void walkToWhere() {  
  124.         // TODO Auto-generated method stub  
  125.         System.out.println("去哪里呢。。");  
  126.     }  
  127. }  
  128. //测试类,看一下你就会发现,跟java的I/O操作有多么相似  
  129. public class Test {  
  130.     public static void main(String[] args) {  
  131.         Human person = new Person();  
  132.         Decorator decorator = new Decorator_two(new Decorator_first(  
  133.                 new Decorator_zero(person)));  
  134.         decorator.wearClothes();  
  135.         decorator.walkToWhere();  
  136.     }  
  137. }  

运行结果:

Java 设计模式之 -- 装饰者模式_第1张图片


UML类图

我们现在来看看装饰者模式的 uml 类图:
Java 设计模式之 -- 装饰者模式_第2张图片
装饰者模式共有四大角色:

    • Component:抽象组件

可以是一个接口或者是抽象类,其充当的就是被装饰的原始对象,用来定义装饰者和被装饰者的基本行为。

    • ConcreteComponent:组件具体实现类

该类是 Component 类的基本实现,也是我们装饰的具体对象。

    • Decorator:抽象装饰者

装饰组件对象,其内部一定要有一个指向组件对象的引用。在大多数情况下,该类为抽象类,需要根据不同的装饰逻辑实现不同的具体子类。当然,如果是装饰逻辑单一,只有一个的情况下我们可以忽略该类直接作为具体的装饰者。

    • ConcreteDecoratorA 和 ConcreteDecoratorB:装饰者具体实现类

对抽象装饰者的具体实现。

在已有的 Component 和 ConcreteComponent 体系下,实现装饰者模式来扩展原有系统的功能,可以分为 5 个步骤

  1. 继承或者实现 Component 组件,生成一个 Decorator 装饰者抽象类;
  2. 在生成的这个 Decorator 装饰者类中,增加一个 Component 的私有成员对象;
  3. 将 ConcreteComponent 或者其他需要被装饰的对象传入 Decorator 类中并赋值给上一步的 Component 对象;
  4. 在装饰者 Decorator 类中,将所有的操作都替换成该 Component 对象的对应操作;
  5. 在 ConcreteDecorator 类中,根据需要对应覆盖需要重写的方法。

装饰者模式在源码中用的也是非常多的,在 Java 和 Android 中都能够见到装饰者模式的影子:

Java 中的装饰者模式

最典型的就是 Java 中的 java.io 包下面的 InputStream 和 OutputStream 相关类了,初学 Java 的时候,看到这些类,头都大了,其实学了装饰者模式之后,再理解这些类就很简单了,画一个简单的类图来表示:
Java 设计模式之 -- 装饰者模式_第3张图片
InputStream 类是一个抽象组件, FileInputStream,StringBufferInputStream 和 ByteArrayInputStream 类都是可以被装饰者包起来的具体组件;FilterInputStream 是一个抽象装饰者,所以它的四个子类都是一个个装饰者了。

Android 中的装饰者模式

其次,对于 android 开发工程师来说,最最重要的就应该是“上帝类” Context 和其子类了,这些类我就不用解释了,上一张类图基本就明确了:
Java 设计模式之 -- 装饰者模式_第4张图片
所以对于 Application,Activity 和 Service 等类来说,他们只是一个个装饰者,都是用来装饰 ContextImpl 这个被装饰者类,Application 是在 createBaseContextForActivity 方法中,通过 ContextImpl 的静态方法 createActivityContext 获得一个 ContextImpl 的实例对象,并通过 setOuterContext 方法将两者建立关联;Activity 是通过 handleLaunchActivity 方法设置的 ContextImpl 实例,这个方法会获取到一个Activity对象,在performLaunchActivity函数中会调用该activity的attach方法,这个方法把一个ContextImpl对象attach到了Activity中,具体可以看看我的这篇博客,里面详细介绍到了 Activity 的启动过程:android 不能在子线程中更新ui的讨论和分析。别的类在这里就不介绍了,具体的大家可以去网上查阅相关资料。

示例与源码

我们这里以一个图形系统中的 Window 为例,一般情况窗口都是能够垂直或者是左右欢动的,所以为了能够更好的支持 Window 的滑动,给滑动的 Window 加上一个 ScrollBar 是一个不错的方法,为了重用代码,水平滑动的 Window 和垂直滑动的 Window 我们就能够使用装饰者模式去处理,基本类图如下所示:
Java 设计模式之 -- 装饰者模式_第5张图片
根据类图,我们首先实现 Window 这个接口:
IWindow.class

1
2
3
4
5
6
public interface IWindow {
 
     void draw();
 
     String getDescription();
}

然后是被装饰者 SimpleWindow 类,它实现了窗口的基本行为:
SimpleWindow.class

1
2
3
4
5
6
7
8
9
10
11
public class SimpleWindow implements IWindow {
     @Override
     public void draw() {
         Log.e( "shawn" , "drawing a window" );
     }
 
     @Override
     public String getDescription() {
         return "a window" ;
     }
}

然后是装饰者类角色的抽象父类:
WindowDecorator.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class WindowDecorator implements IWindow{
 
     private IWindow window;
 
     public WindowDecorator(IWindow window) {
         this .window = window;
     }
 
     @Override
     public void draw() {
         window.draw();
     }
 
     @Override
     public String getDescription() {
         return window.getDescription();
     }
}

最后是实现该装饰者父类的装饰者子类:
HorizontalScrollBarDecorator.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HorizontalScrollBarDecorator extends WindowDecorator {
 
     public HorizontalScrollBarDecorator(IWindow window) {
         super (window);
     }
 
     @Override
     public void draw() {
         super .draw();
         Log.e( "shawn" , "then drawing the horizontal scroll bar" );
     }
 
     @Override
     public String getDescription() {
         return super .getDescription() + " with horizontal scroll bar" ;
     }
}

VerticalScrollBarDecorator.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class VerticalScrollBarDecorator extends WindowDecorator {
 
     public VerticalScrollBarDecorator(IWindow window) {
         super (window);
     }
 
     @Override
     public void draw() {
         super .draw();
         Log.e( "shawn" , "then drawing the vertical scroll bar" );
     }
 
     @Override
     public String getDescription() {
         return super .getDescription() + " with vertical scroll bar" ;
     }
}

最后测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
switch (v.getId()) {
     case R.id.btn_horizontal_window:
         IWindow horizontalWindow = new HorizontalScrollBarDecorator( new SimpleWindow());
         horizontalWindow.draw();
         Log.e( "shawn" , "window description : " + horizontalWindow.getDescription());
         break ;
     case R.id.btn_vertical_window:
         IWindow verticalWindow = new VerticalScrollBarDecorator( new SimpleWindow());
         verticalWindow.draw();
         Log.e( "shawn" , "window description : " + verticalWindow.getDescription());
         break ;
}

结果:

1
2
3
4
5
6
com.android.decoratorpattern E/shawn: drawing a window
com.android.decoratorpattern E/shawn: then drawing the horizontal scroll bar
com.android.decoratorpattern E/shawn: window description : a window with horizontal scroll bar
com.android.decoratorpattern E/shawn: drawing a window
com.android.decoratorpattern E/shawn: then drawing the vertical scroll bar
com.android.decoratorpattern E/shawn: window description : a window with vertical scroll bar

代码一目了然,结构清晰。
其实说到底,每一个写过 Android 程序的人都应该用过装饰者模式,因为每写一个 Activity,就相当于是写了一个装饰者类,不经意间就用了装饰者模式,大家想一想是不是,哈哈~~

你可能感兴趣的:(Java 设计模式之 -- 装饰者模式)