使用包覆(Wrapper)的方式,可以動態地給物件增添新的功能,或是重新定義既有的功能,達到擴充目的。
當原本的程式需要配合需求新增功能,新功能之間彼此不互斥,可以疊加時。簡單的做法是:
隨著新功能功能不斷地增加,簡單的做法必須建立符合各種可能的物件,將導致管理不易。
因此,可以換個想法,讓新功能包覆物件後形成新的物件,且新功能可以包覆其他新功能,如此一來,物件便擁有新功能。而管理上,只要確認包覆的順序即可,減少推測多種可能結果的麻煩。
作法是:
本體物件,祖先代:Commuter
public class Commuter {
private String name;
private String destination;
public Commuter() {
}
public Commuter(String name, String destination) {
this.name = name;
this.destination = destination;
}
public void claimDestination() {
System.out.println("目的地: " + destination + " 已經抵達,準備下車");
}
public void commute() {
System.out.println("我是 " + name + ",是一位通勤者");
}
}
活動親代,繼承本體物件:Activity
public class Activity extends Commuter {
protected Commuter commuter;
public Activity() {
}
public Activity(Commuter commuter) {
this.commuter = commuter;
}
public void commuterDecorate(Commuter commuter) {
this.commuter = commuter;
}
@Override
public void claimDestination() {
if (commuter != null) {
commuter.claimDestination();
}
}
@Override
public void commute() {
if (commuter != null) {
commuter.commute();
}
}
}
活動子代:InADaze
、ListeningMusic
、ObservingOthers
、StopEverything
、WatchingYouTube
public class InADaze extends Activity {
public InADaze() {
}
public InADaze(Activity activity) {
super(activity);
}
public void dream() {
System.out.println("似乎想到什麼,讓人想「A Ha」一下");
}
@Override
public void commute() {
super.commute();
System.out.println("什麼都不想做,發呆中");
dream();
}
}
public class ListeningMusic extends Activity {
public ListeningMusic() {
}
public ListeningMusic(Activity activity) {
super(activity);
}
public void feel() {
System.out.println("這首歌真讚啊");
}
@Override
public void commute() {
super.commute();
System.out.println("專注在聽音樂");
feel();
}
}
public class ObservingOthers extends Activity {
public ObservingOthers() {
}
public ObservingOthers(Activity activity) {
super(activity);
}
public void alert() {
System.out.println("警戒中");
}
@Override
public void commute() {
super.commute();
alert();
System.out.println("觀察他人的衣著、行為");
}
}
public class StopEverything extends Activity {
public StopEverything() {
}
public StopEverything(Activity activity) {
super(activity);
}
@Override
public void commute() {
super.commute();
System.out.println("停止一切事物");
}
}
public class WatchingYouTube extends Activity {
public WatchingYouTube() {
}
public WatchingYouTube(Activity activity) {
super(activity);
}
public void detail() {
System.out.println("這部影片可是今天的發燒啊");
}
@Override
public void commute() {
super.commute();
System.out.println("專注在 YT 上");
detail();
}
}
測試:CommuterDecoratorSample
public class CommuterDecoratorSample {
public static void main(String[] args) {
System.out.println("---第一位通勤者---");
Commuter victor = new Commuter("維特", "臺北車站");
Activity watchingYouTube = new WatchingYouTube();
watchingYouTube.commuterDecorate(victor);
Activity stopEverything = new StopEverything(watchingYouTube);
stopEverything.commute();
stopEverything.claimDestination();
System.out.println("\n---第二位通勤者---");
Commuter sandy = new Commuter("珊迪", "市府站");
Activity listeningMusic = new ListeningMusic();
listeningMusic.commuterDecorate(sandy);
listeningMusic.commute();
listeningMusic.claimDestination();
System.out.println("\n---第三位通勤者---");
Commuter johnny = new Commuter("強尼", "象山站");
Activity inADaze = new InADaze(stopEverything);
inADaze.commuterDecorate(johnny);
Activity observingOthers = new ObservingOthers(inADaze);
observingOthers.commute();
observingOthers.claimDestination();
}
}
本體物件,祖先代:Commuter
class Commuter {
constructor(name, destination) {
this.name = name;
this.destination = destination;
}
claimDestination() {
console.log("目的地: " + this.destination + " 已經抵達,準備下車");
}
commute() {
console.log("我是 " + this.name + ",是一位通勤者");
}
}
活動親代,繼承本體物件:Activity
class Activity extends Commuter {
constructor(commuter) {
super(null, null);
this.commuter = commuter;
}
commuterDecorate(commuter) {
this.commuter = commuter;
}
/** @override */
claimDestination() {
if (this.commuter != null) {
this.commuter.claimDestination();
}
}
/** @override */
commute() {
if (this.commuter != null) {
this.commuter.commute();
}
}
}
活動子代:InADaze
、ListeningMusic
、ObservingOthers
、StopEverything
、WatchingYouTube
class InADaze extends Activity {
constructor(activity) {
super(activity);
}
dream() {
console.log("似乎想到什麼,讓人想「A Ha」一下");
}
/** @override */
commute() {
super.commute();
console.log("什麼都不想做,發呆中");
this.dream();
}
}
class ListeningMusic extends Activity {
constructor(activity) {
super(activity);
}
feel() {
console.log("這首歌真讚啊");
}
/** @override */
commute() {
super.commute();
console.log("專注在聽音樂");
this.feel();
}
}
class ObservingOthers extends Activity {
constructor(activity) {
super(activity);
}
alert() {
console.log("警戒中");
}
/** @override */
commute() {
super.commute();
this.alert();
console.log("觀察他人的衣著、行為");
}
}
class StopEverything extends Activity {
constructor(activity) {
super(activity);
}
/** @override */
commute() {
super.commute();
console.log("停止一切事物");
}
}
class WatchingYouTube extends Activity {
constructor(activity) {
super(activity);
}
detail() {
console.log("這部影片可是今天的發燒啊");
}
/** @override */
commute() {
super.commute();
console.log("專注在 YT 上");
this.detail();
}
}
測試:commuterDecoratorSample
const commuterDecoratorSample = () => {
console.log("---第一位通勤者---");
const victor = new Commuter("維特", "臺北車站");
const watchingYouTube = new WatchingYouTube();
watchingYouTube.commuterDecorate(victor);
const stopEverything = new StopEverything(watchingYouTube);
stopEverything.commute();
stopEverything.claimDestination();
console.log("\n---第二位通勤者---");
const sandy = new Commuter("珊迪", "市府站");
const listeningMusic = new ListeningMusic();
listeningMusic.commuterDecorate(sandy);
listeningMusic.commute();
listeningMusic.claimDestination();
console.log("\n---第三位通勤者---");
const johnny = new Commuter("強尼", "象山站");
const inADaze = new InADaze(stopEverything);
inADaze.commuterDecorate(johnny);
const observingOthers = new ObservingOthers(inADaze);
observingOthers.commute();
observingOthers.claimDestination();
};
commuterDecoratorSample();
Decorator 可以用俄羅斯娃娃來聯想,每一次的包覆,除了維持既有功能之外,還能新增點什麼,好實踐需求。但是,為了達成「有條有理」的繼承、覆寫、新增,必須在動手寫程式碼之前就定義好哪些行為可以擴充,這部分的設計複雜、不容易實踐,可能到最後找不出共同點而罷休。
很明顯地,Decorator 模式也是屬於特殊要求下的解。
明天將介紹 Structural patterns 的第五個模式:Facade 模式。