装饰模式 应用场景和实现

有个大神写的很好:

参考:设计模式学习笔记(四:策略模式)

参考:设计模式学习笔记(二:观察者模式)

参考:设计模式学习笔记-代理模式

参考:设计模式--装饰者模式与代理模式(重要)

参考:设计模式——代理模式与装饰模式的异同 (重要)

参考:设计模式之装饰模式

参考:java模式—装饰者模式

参考:修饰者模式(装饰者模式,Decoration) 

 

装饰者(decorator)模式:在不改变对象自身的基础上,在程序运行期间给对像动态的添加职责。与继承相比,装饰者是一种更轻便灵活的做法。
装饰者模式的特点
可以动态的给某个对象添加额外的职责,而不会影响从这个类中派生的其它对象;

(1)应用场景:

(a)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

  (b)  需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。  当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。

(2)实现的例子:

最常见的就是输入输出流:

 BufferedReader in1 = new BufferedReader(new InputStreamReader(new FileInputStream(file)));//字符流
 DataInputStream in2 = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));//字节流
  // DataInputStream-从数据流读取字节,并将它们装换为正确的基本类型值或字符串
  // BufferedInputStream-可以通过减少读写次数来提高输入和输出的速度

 

从例子开始讲解,比如今天你要出门约会,你肯定是要决定好你要穿什么样的衣服出门,用衣服来装饰下自己,让自己拥有一个完美的约会。比如,你会穿一件衬衫,然后穿一件西服裤,最后穿皮鞋出门。这就是装饰者模式的一个例子。为了实现这个功能,会有下面的代码。
public class People {
 
public void wearShirt(){
 
System.out.println("******穿衬衫******");
 
}
public void wearTrouser(){
 
System.out.println("******穿西服裤******");
 
}
 
 
 
public void wearShoes(){
 
System.out.println("******穿皮鞋******");
 
}
 
}

 

 
public class Client { 
public static void main(String[] args) {
 
People people = new People();
 
people.wearShirt();
 
people.wearTrouser();
 
people.wearShoes();
 
 } 
}

 

虽然上面的代码实现了出门穿衣服的功能,但是这里会问题。如果我现在要增加一个功能,我要先穿袜子,再穿皮鞋。此时就要在People类中增加一个穿袜子的方法。这就违背了设计模式的开闭原则,所谓开闭原则就是类可以进行扩展,但是不可以进行修改。所以就有如下的设计:
public interface People {
public void wearClothing(); 
}
public class Xiaoming implements People{ 
private String name; 
public Xiaoming(){
 
name = "小明";
 
} 
public void wearClothing(){
 
System.out.println(name+"******开始穿衣服******");
 
}
 
public String getName() {
 
return name;
 
}
 
}
public abstract class Finery implements People { 
protected People people; 
public Finery(People people){
 
this.people = people;
 
}
 
public abstract void wearClothing();
 
}
public class ShirtFinery extends Finery {
 
public ShirtFinery(People people){
 
super(people);
 
}
 
@Override
 
public void wearClothing() {
 
people.wearClothing();
 
System.out.println("******穿衬衫******");
 
} 
 
}
public class ShoesFinery extends Finery { 
public ShoesFinery(People people){
 
super(people);
 
}
 
@Override
 
public void wearClothing() {
 
people.wearClothing();
 
System.out.println("******穿皮鞋*******");
 
} 
}
 
public class TrouserFinery extends Finery {
 
 
 
public TrouserFinery(People people){
 
super(people);
 
}
 
@Override
 
public void wearClothing() {
 
people.wearClothing();
 
System.out.println("******穿西服裤*******");
 
} 
}
public class Client { 
 
public static void main(String[] args) {
 
People people = new Xiaoming();
 
Finery shirtFinery = new ShirtFinery(people);
 
Finery trouserFinery = new TrouserFinery(shirtFinery);
 
Finery shoesFinery = new ShoesFinery(trouserFinery);
 
shoesFinery.wearClothing();
 
}
 
 
 
}

  (3)类图:

People是定义了一个接口,用来添加具体的职责,而Xiaoming是具体的People,也就是被装饰的对象。对于Finery实现了People接口,进而对People进行扩展,而Finery的子类就是具体的装饰类,Finery中依赖People类,装饰的具体对象。 这就是所谓的装饰者模式。如果我要添加穿袜子的步骤,则只需要再添加一个实现类,完全不需要修改其他代码(Client是客户端类,肯定是要修改的)。

 

装饰者模式和代理模式的区别:

代理模式可以参考:Java设计模式之代理模式(静态代理和JDK、CGLib动态代理)以及应用场景

两种模式的特点


装饰模式:

  在不改变接口的前提下,动态扩展对象的访问。
  动态继承,让类具有在运行期改变行为的能力。
  装饰模式,突出的是运行期增加行为,这和继承是不同的,继承是在编译期增加行为。
  强调:增强,新增行为

代理模式:

  在不改变接口的前提下,控制对象的访问。
  1.从封装的角度讲,是为了解决类与类之间相互调用而由此导致的耦合关系,可以说是接口的另外一个层引用。
    比如:在a类->b代理->c类这个关系中,c类的一切行为都隐藏在b中。即调用者不知道要访问的内容与代理了什么对象。
  2.从复用的角度讲,可以解决不同类调用一个复杂类时,仅仅因较小的改变而导致整个复杂类新建一个类。
    比如:a类->c类1;b类->c类2。
    可以变为a类->ca代理类->c类;b类->cb代理类-c类。
  代理模式,是类之间的封装和(某方面的)复用。
  强调:限制,控制访问

 

总结:

  1. 装饰模式可以让使用者直观的看到增强了哪些功能,而代理模式完全限制了使用者。

  2. 对装饰模式来说,装饰者(Decorator)和被装饰者(Cafe)都实现同一个 接口。

  3. 对代理模式来说,代理类(Proxy Class)和真实处理的类(Real Class)都实现同一个接口。

  4. 此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。

装饰模式与继承的比较


  明显的,装饰模式可以动态的扩展对象的行为。

  比如某对象有30项行为,但是在第一阶段用到1-20行为,第二阶段用到11-30项行为,所以这时候,就可以只定义11-20的行为。

  在第一阶段运行时,可以将1-10的行为以“装饰1”给加上

  到第二阶段运行时,可以将“装饰1”去掉,将21-30的能以“装饰2”给加上。

  但是继承是在编译期增加行为。

装饰模式的优缺点


优点:

  1. 装饰模式可以提供比继承更多地灵活性。
  2. 可以通过一种动态的方式来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
  3. 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
  4. 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。

缺点:

  1. 会产生很多的小对象(具体装饰类),增加了系统的复杂性。
  2. 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

posted @ 2018-08-10 19:56  aspirant  阅读(10346)  评论(0编辑  收藏  举报