装饰者模式的概念
装饰者模式的基本形式:把一个类对象的引用作为参数传入另一个类的对象的构造方法中,然后以此类推,形成一条引用链,实现对象的多层包装,实现了把有限的类包装成众多有复杂功能的类,实现更多丰富的功能。
Java中的例子:在Java中比较常见的装饰者模式是Java的util包中的IO类库,比如IO字节流的超类InputSream可以包装为文件字节流FileInputStream,缓冲输入流BufferedInputStream等多种类,实现一些特殊的功能。
装饰者模式的原则
类应该对扩展开放,对修改关闭:对扩展开放和对修改关闭这个概念看似矛盾,其实并不矛盾,对扩展开放是指,代码有适应未来在使用中不断更新和变化的能力;对修改关闭是指,Java代码在编写完之后就应该尽量避免修改,因为一旦修改源代码,可能会对原本的代码结构造成破坏,之前辛辛苦苦调的bug可能也前功尽弃了。在编写代码的时候,我们应该尽量避免写“硬代码”,把那些稳定的代码保护起来,把需要修改的部分独立出来。代码的扩展性可以通过后期增删独立出来的类实现,运用装饰者模式的设计理念,可以在外部实现不同的包装层次,获得不同的功能和属性,这样代码的扩展性很强,在实现不同包装的时候也没有修改代码。
咖啡厅点餐(装饰者模式)
写一个咖啡厅的点餐代码。
实现的功能:
1、可以选择饮料的种类和大、中、小杯。
2、可以选择添加的配料及其数量。
3、输出具体点单信息和总价格。
一、饮料(抽象类):
public abstract class Drink {
public abstract String getName();
public abstract void setName(String name);
public abstract int getPrice();
public abstract void setPrice(int price);
}
二、配料(抽象类):
public abstract class Decorator extends Drink {
public abstract String getName();
public abstract void setName(String name);
public abstract int getPrice();
public abstract void setPrice(int price);
}
三、咖啡类(一种饮料):
public class Coffee extends Drink{
private String name;
private int price;
private String size;
public Coffee(String size){
this.size=size;
name="咖啡加";
price=20;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return size+name;
}
public void setPrice(int price){
this.price=price;
}
public int getPrice(){
return price;
}
public void setSize(String size){
this.size=size;
}
public String getSize(){
return size;
}
}
四、摩卡(一种配料):
public class Mocha extends Decorator{
Drink drink;
private String name;
private int price;
private String count;
public Mocha(Drink drink,String count){
this.drink=drink;
this.count=count;
name="摩卡";
price=5;
}
public Mocha(Drink drink){
this.drink=drink;
this.count="";
name="摩卡";
price=5;
}
public String getName() {
return drink.getName()+count+name;
}
public void setName(String name) {
this.name=name;
}
public int getPrice() {
return drink.getPrice()+price;
}
public void setPrice(int price) {
this.price=price;
}
}
五、糖(一种配料):
public class Sugar extends Decorator{
Drink drink;
private String name;
private int price;
private String count;
public Sugar(Drink drink,String count){
this.count=count;
this.drink=drink;
name="糖";
price=3;
}
public Sugar(Drink drink){
this.count="";
this.drink=drink;
name="糖";
price=3;
}
public String getName() {
return drink.getName()+count+name;
}
public void setName(String name) {
this.name=name;
}
public int getPrice() {
return drink.getPrice()+price;
}
public void setPrice(int price) {
this.price=price;
}
}
六、奶泡(一种配料):
public class Whip extends Decorator{
Drink drink;
private String name;
private int price;
private String count;
public Whip(Drink drink,String count){
this.count=count;
this.drink=drink;
name="奶泡";
price=7;
}
public Whip(Drink drink){
this.count="";
this.drink=drink;
name="奶泡";
price=7;
}
public String getName() {
return drink.getName()+count+name;
}
public void setName(String name) {
this.name=name;
}
public int getPrice() {
return drink.getPrice()+price;
}
public void setPrice(int price) {
this.price=price;
}
}
七、运行结果:
第一杯饮料
点了一杯:小杯咖啡加少糖摩卡
价格是:28¥
第二杯饮料
点了一杯:中杯咖啡加奶泡双倍摩卡半糖
价格是:35¥
八、程序分析:
1、实现效果:所有信息都在测试类中输入,可以根据任意的组合配出不同口味的饮料,输出了点单信息和价格。
2、包装的实现:通过对饮料类对象进行多层的配料对象包装,实现给出外层对象引用,得到所有对象的信息并组合输出。内存对象的引用通过外层对象的构造方法传入外层对象。
3、引用链:例如调用最外层对象的getPrice()方法,同时会调用其内一层对象的getPrice()方法,最终实现多层price的叠加。
4、继承:饮料都继承自饮料的抽象类,配料都继承自配料的抽象类,为了实现在包装时的自动转型,让配料类继承饮料类。
小结
装饰者模式使得基础类的任意组合方案得到实现,使得一个最终得到的对象集成了别的一些类的属性和方法,实现更加复杂的功能。装饰者模式的设计遵守了OO设计原则,在对源代码不修改的情况下实现了丰富的扩展功能。但它也有一些自身固有的不足,因为使用到了继承,限制了程序设计的灵活性,如果想要继承别的类,可能需要重新设计新的方案了。对象的包装确实可以实现功能和属性的复合,但是容易因为内容繁杂而不利于理解。