23种设计模式之四(装饰者模式)
装饰者模式:(动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性)
其别名为包装器(Wrapper);装饰模式是一种对象结构模式。
设计原则:对扩展开放、对修改关闭,这句话体现在我如果想扩展被装饰者类的行为,无须修改装饰者抽象类;只需继承装饰者抽象类,实现额外的一些装饰或者叫行为即可对被装饰者进行包装。
所以:扩展体现在继承、修改体现在子类中,而不是具体的抽象类,这充分体现了依赖倒置原则。
举例:
咖啡馆订单项目:
1)、咖啡种类:Espresso、ShortBlack、LongBlack、Decaf
2)、调料:Milk、Soy、Chocolate
3)、扩展性好、改动方便、维护方便
就如同调制一杯饮品:
主体:黑咖啡、浓缩黑咖啡、无咖啡因咖啡、意式浓缩咖啡
包装:牛奶、巧克力、豆浆
超类(被装饰者):
1 package com.java.jikexueyuan.coffeebar; 2 3 public abstract class Drink { 4 public String description=""; 5 private float price=0f;; 6 7 8 public void setDescription(String description) 9 { 10 this.description=description; 11 } 12 13 public String getDescription() 14 { 15 return description+"-"+this.getPrice(); 16 } 17 public float getPrice() 18 { 19 return price; 20 } 21 public void setPrice(float price) 22 { 23 this.price=price; 24 } 25 public abstract float cost(); 26 27 }
主体(被装饰者):
1 package com.java.jikexueyuan.coffeebar.coffee; 2 3 import com.java.jikexueyuan.coffeebar.Drink; 4 5 //中间层:将各种咖啡共有的功能进行封装 6 public class Coffee extends Drink { 7 @Override 8 public float cost() { 9 // TODO Auto-generated method stub 10 return super.getPrice(); 11 } 12 }
1 package com.java.jikexueyuan.coffeebar.coffee; 2 3 public class ShortBlack extends Coffee{ 4 5 public ShortBlack(){ 6 super.setDescription("ShortBlack"); 7 super.setPrice(5.0f); 8 } 9 10 }
1 package com.java.jikexueyuan.coffeebar.coffee; 2 3 public class LongBlack extends Coffee{ 4 5 public LongBlack(){ 6 super.setDescription("LongBlack"); 7 super.setPrice(6.0f); 8 } 9 10 }
1 package com.java.jikexueyuan.coffeebar.coffee; 2 3 public class Decaf extends Coffee { 4 public Decaf() { 5 super.setDescription("Decaf"); 6 super.setPrice(3.0f); 7 } 8 }
1 package com.java.jikexueyuan.coffeebar.coffee; 2 3 public class Espresso extends Coffee{ 4 5 public Espresso(){ 6 super.setDescription("Espresso"); 7 super.setPrice(4.0f); 8 } 9 10 }
装饰者:
1 package com.java.jikexueyuan.coffeebar.decorator; 2 3 import com.java.jikexueyuan.coffeebar.Drink; 4 5 //中间层:将各种调料的共有功能封装出来 6 public class Decorator extends Drink { 7 private Drink Obj; 8 9 public Decorator(Drink Obj){ 10 this.Obj=Obj; 11 }; 12 13 14 @Override 15 public float cost() { 16 return super.getPrice()+Obj.cost(); 17 } 18 19 @Override 20 public String getDescription(){ 21 return super.description+"-"+super.getPrice()+"&&"+Obj.getDescription(); 22 } 23 24 }
1 package com.java.jikexueyuan.coffeebar.decorator; 2 3 import com.java.jikexueyuan.coffeebar.Drink; 4 5 public class Chocolate extends Decorator { 6 7 public Chocolate(Drink Obj) { 8 super(Obj); 9 // TODO Auto-generated constructor stub 10 super.setDescription("Chocolate"); 11 super.setPrice(3.0f); 12 } 13 14 }
1 package com.java.jikexueyuan.coffeebar.decorator; 2 3 import com.java.jikexueyuan.coffeebar.Drink; 4 5 public class Milk extends Decorator { 6 7 public Milk(Drink Obj) { 8 super(Obj); 9 // TODO Auto-generated constructor stub 10 super.setDescription("Milk"); 11 super.setPrice(2.0f); 12 } 13 14 }
1 package com.java.jikexueyuan.coffeebar.decorator; 2 3 import com.java.jikexueyuan.coffeebar.Drink; 4 5 public class Soy extends Decorator { 6 7 public Soy(Drink Obj) { 8 super(Obj); 9 // TODO Auto-generated constructor stub 10 super.setDescription("Soy"); 11 super.setPrice(1.5f); 12 } 13 14 }
测试:
1 package com.java.jikexueyuan.coffeebar; 2 3 import com.java.jikexueyuan.coffeebar.coffee.Decaf; 4 import com.java.jikexueyuan.coffeebar.coffee.LongBlack; 5 import com.java.jikexueyuan.coffeebar.decorator.Chocolate; 6 import com.java.jikexueyuan.coffeebar.decorator.Milk; 7 8 public class CoffeeBar { 9 10 11 public static void main(String[] args) { 12 13 Drink order; 14 order=new Decaf(); 15 System.out.println("order1 price:"+order.cost()); 16 System.out.println("order1 desc:"+order.getDescription()); 17 18 System.out.println("****************"); 19 order=new LongBlack(); 20 order=new Milk(order); 21 order=new Chocolate(order); 22 order=new Chocolate(order); 23 System.out.println("order2 price:"+order.cost()); 24 System.out.println("order2 desc:"+order.getDescription()); 25 26 } 27 28 29 }
order1 price:3.0 order1 desc:Decaf-3.0 **************** order2 price:14.0 order2 desc:Chocolate-3.0&&Chocolate-3.0&&Milk-2.0&&LongBlack-6.0
Java内置装饰者:
2、编写自己的Java I/O装饰者:
1 package com.java.jikexueyuan.myiodecorator; 2 3 import java.io.FilterInputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 7 public class UpperCaseInputStream extends FilterInputStream{ 8 9 protected UpperCaseInputStream(InputStream in) { 10 super(in); 11 // TODO Auto-generated constructor stub 12 } 13 14 public int read() throws IOException 15 { 16 int c=super.read(); 17 return c==-1?c:Character.toUpperCase((char)(c)); 18 } 19 public int read(byte[] b,int offset,int len) throws IOException 20 { 21 int result=super.read(b,offset,len); 22 for(int i=0;i<result;i++) 23 { 24 b[i]=(byte)Character.toUpperCase((char)(b[i])); 25 } 26 27 return result; 28 } 29 }
1 package com.java.jikexueyuan.myiodecorator; 2 3 import java.io.BufferedInputStream; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.IOException; 7 import java.io.InputStream; 8 9 public class InputTest { 10 public static void main(String[] args) { 11 int c; 12 try { 13 InputStream in = new UpperCaseInputStream(new BufferedInputStream( 14 new FileInputStream("D:\\test\\test.txt"))); 15 while((c=in.read())>=0) 16 { 17 System.out.print((char)c); 18 19 } 20 } catch (IOException e) { 21 // TODO Auto-generated catch block 22 e.printStackTrace(); 23 } 24 25 } 26 }
ZHE SHI CE SHI WEN JIAN !
注意:
一个装饰者类的接口必须与被装饰者的接口一致;
尽量保持具体装饰者类作为一个“轻”类,也就是说不要把太多的逻辑和状态放到具体装饰者类去实现;
优先使用组合、聚合原则,少用继承。
(参考:http://www.cnblogs.com/chenxing818/p/4705919.html或http://blog.csdn.net/jason0539/article/details/22713711)
优点:
装饰模式与类继承的目的都是扩展对象的功能,但是装饰模式可以提供比类继承更多的灵活性;
通过使用不同的具体装饰类以及这些装饰类的排列组合,开发者可以创造出很多不同行为的组合;
缺点:
这种比类继承更加灵活机动的特性,也同时意味着装饰模式比类继承更容易出错;
使用装饰模式增加了代码的复杂度。
应用场景:
1、在不影响其它对象情况下,以动态透明的方式给单个对象添加职责;
2、需要动态给一个对象添加功能,这些功能可以在动态的被撤销;
3、当不能采用类继承的方式进行扩展时。
一种情况是可能有大量独立的扩展,每一种组合将产生大量的子类,使得子类数量呈爆炸性增长;
另一种情况可以是因为类定义不能继承(final)或不能用于生成子类。