Component:组件(主体)
concreteComponent:被装饰者
Decorator:装饰者
注意:concreteComponent、Decorator都会实现或继承Component
角色
Client 类:装饰模式的调用者
Component:主体===》饮料(Drink)
concreteComponent:被装饰者===》咖啡(Coffee)
xxxConcreteComponent:具体的被装饰者(ChinaCoffee) 美式咖啡、欧式咖啡、中式咖啡
Decorator:装饰器===》调料
xxxDecorator:子装饰器 糖类、奶酪、牛油、芥末
案例
单体咖啡与调味组合的饮料计价项目
使用前
这个项目最容易想到的设计方案就是采用继承的设计方案,设计思路如下:
Drink===》饮品
Juice===》果汁
.......
Coffee===》咖啡
ChinaCoffee===》中式咖啡
ASeasoningChinaCoffee===》被A调料修饰的中式咖啡
BSeasoningChinaCoffee===》被B调料修饰的中式咖啡
......
XxxCoffee===》其它咖啡
ASeasoningXxxCoffee===》被A调料修饰的其它咖啡
BSeasoningXxxCoffee===》被B调料修饰的其它咖啡
......
上面每个类中都有getPrice计价的功能,从结果上来看,确实可以完成项目需求,但是整个设计体系过于臃肿,不便于后期的扩展与维护;
用前面的桥接模式来进行设计,可以解决体系臃肿问题
package com.javaxl.design.decorator.before;
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 18:27
*
* 饮料包括单体咖啡+调料
*/
public abstract class Drink {
protected double price;
protected int n;
protected DrinkSeasoning seasoning;
public abstract double getPrice();
}
/**
* 单体咖啡
*/
abstract class Coffee extends Drink {
}
/**
* 单体果汁
*/
abstract class Juice extends Drink {
}
class ChinaCoffee extends Coffee{
ChinaCoffee(double price,int n){
this.price = price;
this.n = n;
}
@Override
public double getPrice() {
return this.price*this.n+this.seasoning.getPrice();
}
}
package com.javaxl.design.decorator.before;
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 18:32
*
* 调料的抽象接口
*/
public interface DrinkSeasoning {
public abstract double getPrice();
}
/**
* A类调料
*/
class ADrinkSeasoning implements DrinkSeasoning{
protected double price;
protected int n;
ADrinkSeasoning(double price,int n){
this.price = price;
this.n = n;
}
@Override
public double getPrice() {
return this.price*this.n;
}
}
/**
* B类调料
*/
class BDrinkSeasoning implements DrinkSeasoning{
private double price;
protected int n;
BDrinkSeasoning(double price,int n){
this.price = price;
this.n = n;
}
@Override
public double getPrice() {
return this.price*this.n;
}
}
public class Client {
public static void main(String[] args) {
ChinaCoffee chinaCoffee = new ChinaCoffee(6,1);
ADrinkSeasoning aDrinkSeasoning = new ADrinkSeasoning(2,2);
chinaCoffee.seasoning = aDrinkSeasoning;
System.out.println("中式咖啡1份+A调料2份,最终价格为:"+chinaCoffee.getPrice());
// 思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢?
// 思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢?
}
}
上面的代码存在问题:
思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢? 思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢?
思考3:Drink饮品单体饮料种类多,调料种类也多,会带来什么问题?
Drink抽象类中聚合List<DrinkSeasoning>才可以解决上述的前两个问题;但是,在原有订单追加调料,相当于给原有对象进行装饰,这类的问题更加适合用装饰模式来解决;
使用后
使用装饰模式进行设计
package com.javaxl.design.decorator.after;
import com.javaxl.design.decorator.before.DrinkSeasoning;
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 18:27
* <p>
* 饮料包括单体咖啡+调料
*/
public abstract class Drink {
protected double price;
protected int n;
public abstract double getPrice();
}
/**
* 单体咖啡
*/
abstract class Coffee extends Drink {
}
/**
* 单体果汁
*/
abstract class Juice extends Drink {
}
class ChinaCoffee extends Coffee {
ChinaCoffee(double price, int n) {
this.price = price;
this.n = n;
}
@Override
public double getPrice() {
return this.price * this.n;
}
}
package com.javaxl.design.decorator.after;
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 22:26
*/
public class DecoratorDrink extends Drink {
private Drink drink;
public DecoratorDrink(Drink drink, double price, int n) {
this.drink = drink;
this.price = price;
this.n = n;
}
@Override
public double getPrice() {
return this.price * this.n + drink.getPrice();
}
}
class ADecoratorDrink extends DecoratorDrink {
public ADecoratorDrink(Drink drink, double price, int n) {
super(drink, price, n);
}
}
class BDecoratorDrink extends DecoratorDrink {
public BDecoratorDrink(Drink drink, double price, int n) {
super(drink, price, n);
}
}
package com.javaxl.design.decorator.after;
/**
* @author 小李飞刀
* @site www.javaxl.com
* @company
* @create 2020-02-22 18:50
*/
public class Client {
public static void main(String[] args) {
ChinaCoffee chinaCoffee = new ChinaCoffee(6,1);
// 假定A类调料2元一份,B类调料3元一份
Drink order = new ADecoratorDrink(chinaCoffee, 2, 2);
System.out.println("中式咖啡1份+A调料2份,最终价格为:"+order.getPrice());
// 思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢?
order = new ADecoratorDrink(order,2,1);
System.out.println("式咖啡1份+A调料3份,最终价格为:"+order.getPrice());
order = new BDecoratorDrink(order,3,2);
System.out.println("式咖啡1份+A调料3份+B调料2份,最终价格为:"+order.getPrice());
// 思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢?
order = new BDecoratorDrink(order,3,2);
System.out.println("式咖啡1份+A调料3份+B调料4份,最终价格为:"+order.getPrice());
}
}
注意事项及细节
装饰者模式一般用于对原有功能进行增强/装饰
动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性
应用
IO流体系:缓冲流
总结
继承保证了父类和子类的一致性(有共同的方法),委托保证了使用委托的类和被委托对象的一致性。可以看到装饰模式中,保证了装饰边框与被装饰物体的一致性(有共同父类),使用了模板方法,这个方法几乎无处不在呀,同样使用了委托(组合),通过在原始数据上面一层层的包裹,最终得到了我们想要的输出,有着非常广泛的用处。
over......
备案号:湘ICP备19000029号
Copyright © 2018-2019 javaxl晓码阁 版权所有