「補課」進行時:設計模式(10)——小明起床記了解裝飾模式
1. 前文彙總
2. 小明起床記
小明每天早晨都是起床困難大戶,大冬天的太冷了,溫暖的被窩緊緊的拉住小明,阻止小明每天早晨的起床。
鬧鐘響了一遍又一遍,如果再不起床就要遲到了,遲到了就要扣錢,扣了錢就要喝西北風了。
每天早晨督促小明起床的根本不是鬧鐘,而是貧窮。
起床第一件事兒是穿衣服,先傳衣服,再傳褲子,然後穿鞋子,最後穿上一件外套,出門上班。
首先,定義一個抽象的小明,小明是個人,所以定義一個人:
public abstract class Person {
abstract void dress();
}
每個人早晨起床都要穿衣服,這裡定義一個穿衣服的方法。
具體的小明上線:
public class Man extends Person {
@Override
void dress() {
System.out.println("先穿衣服");
}
}
接下來我們要定義一個抽象的裝飾器了,小明要穿的是衣服,我們將衣服抽象成一個類:
public abstract class Clothes extends Person { private Person person; public Clothes(Person person) { this.person = person; } @Override void dress() { this.person.dress(); } }
接下來是具體的衣服:
public class Trousers extends Clothes { public Trousers(Person person) { super(person); } @Override void dress() { super.dress(); this.dressTrousers(); } private void dressTrousers() { System.out.println("穿上褲子啦!!!"); } } public class Shoes extends Clothes { public Shoes(Person person) { super(person); } @Override void dress() { super.dress(); this.dressShoes(); } private void dressShoes() { System.out.println("穿上鞋子啦!!!"); } } public class Coat extends Clothes { public Coat(Person person) { super(person); } @Override void dress() { super.dress(); this.dressCoat(); } private void dressCoat() { System.out.println("穿上外套啦!!!"); } }
最後是一個測試類:
public class Test1 {
public static void main(String[] args) {
Person person = new Man();
person.dress();
System.out.println("--------------");
System.out.println("增加褲子介面卡");
person = new Trousers(person);
person.dress();
System.out.println("--------------");
System.out.println("增加鞋子介面卡");
person = new Shoes(person);
person.dress();
System.out.println("--------------");
System.out.println("增加外套介面卡");
person = new Coat(person);
person.dress();
}
}
測試結果如下:
先穿衣服
--------------
增加褲子介面卡
先穿衣服
穿上褲子啦!!!
--------------
增加鞋子介面卡
先穿衣服
穿上褲子啦!!!
穿上鞋子啦!!!
--------------
增加外套介面卡
先穿衣服
穿上褲子啦!!!
穿上鞋子啦!!!
穿上外套啦!!!
上面這麼寫有點麻煩,我們可以稍微縮減一下測試類,使用裝飾器巢狀,一次性直接把所有的衣服都穿好:
public class Test2 {
public static void main(String[] args) {
Person person = new Coat(new Shoes(new Trousers(new Man())));
person.dress();
}
}
3. 裝飾器模式
3.1 定義
裝飾模式(Decorator Pattern)是一種比較常見的模式,其定義如下:
Attachadditional responsibilities to an object dynamically keeping the sameinterface.Decorators provide a flexible alternative to subclassing forextending functionality.(動態地給一個物件新增一些額外的職責。就增加功能來說,裝飾模式相比生成子類更為靈活。)
- Component: 抽象構件,是一個介面或者是抽象類,就是定義我們最核心的物件,也就是最原始的物件。
- ConcreteComponent: 具體構件,是最核心、最原始、最基本的介面或抽象類的實現。
- Decorator: 通用的裝飾 ConcreteComponent 的裝飾器,其內部必然有一個屬性指向 Component 抽象元件;其實現一般是一個抽象類,主要是為了讓其子類按照其構造形式傳入一個 Component 抽象元件,這是強制的通用行為(當然,如果系統中裝飾邏輯單一,並不需要實現許多裝飾器,那麼我們可以直接省略該類,而直接實現一個 具體裝飾器(ConcreteDecorator) 即可)。
- ConcreteDecorator: Decorator 的具體實現類,理論上,每個 ConcreteDecorator 都擴充套件了 Component 物件的一種功能。
通用程式碼:
public abstract class Component {
abstract void operate();
}
public class ConcreteComponent extends Component {
@Override
void operate() {
System.out.println("do Something");
}
}
public abstract class Decorator extends Component {
private Component component = null;
// 通過建構函式傳遞被修飾者
public Decorator(Component component) {
this.component = component;
}
// 委託給被修飾者執行
@Override
void operate() {
this.component.operate();
}
}
public class ConcreteDecorator1 extends Decorator {
// 定義被修飾者
public ConcreteDecorator1(Component component) {
super(component);
}
private void method1() {
System.out.println("method1 修飾");
}
@Override
void operate() {
this.method1();
super.operate();
}
}
public class ConcreteDecorator2 extends Decorator {
public ConcreteDecorator2(Component component) {
super(component);
}
private void method2() {
System.out.println("method2 修飾");
}
@Override
void operate() {
super.operate();
this.method2();
}
}
public class Test {
public static void main(String[] args) {
Component component = new ConcreteComponent();
// 第一次修飾
component = new ConcreteDecorator1(component);
// 第二次修飾
component = new ConcreteDecorator2(component);
// 修飾後執行
component.operate();
}
}
3.2 優點
裝飾類和被裝飾類可以獨立發展,而不會相互耦合。換句話說, Component 類無須知道 Decorator 類, Decorator 類是從外部來擴充套件 Component 類的功能,而 Decorator 也不用知道具體的構件。
3.3 缺點
對於裝飾模式記住一點就足夠了:多層的裝飾是比較複雜的。為什麼會複雜呢?想想看,就像剝洋蔥一樣,至於剝到了最後才發現是最裡層的裝飾出現了問題,想象一下工作量吧,因此,儘量減少裝飾類的數量,以便降低系統的複雜度。
3.3 使用場景
- 需要擴充套件一個類的功能,或給一個類增加附加功能。
- 需要動態地給一個物件增加功能,這些功能可以再動態地撤銷。
- 需要為一批的兄弟類進行改裝或加裝功能,當然是首選裝飾模式。