亲代物件负责架构,实作细节则交给继承的子代物件负责。
试想一个情境,物件内某个方法的实作内容有各种可能,如果没有思考,可能会依照实作内容的不同,建立多个物件。
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 模式。
>>: 【把玩Azure DevOps】Day30 2021铁人赛结尾感言
什麽是 Geolocation API? 透过 Geolocation API 可以让 Progre...
网际网路的兴盛离不开Http、Https两种协议的功劳,但在网路世界中,DDOS、CC Attac...
本系列文之後也会置於个人网站 +---------+ +---------------+ | | ...
其实原本最初规画想要做Index方式的纪录,然後多增加一些没写到的面向 不过,总是计画赶不上变化 ...
举例: 想像你的产品有个/user/email route允许post request去修改已经认证...