装饰者模式(Decorator)
1.装饰者模式:
就是用来装饰别的类的,可以理解成 加油添醋(或锦上添花····)
就像人一样,人就是原来的类,每一个装饰类就如一件件的衣服,可以根据需求随意组合的穿到人的身上。
例子:
面条,可以是牛肉面、瘦肉面、·····等等,
转换成java代码就是:
面条 --- 抽象类 Noodle;
牛肉面 ---- BeefNoodles;
瘦肉面 ---- MeatNoodles;
```````````这样就会有各种各样的面条的具体类了,
关键不是数量的问题,假如有一个 加鸡蛋的牛肉面呢? 再新建一个 BeefAndEggNoodles?
那么,如果顾客的要求更多一点呢 加蛋加香肠的牛肉面呢·······················
应该怎么办呢,其实我们应该分清楚主次,很明显这里主体是牛肉面/瘦肉面,装饰的是配料(什么鸡蛋,香肠,香菜等·····),
而且这些配料还有可能随意组合的,所以按配料来定义一个面条对象是不理智的。
首先,先创建一个Noodle抽象类,这个没问题吧:
1 package design.patterns.decorator; 2 3 public abstract class Noodle { 4 //描述,说明这是什么面 5 public String description; 6 7 public String getDescription(){ 8 return description; 9 } 10 11 //吃后感 12 public abstract String feeling(); 13 }
然后创建一个继承了Noodle类的 牛肉面类BeefNoodle
(可能有人会问,为什么鸡蛋,香肠这些才是配料,而牛肉不也是加在面里面的吗,牛肉应该也算是配料的一种啊;这里只是相对而言,因为相对于一个香肠,那些牛肉就已经不算是配料了,
又例如蛋糕,巧克力蛋糕、冰激凌蛋糕、还有花瓣以及卡片,相对而言,花瓣和卡片才算是装饰,巧克力和冰激凌则应该认为是蛋糕的一部分了,当然这个标准你可以自己拿捏)
1 package design.patterns.decorator; 2 3 public class BeefNoodle extends Noodle{ 4 public BeefNoodle(){ 5 this.description = "这是一碗牛肉面"; 6 } 7 8 @Override 9 public String feeling() { 10 return "牛肉面就是香。"; 11 } 12 13 }
在还没有加其他配料前就先直接测试一下吧:TestNoodle类
1 package design.patterns.decorator; 2 3 public class TestNoodle { 4 public static void main(String[] args){ 5 //这是普通的牛肉面。 6 Noodle noodle = new BeefNoodle(); 7 System.out.println(noodle.feeling()); 8 } 9 }
结果如下:
重点来了,关键来了,定义一个面条配料类NoodleDecorator :
(因为这只是配料,可以理解为"加了·····配料的·····面",如:"加了 蛋 的 牛肉面",但这种面并不是真实存在的(不像牛肉面那么具体),所以声明为抽象类)
1 package design.patterns.decorator; 2 3 public abstract class NoodleDecorator extends Noodle{ 4 //同时它也是继承了Noodle,跟各种面是同一父类的,所以在多态上会更便利 5 //这一步是关键,使用了组合,把"被装饰者"传进来让"装饰者"进行"装饰(包装,所谓的加油添醋)" 6 public Noodle noodle; 7 8 public NoodleDecorator(Noodle n){ 9 noodle = n; 10 } 11 @Override 12 public abstract String feeling() ; 13 }
然后创建具体的装饰类,加蛋的配料面条类 AddEgg:
1 package design.patterns.decorator; 2 3 public class AddEgg extends NoodleDecorator{ 4 5 public AddEgg(Noodle n) { 6 super(n); 7 } 8 9 @Override 10 public String feeling() { 11 return this.noodle.getDescription() +" +加了蛋才是最美味。"; 12 } 13 14 }
再创建一个 加了香肠的类 AddSuasage:
1 package design.patterns.decorator; 2 3 public class AddSausage extends NoodleDecorator{ 4 5 public AddSausage(Noodle n) { 6 super(n); 7 } 8 9 @Override 10 public String feeling() { 11 return this.noodle.feeling() +" +加一根香肠后,更加回味无穷。"; 12 } 13 14 }
好了,现在来开始装饰吧:
1 package design.patterns.decorator; 2 3 public class TestNoodle { 4 public static void main(String[] args){ 5 //这是普通的牛肉面。 6 Noodle noodle = new BeefNoodle(); 7 System.out.println(noodle.feeling()); 8 9 //老板,给我来晚加蛋的牛肉面 10 Noodle n1 = new AddEgg(new BeefNoodle()); 11 System.out.println(n1.feeling()); 12 13 //老板,给我来晚加香肠的牛肉面 14 Noodle n2 = new AddSausage(new BeefNoodle()); 15 System.out.println(n2.feeling()); 16 17 //加蛋 又加香肠的 牛肉面 18 Noodle n3 = new AddSausage(new AddEgg(new BeefNoodle())); 19 System.out.println(n3.feeling()); 20 } 21 }
如果这个测试类看着不爽,那就看这个吧:
1 package design.patterns.decorator; 2 3 public class TestNoodle { 4 public static void main(String[] args){ 5 //这是普通的牛肉面。 6 Noodle noodle = new BeefNoodle(); 7 System.out.println(noodle.feeling()); 8 9 //没蛋不好吃,在牛肉面的基础上加个蛋 10 noodle = new AddEgg(noodle); 11 System.out.println(noodle.feeling()); 12 13 //既然蛋都加了,再加跟香肠吧 14 noodle = new AddSausage(noodle); 15 System.out.println(noodle.feeling()); 16 } 17 }
结果如下图:
总结一下:1. 什么时候用装饰者模式,当类的属性中存在很明显的主次关系,同时这些属性还有可能随意组合,就把那些作为装饰的属性以组合的方式动态添加到类中
2. 在Java的IO中,最常见的装饰者模式就是 BufferedInputStream bin = new BufferedInputStream(InputStream);
这里,buffer也是以组合的方式"装饰(添加到)"普通的inputStream中的
3. 缺点,所有的设计模式都共有的,类的数量增加了,每多一个装饰功能,就得新增一个装饰类;同时逻辑上复杂了不少,理解有一定难度。