親代物件負責架構,實作細節則交給繼承的子代物件負責。
試想一個情境,物件內某個方法的實作內容有各種可能,如果沒有思考,可能會依照實作內容的不同,建立多個物件。
class Operation1 {
operation() {
// 相同處,都做點什麼
// 差異處 AAA
}
}
class Operation2 {
operation() {
// 相同處,都做點什麼
// 差異處 BBB
}
}
隨著物件數量增加,大量重複的程式碼會不斷出現。此時,該思考的是,如何將重複的部分抽出,並且不影響原有功能。剛好,每個物件的差異都在實作的細節,所以可以建立一個親代物件,除了將共同的程式碼封裝之外,建立一個供子代實作但親代只負責開規格的方法。
class OperationPrototype {
operation() {
// 相同處,都做點什麼
}
operationDetails() { }
}
class Operation1 extends OperationPrototype {
/** @override */
operationDetails() { }() {
// 差異處 AAA
}
}
class Operation2 extends OperationPrototype {
/** @override */
operationDetails() { }() {
// 差異處 BBB
}
}
如此一來,子代只要負責實作的內容,整體的框架仍然由親代制定,子代沒有能力做更動。
作法是:
以下範例以「簡易打掃家庭」為核心製作。
建立親代虛擬層物件:Housekeeping
public abstract class Housekeeping {
protected String type;
Housekeeping(String type) {
this.type = type;
}
public String washClothes() {
return washDetails();
}
protected abstract String washDetails();
public String cleanUp() {
return cleanUpDetails();
}
protected abstract String cleanUpDetails();
}
建立子代物件:SimpleHousekeeping
、AppliancesHousekeeping
(Template 物件)
public class SimpleHousekeeping extends Housekeeping {
public SimpleHousekeeping() {
super("簡單的打掃方式");
}
@Override
protected String washDetails() {
System.out.println("手洗衣服");
for (int i = 0; i < 10; i++) {
System.out.println("手洗中" + ".".repeat(i));
}
return "一堆乾淨的衣服,花了兩小時";
}
@Override
protected String cleanUpDetails() {
System.out.println("用掃把打掃");
for (int i = 0; i < 8; i++) {
System.out.println("打掃中" + ".".repeat(i));
}
return "乾淨的房間,腰痠背痛";
}
}
public class AppliancesHousekeeping extends Housekeeping {
public AppliancesHousekeeping() {
super("家電幫忙下的打掃方式");
}
@Override
protected String washDetails() {
System.out.println("洗衣機洗衣服");
for (int i = 0; i < 5; i++) {
System.out.println("嘟".repeat(i));
}
return "一堆乾淨的衣服,花了一小時";
}
@Override
protected String cleanUpDetails() {
System.out.println("用吸塵器打掃");
for (int i = 0; i < 3; i++) {
System.out.println("嗚".repeat(i));
}
return "乾淨的房間,輕鬆完成";
}
}
測試,模擬使用不同器具打掃家庭:TicketMachineStrategySample
public class HousekeepingTemplateSample {
public static void main(String[] args) {
Housekeeping housekeeping = null;
String washClothesResult = null;
String cleanUpResult = null;
System.out.println("沒什麼閒錢,整理家務簡單即可");
housekeeping = new SimpleHousekeeping();
washClothesResult = housekeeping.washClothes();
cleanUpResult = housekeeping.cleanUp();
System.out.println("清理結果: " + washClothesResult + ";" + cleanUpResult);
System.out.println("\n有點錢了,買些家電幫忙整理家務");
housekeeping = new AppliancesHousekeeping();
washClothesResult = housekeeping.washClothes();
cleanUpResult = housekeeping.cleanUp();
System.out.println("\n清理結果: " + washClothesResult + ";" + cleanUpResult);
}
}
建立親代虛擬層物件:Housekeeping
/** @abstract */
class Housekeeping {
/** @param {string} type */
constructor(type) {
this.type = type;
}
washClothes() {
return this.washDetails();
}
/** @abstract */
washDetails() { return ""; }
cleanUp() {
return this.cleanUpDetails();
}
/** @abstract */
cleanUpDetails() { return ""; }
}
建立子代物件:SimpleHousekeeping
、AppliancesHousekeeping
(Template 物件)
class SimpleHousekeeping extends Housekeeping {
constructor() {
super("簡單的打掃方式");
}
/** @override */
washDetails() {
console.log("手洗衣服");
for (let i = 0; i < 10; i++) {
console.log("手洗中" + ".".repeat(i));
}
return "一堆乾淨的衣服,花了兩小時";
}
/** @override */
cleanUpDetails() {
console.log("用掃把打掃");
for (let i = 0; i < 8; i++) {
console.log("打掃中" + ".".repeat(i));
}
return "乾淨的房間,腰痠背痛";
}
}
class AppliancesHousekeeping extends Housekeeping {
constructor() {
super("家電幫忙下的打掃方式");
}
/** @override */
washDetails() {
console.log("洗衣機洗衣服");
for (let i = 0; i < 5; i++) {
console.log("嘟".repeat(i));
}
return "一堆乾淨的衣服,花了一小時";
}
/** @override */
cleanUpDetails() {
console.log("用吸塵器打掃");
for (let i = 0; i < 3; i++) {
console.log("嗚".repeat(i));
}
return "乾淨的房間,輕鬆完成";
}
}
測試,模擬使用不同器具打掃家庭:TicketMachineStrategySample
const housekeepingTemplateSample = () => {
let housekeeping = null;
let washClothesResult = null;
let cleanUpResult = null;
console.log("沒什麼閒錢,整理家務簡單即可");
housekeeping = new SimpleHousekeeping();
washClothesResult = housekeeping.washClothes();
cleanUpResult = housekeeping.cleanUp();
console.log("清理結果: " + washClothesResult + ";" + cleanUpResult);
console.log("\n有點錢了,買些家電幫忙整理家務");
housekeeping = new AppliancesHousekeeping();
washClothesResult = housekeeping.washClothes();
cleanUpResult = housekeeping.cleanUp();
console.log("\n清理結果: " + washClothesResult + ";" + cleanUpResult);
};
housekeepingTemplateSample();
Template Method 模式有趣在於,如果常常使用 OOP 原則 - 繼承(Inheritance)的話,那閱讀這個模式的內容時會恍然大悟,並且有感而發:「原來平常開發的習慣,即使再怎麼簡單,也是一種模式?!」
想想也是如此,沒有實際接觸、了解前,不用把不知道的事情想成是偉大、繁瑣、有難度的、不易暸解等等,徒然增加自身在學習上的障礙物。
明天將介紹 Behavioural patterns 的第十一個也是該類別最後一個模式:Visitor 模式。