1 装饰者模式的定义和特点
1.1什么是装饰者模式
书中解释: 动态的将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种选择。
一句化解释: 不改变对象的情况下,动态对对象功能进行扩展。完美的诠释了开闭原则。
1.2 装饰者模式特点
被装饰者抽象角色: 被装饰者有那些功能
被装饰者角色: 被装饰者功能的具体实现
装饰者角色: 装饰 被装饰者的角色 装饰者持有被装饰者抽象角色。
3 装饰者模式要点和设计原则
3.1设计原则
多用组合少用继承
针对接口编程,不针对实现编程
类应该对外扩展开放,对修改关闭 (开闭原则)
3.2 装饰者模式要点
- 继承可以进行扩展,但不是最佳弹性设计的方式
- 应该允许行为可以扩展,而且无需修改现有的代码
- 组合和委托可以在运行时加上新的行为
- 装饰是除了继承的另外一种扩展方式
- 装饰者就是一群装饰类去包装一个组件
- 装饰者和被装饰着应该是相同类型
- 装饰者可以对被装饰者添加自己的行为
- 装饰者会导致设计中出现很多小对象,如果多度使用,会让程序变得很复杂
2 书中代码优化案例演进
书中通过介绍星巴克 各种饮料和调料组成不同的新饮料和价格的变化的案例来一步步演示如何优化成装饰者模式的方案。
2.1第一个版本代码和类图
第一版本代码中不同的调料配合我们可以组成上千中饮料 我们需要写上千个类,写着写着你就疯了
类图
具体的代码
饮料的抽象类
package cn.zhuoqianmingyue.decorator.first;
/**
* 饮料 抽象类
*/
public abstract class Beverage {
private String description;
public String getDescription(){
return description;
}
public void setDescription(String description){
this.description = description;
}
public abstract double cost();
}
具体的饮料类 脱脂蒸奶
package cn.zhuoqianmingyue.decorator.first;
/**
* 脱脂蒸奶
*/
public class DeacfSteamedMilk extends Beverage{
public DeacfSteamedMilk(){
setDescription("脱脂蒸奶");
}
@Override
public double cost() {
return 30d;
}
}
具体的饮料类 脂豆浆
package cn.zhuoqianmingyue.decorator.first;
/**
* 脱脂豆浆
*/
public class DecafWithSoy extends Beverage{
public DecafWithSoy(){
setDescription("脱脂豆浆");
}
@Override
public double cost() {
return 15d;
}
}
测试类
package cn.zhuoqianmingyue.decorator.first;
/**
* 不同的调料配合我们可以组成上千中饮料 我们需要写上千个类,写着写着你就疯了
*/
public class Application {
public static void main(String[] args){
Beverage deacfSteamedMilk = new DeacfSteamedMilk();
double cost = deacfSteamedMilk.cost();
System.out.println(deacfSteamedMilk.getDescription()+"需要"+cost);
Beverage decafWithSoy = new DecafWithSoy();
double cost2 = decafWithSoy.cost();
System.out.println(decafWithSoy.getDescription()+"需要"+cost2);
}
}
2.2第2个版本代码和类图
第2版本代码虽然大大减少类的数量但是不能解决下面的问题:
- 这个方式改变调料的价钱需要我们修改Beverage类中的源代码
- 一旦添加新的调料要在修改Beverage的cost方法
- 添加新的调料并不适合新的饮料
- 无法满足客户想要加双倍的摩卡
类图
具体的代码
饮料的抽象类
package cn.zhuoqianmingyue.decorator.second;
/**
* 饮料 抽象类
*/
public abstract class Beverage {
private String description;
private Boolean milk =false;//是否加奶
private Boolean soy =false;//是否加豆浆
private Boolean mocha =false;//是否夹摩卡
private Boolean whip =false;//是否夹奶泡
double milkCost = 3d;
double soyCost = 4d;
double mochaCost =5d;
double whipCost =6d;
public String getDescription(){
return description;
}
public void setDescription(String description){
this.description = description;
}
public double cost(){
double condimentCost = 0.0d;
if(hasMilk()){
condimentCost += milkCost;
}
if(hasSoy()){
condimentCost += soyCost;
}
if(hasMocha()){
condimentCost += mochaCost;
}
if(hasWhip()){
condimentCost += whipCost;
}
return condimentCost;
}
public Boolean hasMilk() {
return milk;
}
public void setMilk(Boolean milk) {
this.milk = milk;
}
public Boolean hasSoy() {
return soy;
}
public void setSoy(Boolean soy) {
this.soy = soy;
}
public Boolean hasMocha() {
return mocha;
}
public void setMocha(Boolean mocha) {
this.mocha = mocha;
}
public Boolean hasWhip() {
return whip;
}
public void setWhip(Boolean whip) {
this.whip = whip;
}
}
具体的饮料类
package cn.zhuoqianmingyue.decorator.second;
public class DarkRoast extends Beverage{
@Override
public double cost() {
return super.cost()+1.99d;
}
}
package cn.zhuoqianmingyue.decorator.second;
public class Decaf extends Beverage {
@Override
public double cost() {
return super.cost()+2.99d;
}
}
具体的饮料类
package cn.zhuoqianmingyue.decorator.second;
public class HouseBleand extends Beverage{
@Override
public double cost() {
return super.cost()+3.99d;
}
}
测试类
package cn.zhuoqianmingyue.decorator.second;
/**
* 这个方式改变调料的价钱需要我们修改Beverage类中的源代码
* 一旦添加新的调料要在修改Beverage的cost方法
* 添加新的调料并不适合新的饮料
* 无法满足客户想要加双倍的摩卡
*/
public class Application {
public static void main(String[] args){
Beverage darkRoastarkRoast = new DarkRoast();
darkRoastarkRoast.setMilk(true);
darkRoastarkRoast.setSoy(true);
double cost = darkRoastarkRoast.cost();
System.out.println(cost);
}
}
2.3第3个版本代码和类图
这个版本采用装饰者模式。
类图
具体的代码
饮料的抽象类
package cn.zhuoqianmingyue.decorator.third;
public abstract class Beverage {
String description = "不知道是什么饮料";
public String getDescription(){
return description;
}
public abstract double cost();
}
调料的抽象类
package cn.zhuoqianmingyue.decorator.third;
/**
* 调料的抽象类
*/
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
被装饰的饮料类: 浓缩咖啡
package cn.zhuoqianmingyue.decorator.third;
public class Espresso extends Beverage {
public Espresso(){
description = "浓缩咖啡";
}
@Override
public double cost() {
return 1.99d;
}
}
被装饰的饮料类: 综合咖啡
package cn.zhuoqianmingyue.decorator.third;
public class HouseBlend extends Beverage {
public HouseBlend(){
description = "综合咖啡";
}
@Override
public double cost() {
return 0.89d;
}
}
装饰调料类: 加摩卡
package cn.zhuoqianmingyue.decorator.third;
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription()+"加摩卡";
}
@Override
public double cost() {
return 0.20+beverage.cost();
}
}
装饰调料类: 加奶泡
package cn.zhuoqianmingyue.decorator.third;
public class Whip extends CondimentDecorator {
Beverage beverage;
public Whip(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription()+"加奶泡";
}
@Override
public double cost() {
return beverage.cost()+ 0.5d;
}
}
测试类
package cn.zhuoqianmingyue.decorator.third;
public class Application {
public static void main(String[] args){
Beverage espresso = new Espresso();
System.out.println(espresso.getDescription() + "$ " + espresso.cost());
Beverage houseBlend = new HouseBlend();
houseBlend = new Mocha(houseBlend);
System.out.println(houseBlend.getDescription() + "$ " + houseBlend.cost());
Beverage houseBlend2 = new HouseBlend();
houseBlend2 = new Whip(houseBlend);
System.out.println(houseBlend2.getDescription() + "$ " + houseBlend2.cost());
}
}