在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
从上图可以看出,装饰者和被装饰者拥有共同的父类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 设计模式》
相关文章: 《设计模式——观察者模式:天气推送的两种实现》
《设计模式——策略模式:会员价格体系的简单实现 》