设计模式java装饰模式范例_Java设计模式之装饰模式详解
装饰者模式是动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
假设我们有一个需求,是给一家饮料店做一个计算各种饮料价格的功能。听起来很简单,我们创建一个抽象父类Beverages,description用来描述饮料名字,price方法用来计算饮料的价格。
public abstract class Beverages {
private String description;
public String getDescription() {
return description;
}
public abstract double price();
}
每种饮料我们都创建一个子类去继承父类,赋值给description,并且重写price方法来设置自己的价格。看上去很完美,我们运行一下看看结果。
public class Coffee extends Beverages {
public Coffee(String description){
this.description = description;
}
@Override
public String getDescription() {
return description;
}
@Override
public double price() {
return 3.5;
}
}
public static void main(String[] args){
Coffee coffee = new Coffee("咖啡");
System.out.println(coffee.getDescription());
System.out.println(coffee.price());
}
结果:
咖啡
3.5
但是问题来了,饮料店里不仅仅只有一种饮料,还有可乐、七喜、奶茶其他各种各样的饮料,难道我们要每个都创建一个子类吗?好吧,就算你觉得几十种饮料还不算多,那如果各种饮料直接再进行搭配呢?咖啡加牛奶、咖啡加巧克力、加糖、不加糖,因为各种配料的不同价格和描述也不同,我的天呀,难道要创建几百个类吗?这个时候我们突然想到了不是可以用继承来解决这个问题吗?那我们就来试一下。
我们改造一下Beverages类,把要加的配料用boolean值声明,如果需要添加配料就调用set方法设置为true,在price方法中计算的时候来判断哪一些配料添加了需要计算价格。
public class Beverages {
public String description;
public boolean milk;
public boolean sugar;
public double milkPrice = 1.5;
public double sugarPrice = 0.5;
public boolean isMilk() {
return milk;
}
public void setMilk(boolean milk) {
this.milk = milk;
}
public boolean isSugar() {
return sugar;
}
public void setSugar(boolean sugar) {
this.sugar = sugar;
}
public String getDescription() {
return description;
}
public double price(){
double basePrice = 0.0;
if (isMilk()){
basePrice += milkPrice;
}
if (isSugar()){
basePrice += sugarPrice;
}
return basePrice;
}
}
然后我们在子类计算价格的时候加上父类中计算好的配料的价格。
public class CoffeeWithMilk extends Beverages {
public CoffeeWithMilk(String description){
this.description = description;
}
@Override
public double price() {
return 3.0 + super.price();
}
}
我们运行看一下,咖啡加牛奶加糖,结果没有问题。
CoffeeWithMilk coffeeWithMilk = new CoffeeWithMilk("咖啡加牛奶加糖");
coffeeWithMilk.setMilk(true);
coffeeWithMilk.setSugar(true);
System.out.println(coffeeWithMilk.getDescription());
System.out.println(coffeeWithMilk.price());
结果:
咖啡加牛奶加糖
5.0
这样一来我们解决了新建无数个子类的问题,但是我们发现单纯继承的做法还是有太多弊端,比如说如果我们想要新添加配料,我们得修改在父类Beverages中添加新的调料字段,还要修改price方法,这严重违反了开发-关闭的设计原则,类应该对扩展开发,对修改关闭。而且有些饮料和配料是没办法搭配的,例如啤酒加糖,但是子类还是会继承到这些配料,并且如果是要同一份配料要加双份又该怎么改呢?所以单纯继承的方法还是不行。这时候我们就要使用到装饰者模式。
首先我们创建抽象父类Beverages,这个父类只是饮料的父类,不是配料的父类。我们创建一个Cola类直接继承它。
public abstract class Beverages {
public String description;
public String getDescription() {
return description;
}
public abstract double price();
}
public class Cola extends Beverages {
public Cola(String description) {
this.description = description;
}
@Override
public double price() {
return 2.0;
}
}
现在就缺配料的部分的代码了,我们再创建一个配料的抽象类Seasonings,我们让它继承Berverages类,并且声明了一个Berverages的引用和抽象的getDescription方法,这里我们稍后再作解释。
public abstract class Seasonings extends Beverages{
public Beverages beverages;
public abstract String getDescription();
}
接下来我们看看配料的实现类怎么写。我们让他继承了父类Seasonings,构造函数接收父类Beverages类型,其实也就是要被装饰的对象,也就是各种各样需要加配料的饮料。然后我们重写了getDescription,我们在传进来的那个Beverages对象的基础上添加名字,price方法也是同理。这样其实就在给传进来的对象外面做了一层装饰,也就是给饮料添加了配料。
public class Ice extends Seasonings {
public Ice(Beverages beverages) {
this.beverages = beverages;
}
@Override
public String getDescription() {
return beverages.getDescription() + "加冰";
}
@Override
public double price() {
return beverages.price() + 0.2;
}
}
运行一下,看看结果。没有问题,可以搭配成功。
Cola cola = new Cola("可乐");
Beverages ice = new Ice(cola);
System.out.println(ice.getDescription());
System.out.println(ice.price());
结果:
可乐加冰
2.2
我们还想再添加别的配料,再创建一个新的配料。
public class Sugar extends Seasonings {
public Sugar(Beverages beverages) {
this.beverages = beverages;
}
@Override
public String getDescription() {
return beverages.getDescription() + "加糖";
}
@Override
public double price() {
return 0.5 + beverages.price();
}
}
我们在原来搭配好的基础上再进行装饰,结果也是没有问题的。这个时候就要提到为什么我们的Seasonings类要继承Beverages类了,因为每个配料的实现类里有一个Beverages类型的引用,这样我们才可以对它的子类进行装饰,我们让Seasonings也继承Beverages那它的子类也是Beverages类型,我们就可以想装饰几层就装饰几层。
Beverages ice = new Ice(cola);
System.out.println(ice.getDescription());
System.out.println(ice.price());
Beverages sugar = new Sugar(ice);
System.out.println(sugar.getDescription());
System.out.println(sugar.price());
结果:
可乐加冰
2.2
可乐加冰加糖
2.7
在Java的类库中就有很多实际应用到了装饰模式,比如BufferedInputStream就可以用来装饰FileInputStream,提供更加强大的功能。
总结:
装饰模式就是装饰者和被装饰者都具有相同的超类,装饰者拿到被装饰者的引用之后,在调用被装饰者的方法的同时再加上自己的新功能,从而实现了功能的增加,也不需要修改原来的代码。而且因为是相同的超类,所以可以装饰很多层。
设计模式java装饰模式范例_Java设计模式之装饰模式详解相关推荐
- java jtable组件_java中jtable组件详解实例
java中jtable组件详解实例 java 表格控件 JTable 常用操作详解 JTable 是 Swing 编程中很常用的控件,这里总结了一些常用方法以备查阅.欢迎补充,转载请注明作者与出处.一 ...
- java异常例子_java 异常的实例详解
java 异常的实例详解 1.异常的定义:程序在运行时出现不正常情况. 异常的划分: Error:严重的问题,对于error一般不编写针对性的代码对其进行处理. Exception:非严重的问题,对于 ...
- java list用法_java list的用法详解
java list的用法详解 java中可变数组的原理就是不断的创建新的数组,将原数组加到新的数组中.以下是百分网小编搜索整理的关于java list的用法详解,需要的朋友可以参考一下!想了解更多相关 ...
- java 自定义正则表达式_java中正则表达式实例详解
Java中正则表达式运用实例(参看java中正则表达式运用详解): 测试代码 package test; /** * 在String的matches()方法,split()方法中使用正则表达式. * ...
- java aqs原理_Java并发之AQS详解
一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronized(AQS)! 类如其名,抽象的队列式的同步器,AQ ...
- java多线程代码_java多线程实例代码详解
原文:http://blog.csdn.net/paranoidyang/article/details/70184523 作者:Paranoidyang 线程与进程的区别 (1)程序是一段静态的代码 ...
- java file 实例_Java File类的详解及简单实例
Java File类的详解及简单实例 1. File():构造函数,一般是依据文件所在的指定位置来创建文件对象. CanWrite():返回文件是否可写. CanRead():返回文件是否可读. Co ...
- java 枚举 方法_Java枚举使用方法详解
在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的. 例如星期一到星期日七个数据元素组成了一周的"数 ...
- java 返回值_Java方法返回值详解
Java方法返回值详解 每个方法都是为了完成某个特定的功能,例如:登录功能.求和功能等,既然是功能,那么当这个功能完成之后,大多数情况下都会有一个结果的,比如,登录成功了或者失败了(true/fals ...
最新文章
- qzone.class.php,PHP实现QQ空间自动回复说说的方法
- gen_cam_par_area_scan_division和gen_cam_par_area_scan_polynomial区别
- 高端人工智能服务器,产品技术-HPE Apollo6500 Gen10服务器:人工智能的高速引擎-新华三集团-H3C...
- java职业发展路线图_Java程序员如何选择未来的职业路线
- Linux下构建FTP服务器
- 计算机网络通信的仿真,计算机网络虚拟仿真技术研究与应用.doc
- R开发(part6)--pryr包
- Arcgis License的安装及破解
- 编写一个脚本判断某个用户是否处在活动模式_大数据技术之Spark内核解析(二)Spark 部署模式...
- c/c++编译的程序占用的内存分配
- 图片怎么批量修改尺寸
- 8个常用的python办公室自动化技巧
- VSCode格式化JS自动添加或去掉分号
- linux打开nginx配置文件,【linux】systemctl启动nginx没有加载nginx.conf配置文件?
- JavaScript《一》
- android 手机红外遥控器
- word2vec训练中文模型—wiki百科中文库
- [Spring手撸专栏学习笔记]——把AOP动态代理,融入到Bean的生命周期
- SpringBoot实现定时任务的三种方式
- 战神引挚手游数据库解析mysql/mir
热门文章
- Apache Jserv protocol
- rop的noejs客户端
- 新年图书整理和相关的产品
- 数据库连接oracle 10g rman 备份与恢复 之一
- 用Windows live Writer 2012发布51cto博客
- java 调整数组顺序使奇数位于偶数前面
- oracle case when exists()
- Failed to create the Java Virtual Machine
- 不要为框架作过多的假设
- git commit Please tell me who you are it config --global user.email you@example.com