放弃该放弃的是无奈,放弃不该放弃的是无能,不放弃该放弃的是无知,不放弃不该放弃的是执着。
愿自己能在自己所热爱的道路上越走越远。
在学习装饰者模式前,我们应该先对其有个大概的了解:
装饰模式的设计理念主要是以对客户端透明的方式动态扩展对象的功能。 装饰模式可以在不创造更多子类的情况下,将对象的功能加以扩展。 我们要记住装饰模式的关键在于这种扩展完全是透明的,装饰模式的透明性要求客户端程序不应该将对象声明为具体的类对象或者具体的装饰类型,而应该全部声明为抽象类型.
被装饰的类:
Drink 我们现在对饮料的价格计算进行扩展,装饰类是饮料的调料.
- 被装饰类的顶层接口
这里不是一定非的使用抽象类,也可以使用接口,接口只能有一个行为,所有Drink的属性应该自己在被装饰类的具体实现类里面进行声明, 而这里我们使用抽象类的处就是价格和描述信息只需要声明一次.
@Data
@ToString
public abstract class Drink {
/*
* --------------------------------------------------------
* 当前抽象类表示Drink 饮料.
* --------------------------------------------------------
* */
/**
* 当前饮料的描述信息.
*/
private String message;
/**
* 当前饮料的价格.
*/
private int price;
/**
* 当前饮料总共消费多少.
* @return
*/
public abstract int cost();
}
Drink饮料下Coffee类型的饮料.
/**
* @author ZhenXinMa
* @date 2020/8/28 20:40
*/
public class Coffee extends Drink {
/*
* 由于Coffee继承了其Coffee所表示的抽象类: 其字节码文件中应该包含该Coffee中的描述信息和价格.
* */
@Override
public int cost() {
// 计算Coffee当前的价格:
return super.getPrice();
}
}
这里面是coffee下面的几种具体饮料的实现:
黑咖啡的具体实现:
/**
* @author ZhenXinMa
* @date 2020/8/28 20:43
*/
public class LongBlack extends Coffee {
public LongBlack(){
// 这里写不写super(); 应该都一样 JVM默认将会调用父类的构造器.
super.setMessage("黑咖啡.");
super.setPrice(10);
}
}
浓缩咖啡的实现:
public class Espresson extends Coffee {
public Espresson(){
super.setMessage("浓缩咖啡. ");
super.setPrice(20);
}
}
装饰类: 对要被装饰的类进行装饰
/**
* 装饰者类其实就是对要被装饰类的一个装饰物件: 调料装饰品.
* 但是为了说明该调料装饰品是对于那个物件的装饰,那么我们抽象出了被装饰物件的一个接口(或者一个顶层接口(由于是抽象出一个物件的接口,所以说最好
* 是使用抽象类.)) 然后对该类抽象物件的要被装饰的行为 而该行为交给其子类进行实现.
* <p>
* 装饰者类需要继承被装饰类的顶层接口.然后实现其默认行为并对其进行装饰. 我们既然是对其进行装饰,那么在该类中我们需要引入一个顶层接口所属的引用,
* 来调用其默认的行为,我们可以在装饰者类通过其重写的方法来对其进行装饰.
*
* @author ZhenXinMa
* @date 2020/8/28 20:48
*/
@Data
@ToString
public class FlavourDecorater extends Drink {
private Drink drink;
public FlavourDecorater(Drink drink) {
this.drink = drink;
}
@Override
public int cost() {
// 返回的价格是 当前调料的价格 + 饮料的价格.
return super.getPrice() + drink.getPrice();
}
@Override
public String getMessage() {
return super.getMessage() + "::" + super.getPrice() + "::" + drink.getMessage();
}
}
下面是装饰类的具体实现:
public class Chocolate extends FlavourDecorater {
/*
* 现在需要给该巧克力调味品设置一些就基本属性.
* */
public Chocolate(Drink drink){
// 如果自己在类中写了构造函数,那么JVM就不回自动生成空的构造函数,
// 所以说在子类中应该明显的调用非空构造函数.
super(drink);
super.setMessage("巧克力调味品.");
super.setPrice(10);
}
}
主测试:
public static void main(String[] args) {
// 现在需要买一各饮料 并且加一份巧克力
// 我现在买了一份黑咖啡》
Drink drink = new LongBlack();
// 我现在给该饮料添加了一份巧克力:添加了之后又重新返回》
// 装饰者模式就是以对客户端透明的方式来扩展功能.
// 这么来理解饮料: 我买的饮料之后,我获取了一个饮料对象,但是我要在该饮料上添加调料,那么添加了调料之后 我应该继续返回我的饮料.
// 但是这个时候返回来的饮料是已经添加了调料的饮料.
// 客户端(我们在测试)在添加调料的时候,不需要知道具体操作, 我只需要在该原有对象上进行扩展功能. 而装饰者模式扩展功能是对客户端进行
// 透明的. 也就是说客户端时是不知道具体颞部操作的.
// 这里所说明的透明是 声明的对象是的顶层接口.
drink = new Chocolate(drink);
// 计算画的总费用.
System.out.println(drink.cost());
}
装饰模式的总结:
一、装饰模式的几个核心类
- 被装饰类顶层抽象接口(在该实例中被装饰类的顶层抽象接口是Drink饮料) 该抽象接口是被装饰的顶层接口,是必须的.
- 然后就是被装饰者类的具体实现,在该类下可以继续划分,也可以不进行划分. 所有的被装饰着(也就是所有的饮料)必须继承Drink类接口.
- 在中间说明一点:被装饰类的属性可以自己定义,但是一定要有一个被装饰的行为, 从侧面说,被装饰类的顶层接口可以是抽象类,也可以是接口.
- 最重要的核心类是装饰类:该类需要说明当前装饰类是针对哪一类商品进行装饰,所以说该装饰类应该继承(或实现)被装饰类的顶层接口. 然后通过重写要被装饰的行为,并且该类还需要聚合一个被装饰的类Drink类,通过调用Drink类来获取Drink的默认实现,并且换可以对该默认实现进行装饰.
- 最后就是对装饰类的具体实现,并且对装饰类的具体实现不需要做太多的事情,只需要在其构造函数中注入Drink,并且其传递到父类的构造函数.