装饰者模式
在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
装饰者模式特点
- 装饰者和被装饰对象有相同的超类型。
- 你可以用一个或者多个装饰者包装一个对象。
- 既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它。
- 装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。
- 对象可以在任何时候被装饰,所以可以在运行时动态的,不限量的用你喜欢的装饰者来装饰对象。
装饰者模式类图
从上图可以看出,装饰者和被装饰者拥有共同的父类Componet,为了方便后面的扩展,我们加了一个装饰者父类Decorator,让Decorator去继承Componet,然后让众多装饰者去继承Decorator。
应用场景(婚纱照收费)
生活中符合装饰者模式的应用场景其实还挺多,就拿前阵子去拍婚纱照的经历来说吧,婚纱工作人员一般先会跟你整体说,拍什么样的场景,多少套衣服,怎么去收费,然后在不改变原有收费的前提下,会跟你说精修入册数量,每加一张,就加收多少钱。现在我们来把这个分级收费模式抽象一下,假如说,我们拍摄一张照片是40块钱,精修一张40块,入册10块。安插在装饰者模式中,照片本身就成了我们说的被装饰者(ConcreteComponet),而精修和入册则符合我们对装饰者的定义,分别为DecoratorA,DecoratorB。接下来我们来实现一下这个应用场景的类图结构。
照片本身 | 照片入册 | 照片精修 | |
费用 | 40 | 10 | 40 |
场景(婚纱照收费)类图
Format为装饰者父类与WeddingPhoto(被装饰者)共同继承了Photo。IsPhotoAlbum(入册)与PhotoWithPs(精修)为装饰者,共同修饰WeddingPhoto。他们都拥有共同的行为cost(),用于计算出价格,description属性主要用于记录信息,方便查看实现结果,可要可不要。
代码实现
共同抽象父类
package decorate.base;
/**
* 照片父类
* @author vision
*/
public abstract class Photo {
private String description="";
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
/**
* 价格
* @param price
* @return
*/
public abstract Double cost();
}
装饰者抽象父类
package decorate.format;
import decorate.base.Photo;
/**
* Format(照片规格)装饰者,扩展自Photo父类
* @author vision
*/
public abstract class Format extends Photo{
/**
* 描述
* @param description
* @return
*/
public abstract String getDescription();
}
被装饰者(婚纱照)
package decorate;
import decorate.base.Photo;
/**
* 被装饰者:婚纱照
* @author vision
*/
public class WeddingPhoto extends Photo{
public WeddingPhoto() {
setDescription(getDescription()+"WeddingPhoto(婚纱照)");
}
@Override
public Double cost() {
return 40.0;
}
}
装饰者(入册)
package decorate.format;
import decorate.base.Photo;
/**
* 装饰者:照片是否入册
* @author vision
*/
public class IsPhotoAlbum extends Format{
private Photo photo;
public IsPhotoAlbum(Photo photo) {
this.photo=photo;
}
@Override
public String getDescription() {
return photo.getDescription()+"+IsPhotoAlbum(入册)";
}
@Override
public Double cost() {
// TODO Auto-generated method stub
return 10.0+photo.cost();
}
}
装饰者(精修)
package decorate.format;
import decorate.base.Photo;
/**
* 装饰者:照片精修
* @author vision
*/
public class PhotoWithPs extends Format{
private Photo photo;
public PhotoWithPs(Photo photo){
this.photo=photo;
}
@Override
public String getDescription() {
return photo.getDescription()+"+PhotoWithPs(精修)";
}
/**
* 照片精修,每张多加40块
*/
@Override
public Double cost() {
return 40.0+photo.cost();
}
}
测试类
package decorate;
import decorate.base.Photo;
import decorate.format.IsPhotoAlbum;
import decorate.format.PhotoWithPs;
public class TestClass {
public static void main(String[] args) {
Photo weddingPhoto=new WeddingPhoto();
//精修
PhotoWithPs photoWithPs=new PhotoWithPs(weddingPhoto);
System.out.println("最终选择的照片为:"+photoWithPs.getDescription());
System.out.println("最终单价为:"+photoWithPs.cost());
//入册
IsPhotoAlbum isPhotoAlbum=new IsPhotoAlbum(photoWithPs);
System.out.println("最终选择的照片为:"+isPhotoAlbum.getDescription());
System.out.println("最终单价为:"+isPhotoAlbum.cost());
}
}
输出结果
备注
参考资料:《Head First 设计模式》
时间:2016-10-22 09:13
来源:开源中国社区
作者:开源中国OSC
原文链接