Java设计模式学习记录-装饰模式
前言
装饰模式也是一种结构型模式,主要是目的是相对于类与类之间的继承关系来说,使用装饰模式可以降低耦合度。JDK中有不少地方都使用到了装饰模式,例如Java的各种I/O流,javax.swing包中一些图形界面构件功能的增强等地方都运用了装饰模式。
装饰模式
定义
装饰模式的定义是:在不改变原类文件以及不使用继承的情况下,动态的扩展一个对象的功能。装饰模式是通过创建一个包装对象来实现的,也就是用装饰来包裹真实的对象。
举例
还是老规矩,举例说明,在给亲朋好友过生日时会买生日蛋糕,然后生日蛋糕又有各种各样的辅料来进行装饰,例如:奶油,水果,芝士,巧克力等等。如果没有这些辅料来进行装饰,就是普通的鸡蛋糕。所以我们可以把做蛋糕的这个过程来用代码实现出来:
首先定义蛋糕接口
/**
* 蛋糕
*/
public interface CakeGoods {
/**
* 展示蛋糕
*/
void showCake();
/**
* 展示价格
*/
BigDecimal showPrice();
}
具体的鸡蛋糕
/**
* 鸡蛋糕
*/
@Data
public class SpongeCake implements CakeGoods{
//蛋糕名称
private String name;
//蛋糕价格
private BigDecimal price;
public SpongeCake(String name,BigDecimal price){
this.name = name;
this.price = price;
}
/**
* 展示蛋糕
*/
@Override
public void showCake() {
System.out.println("的"+this.name+"蛋糕");
}
/**
* 展示价格
*/
@Override
public BigDecimal showPrice() {
return this.price;
}
}
辅料的抽象类
/**
* 辅料的抽象类
*/
public abstract class Decorator implements CakeGoods{
private CakeGoods cakeGoods;
public void setCakeGoods(CakeGoods cakeGoods){
this.cakeGoods = cakeGoods;
}
/**
* 展示蛋糕
*/
@Override
public void showCake() {
cakeGoods.showCake();
}
/**
* 展示价格
*/
@Override
public BigDecimal showPrice() {
return cakeGoods.showPrice();
}
}
水果
/**
* 水果
*/
public class Fruits extends Decorator {
/**
* 展示蛋糕
*/
@Override
public void showCake() {
System.out.print("加水果");
super.showCake();
}
/**
* 展示价格
*/
@Override
public BigDecimal showPrice() {
return new BigDecimal(20.00).add(super.showPrice());
}
}
奶油
/**
* 奶油
*/
public class Cream extends Decorator {
/**
* 展示蛋糕
*/
@Override
public void showCake() {
System.out.print("加奶油");
super.showCake();
}
/**
* 展示价格
*/
@Override
public BigDecimal showPrice() {
return new BigDecimal(15.00).add(super.showPrice());
}
}
测试
public class Tests {
public static void main(String[] args) {
//制造鸡蛋糕
CakeGoods cakeGoods = new SpongeCake("生日祝福",new BigDecimal(50));
Decorator cream = new Cream();
Decorator fruits = new Fruits();
//加奶油
cream.setCakeGoods(fruits);
//加水果
fruits.setCakeGoods(cakeGoods);
//展示
cream.showCake();
System.out.println("的价格是:"+cream.showPrice()+"元");
}
}
运行结果:
加奶油加水果的生日祝福蛋糕
的价格是:85元
上面的这个蛋糕制造的例子使用的就是装饰模式,在为鸡蛋糕SpongeCake进行扩展的时候并没有影响它原来的类的结构,也没有使用继承的关系,最终却达到了装饰的目的。下面我们来分析一下装饰模式具体是由那几部分组成。
装饰模式的结构
装饰模式的结构图如上所示,主要由以下几个角色组成。
抽象构件角色:(上图的CakeGoods)定义一个抽象接口,以规范准备接受装饰的对象,想当于Java中的IO流里的InputStram/OutputStream和Reader/Writer。
具体的构件角色:(上图的SpongeCake)定义一个将要接受装饰的类,相当于I/O流里面的FileOutputStream和FileInputStream。
装饰角色:(上图的Decorator)定义一个持有抽象构件角色的引用,并定义一个与抽象构件一直的接口。相当于I/O里面的FilterOutputStream和FilterInputStream。
具体的装饰角色:(上图的Fruits和Cream)负责各构件角色对象加上相应的装饰品,相当于I/O流里的BufferedOutputStream、BufferedInputStream。
在使用的时候,必须扩展CakeGoods的功能,但是我们是通过Fruits和Cream来对CakeGoods进行扩展的,所以相对于CakeGoods来说无需知道Decorator的存在,就能扩展功能了。
总结
装饰模式的优点
- 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。例如上面的例子如果想增加一个枣糕或是一个巧克力辅料,无需修改现有代码,只需将枣糕作为CakeGoods的一个子类,以及Decorator的一个子类即可。
- 可以动态的扩展一个对象的功能。
- 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。
- 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。
装饰模式的缺点
- 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。
- 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。
适用场景
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类已定义为不能被继承(如Java语言中的final类)。
加油,给自己打气,克服惰性!!!
想了解更多的设计模式请查看Java设计模式学习记录-GoF设计模式概述。
- android之listview缓存图片(缓存优化)
- 使用ASP.NET实现Model View Presenter(MVP)
- android性能优化1
- 百度地图之收索视野内的建筑物
- Line Counter - Writing a Visual Studio 2005 Add-In
- 百度地图之标注聚会
- How to Add an API to your Web Service
- 「微信小程序」剖析(四):原生的实时DOM转Virtual DOM
- 让你的「微信小程序」运行在Chrome浏览器上,让我们使用WebStorm
- android上拉下拉加载更多数据
- 「微信小程序」剖析(二):框架原理 | 在桌面浏览器上运行的尝试
- Working with Windows Workflow Foundation in ASP.NET
- 微信小程序剖析【下】:运行机制
- android多屏幕分辨率适配
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- SAP Spartacus的url parameter
- 来讲讲你对ThreadLocal的理解
- 用了这个jupyter插件,我已经半个月没打开过excel了
- vue接入腾讯地图(二)【标注&定位实战】
- 图像处理笔记(4)----OpenCV对象追踪
- MySQL 数据恢复
- 【从0到1学习边缘容器系列2】之 边缘应用管理
- 【从0到1学习边缘容器系列-3】应用容灾之边缘自治
- Hacking with iOS: SwiftUI Edition - 里程碑:项目 13 - 15
- HDU 1896 优先队列用法
- 蓝桥杯省内模拟赛C++
- C++ STL (标准模板库) 详细内容讲解
- 蓝桥杯 试题 基础练习 分解质因数
- 蓝桥杯 试题 基础练习 FJ的字符串
- 蓝桥杯 试题 基础练习 龟兔赛跑预测