设计模式之装饰者模式

来源:互联网 发布:生产管理工程师 知乎 编辑:程序博客网 时间:2024/05/16 02:52
     装饰者模式 :动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

     装饰者模式类图:
          
  
 

    问题:  说装饰者模式比用继承会更富有弹性,在类图中不是一样用到了继承了吗?

    说明:装饰者和被装饰者之间必须是一样的类型,也就是要有共同的超类。在这里应用继承并不是实现方法的复制,而是实现类型的匹配。因为装饰者和被装饰者是同一个类型,因此装饰者可以取代被装饰者,这样就使被装饰者拥有了装饰者独有的行为。根据装饰者模式的理念,我们可以在任何时候,实现新的装饰者增加新的行为。如果是用继承,每当需要增加新的行为时,就要修改原程序了。

    //定义被装饰者      public interface Human {          public void wearClothes();                public void walkToWhere();      }            //定义装饰者      public abstract class Decorator implements Human {          private Human human;                public Decorator(Human human) {              this.human = human;          }                public void wearClothes() {              human.wearClothes();          }                public void walkToWhere() {              human.walkToWhere();          }      }            //下面定义三种装饰,这是第一个,第二个第三个功能依次细化,即装饰者的功能越来越多      public class Decorator_zero extends Decorator {                public Decorator_zero(Human human) {              super(human);          }                public void goHome() {              System.out.println("进房子。。");          }                public void findMap() {              System.out.println("书房找找Map。。");          }                @Override          public void wearClothes() {              // TODO Auto-generated method stub              super.wearClothes();              goHome();          }                @Override          public void walkToWhere() {              // TODO Auto-generated method stub              super.walkToWhere();              findMap();          }      }            public class Decorator_first extends Decorator {                public Decorator_first(Human human) {              super(human);          }                public void goClothespress() {              System.out.println("去衣柜找找看。。");          }                public void findPlaceOnMap() {              System.out.println("在Map上找找。。");          }                @Override          public void wearClothes() {              // TODO Auto-generated method stub              super.wearClothes();              goClothespress();          }                @Override          public void walkToWhere() {              // TODO Auto-generated method stub              super.walkToWhere();              findPlaceOnMap();          }      }            public class Decorator_two extends Decorator {                public Decorator_two(Human human) {              super(human);          }                public void findClothes() {              System.out.println("找到一件D&G。。");          }                public void findTheTarget() {              System.out.println("在Map上找到神秘花园和城堡。。");          }                @Override          public void wearClothes() {              // TODO Auto-generated method stub              super.wearClothes();              findClothes();          }                @Override          public void walkToWhere() {              // TODO Auto-generated method stub              super.walkToWhere();              findTheTarget();          }      }            //定义被装饰者,被装饰者初始状态有些自己的装饰      public class Person implements Human {                @Override          public void wearClothes() {              // TODO Auto-generated method stub              System.out.println("穿什么呢。。");          }                @Override          public void walkToWhere() {              // TODO Auto-generated method stub              System.out.println("去哪里呢。。");          }      }      //测试类,看一下你就会发现,跟java的I/O操作有多么相似      public class Test {          public static void main(String[] args) {              Human person = new Person();              Decorator decorator = new Decorator_two(new Decorator_first(                      new Decorator_zero(person)));              decorator.wearClothes();              decorator.walkToWhere();          }      }  

运行结果:

其实就是进房子找衣服,然后找地图这样一个过程,通过装饰者的三层装饰,把细节变得丰富。

关键点:
1、Decorator抽象类中,持有Human接口,方法全部委托给该接口调用,目的是交给该接口的实现类即子类进行调用。
2、Decorator抽象类的子类(具体装饰者),里面都有一个构造方法调用super(human),这一句就体现了抽象类依赖于子类实现即抽象依赖于实现的原则。因为构造里面参数都是Human接口,只要是该Human的实现类都可以传递进去,即表现出Decorator dt = new Decorator_second(new Decorator_first(new Decorator_zero(human)));这种结构的样子。所以当调用dt.wearClothes();dt.walkToWhere()的时候,又因为每个具体装饰者类中,都先调用super.wearClothes和super.walkToWhere()方法,而该super已经由构造传递并指向了具体的某一个装饰者类(这个可以根据需要调换顺序),那么调用的即为装饰类的方法,然后才调用自身的装饰方法,即表现出一种装饰、链式的类似于过滤的行为。
3、具体被装饰者类,可以定义初始的状态或者初始的自己的装饰,后面的装饰行为都在此基础上一步一步进行点缀、装饰。
4、装饰者模式的设计原则为:对扩展开放、对修改关闭,这句话体现在我如果想扩展被装饰者类的行为,无须修改装饰者抽象类,只需继承装饰者抽象类,实现额外的一些装饰或者叫行为即可对被装饰者进行包装。所以:扩展体现在继承、修改体现在子类中,而不是具体的抽象类,这充分体现了依赖倒置原则,这是自己理解的装饰者模式。


Java I/O使用装饰模式设计:

FilterInputStream为装饰模式中的Decorator,FilterInputStream是一个普通的Java类,不是接口也不是抽象类

    public class FilterInputStreamextends InputStream{          protected  InputStream in;                    protected  FilterInputStream(InputStream in){              this.in = in;          }             ...      }  

在InputStream提供的基本方法的基础上,FilterInputStream的子类提供了更多附加功能,例如:

BufferedInputStream类会提供一个内部的字节数组作为输入缓存

通过DataInputStream类,可以用与机器无关的方式从底层数据流中读取基本Java数据类型

LineNumberInputStream类可以跟踪当前行号

一个提供输入缓存、并且能够获取输入文件流行数的小程序:

[java] view plain copy
  1. import java.io.BufferedInputStream;  
  2. import java.io.File;  
  3. import java.io.FileInputStream;  
  4. import java.io.InputStream;  
  5. import java.io.LineNumberInputStream;  
  6.   
  7. public class Test {  
  8.     public static void main(String[] args) {  
  9.         try {  
  10.             InputStream in = new FileInputStream(new File("c:\\seanApp\\test.txt"));  
  11.             BufferedInputStream in1 = new BufferedInputStream(in);  
  12.             LineNumberInputStream in2 = new LineNumberInputStream(in1);  
  13.   
  14.             int result = in2.read();  
  15.             while(result != '\n'){  
  16.                 result = in2.read();  
  17.             }  
  18.             System.out.println(in2.getLineNumber());//2  
  19.             in2.close();  
  20.         } catch (Exception e) {  
  21.             e.printStackTrace();  
  22.         }  
  23.     }  
  24. }  

当然也可以通过继承FilterInputStream类来实现自己的装饰者:

[java] view plain copy
  1. import java.io.BufferedInputStream;  
  2. import java.io.File;  
  3. import java.io.FileInputStream;  
  4. import java.io.FilterInputStream;  
  5. import java.io.IOException;  
  6. import java.io.InputStream;  
  7.   
  8. //We can read the string directly by this class  
  9. public class MyInputStream extends FilterInputStream {  
  10.     protected MyInputStream(InputStream in) {  
  11.         super(in);  
  12.     }  
  13.   
  14.     public int read(String str) throws IOException {  
  15.         byte[] bs = str.getBytes();  
  16.         return super.read(bs);  
  17.     }  
  18.       
  19.     public static void main(String[] args){  
  20.         try {  
  21.             InputStream in = new FileInputStream(new File("c:\\seanApp\\test.txt"));  
  22.             BufferedInputStream in2 = new BufferedInputStream(in);  
  23.             MyInputStream in3 = new MyInputStream(in2);  
  24.               
  25.             in3.read("Just a test!!!");  
  26.             in3.close();  
  27.         } catch (Exception e) {  
  28.             e.printStackTrace();  
  29.         }  
  30.     }  
  31. }  

JDK中还有很多类是使用装饰模式设计的,如:Reader类、Writer类、OutputStream类等,如果使用这种结构,那么在基础方法上添加新的功能将十分简单

例如:java.io.Reader:

FilterReader是一个抽象类,可以很方便的进行扩展

参考链接:http://blog.csdn.net/a19881029/article/details/8990655

点击打http://www.cnblogs.com/ASPNET2008/archive/2008/06/15/1222724.html

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

0 0
原创粉丝点击