設計模式:裝飾器模式
阿新 • • 發佈:2020-12-26
裝飾器模式概述
裝飾器模式,也稱為包裝器模式,指在不改變原有物件的基礎上,動態給一個物件新增一個額外的職責。
場景一
星巴克裡面賣多種飲料,拿鐵,咖啡,每一種飲料都有自己的價格。
為解決需求,設計一個飲料抽象類:
public abstract class Beverage { private String name; private int cost; public void setName() { this.name = "飲料"; } String getName() { return name; } Beverage(String name) { this.name = name; } //價格 public int cost(){ return cost; } }
具體飲料:拿鐵和咖啡繼承飲料抽象
public class Coffee extends Beverage{ public Coffee() { super("咖啡"); } @Override public int cost() { return 10; } } public class Latte extends Beverage{ public Latte() { super("拿鐵"); } @Override public int cost() { return 12; } }
測試:
public static void main(String[] args) {
Beverage beverage = new Coffee();
Beverage beverage2 = new Latte();
System.out.println(beverage.getName()+" : "+beverage.cost());
System.out.println(beverage2.getName()+" : "+beverage2.cost());
}
場景二
現在需求變化了,現在星巴克不僅有拿鐵,咖啡,可能在拿鐵和咖啡中加入巧克力,糖,堅果,花生等調料,並且每份調料分別計算價格。
那我們可以創建出 巧克力拿鐵、糖拿鐵、堅果拿鐵、花生拿鐵、巧克力糖拿鐵、巧克力花生拿鐵等等類,會造成類爆炸的情況,996程式設計師也寫不完,007也難,並且每加一種調料,工作量也是成倍增加。
所以我們改進寫法:
把每一份調料作為成員變數放入飲料中。修改飲料類:
public abstract class Beverage {
private boolean chocolate = false;//巧克力
private boolean sugar = false;//糖
private boolean nut = false;//堅果
private boolean peanut = false;//花生
private String name;
private int cost;
public void setName() {
this.name = "飲料";
}
String getName() {
if(this.chocolate){
name = name + "+巧克力";
}
if(this.sugar){
name = name + "+糖";
}
if(this.nut){
name = name + "+堅果";
}
if(this.peanut){
name = name + "+花生";
}
return name;
}
Beverage(String name) {
this.name = name;
}
//價格
public int cost(){
if(this.chocolate){
cost = cost + 2;
}
if(this.sugar){
cost = cost + 3;
}
if(this.nut){
cost = cost + 4;
}
if(this.peanut){
cost = cost + 5;
}
return cost;
}
public void setChocolate(boolean chocolate) {
this.chocolate = chocolate;
}
public void setSugar(boolean sugar) {
this.sugar = sugar;
}
public void setNut(boolean nut) {
this.nut = nut;
}
public void setPeanut(boolean peanut) {
this.peanut = peanut;
}
}
以咖啡類為例,修改cost方法:
public class Coffee extends Beverage {
public Coffee() {
super("咖啡");
}
@Override
public int cost() {
return 10 + super.cost();
}
}
測試:
public static void main(String[] args) {
Beverage beverage = new Coffee();
beverage.setChocolate(true);
beverage.setNut(true);
System.out.println(beverage.getName()+" : "+beverage.cost());
}
需求我們完美實現了,飲料類中加多份調料,並計算出價格。
場景三
- 業務一:隨著星巴克業務的變化和客戶的需求,需要加入奶油這一調料。我們這時候勢必需要修改飲料類的cost方法和getName方法,而我們作為客戶端是不能修改程式碼的,這違反了開閉原則。
- 業務二:有的客戶需要在咖啡中加入兩份巧克力,兩份巧克力總不能和一份的一樣價錢吧!而我們寫的成員變數作為布林值,無法體現出同一調料的數量,勢必需要改變原始碼以適應新的需求,又違反開閉原則。
這時候,救世主“裝飾器模式”誕生了!
重新修改飲料類,不再需要各種調料判斷:
public abstract class Beverage {
private String name;
private int cost;
public void setName() {
this.name = "飲料";
}
String getName() {
return name;
}
Beverage(String name) {
this.name = name;
}
//價格
public abstract int cost();
}
並抽象出調料類:
//調料
public abstract class Seasoning extends Beverage{//1.繼承Beverage
protected Beverage beverage;//2.需要有一個Beverage的成員變數
Seasoning(Beverage beverage) {
super("調料");
this.beverage = beverage;
}
}
寫兩個調料的實現類:巧克力和糖(花生類和堅果類不寫了)
public class Chocolate extends Seasoning{
private int cost = 2;
Chocolate(Beverage beverage){
super(beverage);
}
@Override
public int cost() {
return beverage.cost() + this.cost;
}
@Override
String getName() {
return beverage.getName() + "+巧克力";
}
}
public class Sugar extends Seasoning{
private int cost = 3;
Sugar(Beverage beverage) {
super(beverage);
}
@Override
public int cost() {
return beverage.cost() + this.cost;
}
@Override
String getName() {
return beverage.getName() + "+糖";
}
}
測試:
public static void main(String[] args) {
Beverage beverage ;
beverage = new Coffee();
beverage = new Sugar(beverage);//這裡一層裝飾一層(包粽子一樣)
beverage = new Chocolate(beverage);
beverage = new Sugar(beverage);
beverage = new Chocolate(beverage);
beverage = new Chocolate(beverage);
System.out.println(beverage.getName()+" : "+beverage.cost());
}
測試結果:
這裡,我們實現了上述需求。以後無論增加什麼調料或者飲料,我們都只需要繼承Seasoning或者Beverage,調料如何加,加幾份,我們也能實現,並且無需修改原始碼。上述就是裝飾器模式的具體實現。
裝飾器模式可以靈活擴充套件,但所有類都來自同一個祖宗Beverage。
uml類圖:
裝飾器模式的通用寫法
//抽象元件
public abstract class Component {
public abstract void operation();
}
//裝飾器抽象
public abstract class Decorator extends Component{
protected Component component;
public Decorator(Component component) {
this.component = component;
}
public void operation(){
component.operation();
}
}
//具體元件
public class ConcreteComponent extends Component{
@Override
public void operation() {
System.out.println("ConcreteComponent operation");
}
}
//具體裝飾器
public class ConcreteDecorator extends Decorator{
public ConcreteDecorator(Component component) {
super(component);
}
public void operation(){
System.out.println("first");
super.operation();
System.out.println("last");
}
}
測試:
public static void main(String[] args) {
Component component = new ConcreteDecorator(new ConcreteComponent());
component.operation();
}
jdk中的裝飾器模式
jdk中的io相關的流所用的就是最明顯的裝飾器模式。InputStream就是我們上述的飲料,而FilterInputStream就是我們上述的調料。