建立一个对外的窗口(介面),负责提供特定功能,而功能背後如何运作?与哪些物件有所关联?通通交给对外窗口来实践。
让复杂的系统有一个对外的窗口,负责发号施令告知子系统该如何运作。
现实生活中,不少方便的事物对使用者来说都是简单,背後却有着复杂的步骤,例如:
换到程序上,可以分成三层:资料存取层、业务逻辑层、表示层,各层有各自的生态圈,如果放任彼此的联系方式,将导致过度耦合,未来的新增、修改将渐渐不容易。所以,适时地建立对外窗口,负责制定能提供的功能,各层之间依赖窗口沟通,进而简化耦合度
在网页开发上最有名的个案是 jQuery
,提供一个简单的介面,背後却是非常复杂的运算。
实践的作法是:
子系统:DrinksVendingMachine
、MoneySystem
、ShippingSystem
public class DrinksVendingMachine {
private boolean isNormal;
public DrinksVendingMachine() {
isNormal = true;
}
public void welcome() {
if (isNormal) {
System.out.println("欢迎使用本机器");
System.out.println("请投入硬币或纸钞");
} else {
System.out.println("机器故障,请联络厂商");
}
}
public int getCoin() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
return Integer.parseInt(br.readLine());
}
public void displayMoney(int money) {
System.out.println("已投入 " + money + " 元");
System.out.println("系统亮起可购买饮料");
}
public String chooseDrink() throws IOException {
System.out.println("请选择饮料");
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
return br.readLine();
}
public void takeDrink(String drink) {
System.out.println("客户已经取出" + drink);
}
public void getError() {
isNormal = false;
}
}
public class MoneySystem {
private int currentMoney;
public MoneySystem() {
currentMoney = 0;
}
public void insertCoin(int value) {
currentMoney += value;
}
public int showCurrentMoney() {
return currentMoney;
}
}
public class ShippingSystem {
private String drinkName;
public ShippingSystem() {
drinkName = "";
}
public void setUpChooseDrink(String drink) {
drinkName = drink;
}
public void openGate() {
System.out.println("贩卖机底下出口已开启");
}
public void shipping() {
System.out.println("运作" + drinkName + "的输送带");
System.out.println(drinkName + "往下掉入出口");
System.out.println("发出撞击声");
System.out.println(drinkName + "可乐已抵达取出口");
}
public String getChosenDrink() {
return drinkName;
}
}
对外窗口:VendingMachineFacade
import java.io.IOException;
public class VendingMachineFacade {
private DrinksVendingMachine dvm;
private MoneySystem ms;
private ShippingSystem ss;
public VendingMachineFacade() {
dvm = new DrinksVendingMachine();
ms = new MoneySystem();
ss = new ShippingSystem();
}
public void useIt() throws IOException {
dvm.welcome();
ms.insertCoin(dvm.getCoin());
dvm.displayMoney(ms.showCurrentMoney());
ss.setUpChooseDrink(dvm.chooseDrink());
ss.shipping();
dvm.takeDrink(ss.getChosenDrink());
}
}
测试:DrinkFacadeSample
import java.io.IOException;
public class DrinkFacadeSample {
public static void main(String[] args) throws IOException {
VendingMachineFacade machine = new VendingMachineFacade();
machine.useIt();
}
}
设定环境,能够读取终端机的输入文字
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const getUserInput = () => {
return new Promise((resolve, reject) => {
rl.question("", (input) => {
resolve(input);
});
});
};
子系统:DrinksVendingMachine
、MoneySystem
、ShippingSystem
class DrinksVendingMachine {
constructor() {
this.isNormal = true;
}
welcome() {
if (this.isNormal) {
console.log("欢迎使用本机器");
console.log("请投入硬币或纸钞");
} else {
console.log("机器故障,请联络厂商");
}
}
getCoin() {
return getUserInput();
}
displayMoney(money) {
console.log("已投入 " + money + " 元");
console.log("系统亮起可购买饮料");
}
chooseDrink() {
console.log("请选择饮料");
return getUserInput();
}
takeDrink(drink) {
console.log("客户已经取出" + drink);
}
getError() {
this.isNormal = false;
}
}
class MoneySystem {
constructor() {
this.currentMoney = 0;
}
insertCoin(value) {
this.currentMoney += value;
}
showCurrentMoney() {
return this.currentMoney;
}
}
class ShippingSystem {
constructor() {
this.drinkName = "";
}
setUpChooseDrink(drink) {
this.drinkName = drink;
}
openGate() {
console.log("贩卖机底下出口已开启");
}
shipping() {
console.log("运作" + this.drinkName + "的输送带");
console.log(this.drinkName + "往下掉入出口");
console.log("发出撞击声");
console.log(this.drinkName + "已抵达取出口");
}
getChosenDrink() {
return this.drinkName;
}
}
对外窗口:VendingMachineFacade
class VendingMachineFacade {
constructor() {
this.dvm = new DrinksVendingMachine();
this.ms = new MoneySystem();
this.ss = new ShippingSystem();
}
async useIt() {
this.dvm.welcome();
this.ms.insertCoin(parseInt(await this.dvm.getCoin()));
this.dvm.displayMoney(this.ms.showCurrentMoney());
this.ss.setUpChooseDrink(await this.dvm.chooseDrink());
this.ss.shipping();
this.dvm.takeDrink(this.ss.getChosenDrink());
rl.close();
}
}
测试:drinkFacadeSample
const drinkFacadeSample = async () => {
const machine = new VendingMachineFacade();
await machine.useIt();
}
drinkFacadeSample();
Facade 有趣在於,日常开发上经常使用,只是没有仔细思索过这样做的好处,因此在阅读相关文章时,除了脑中有经验可以马上连结之外,还能重新看待当初开发的过程,细细品味当时有没有尽力做好?是否有些对外窗口没有好好处理?再者,阅读自己的专案之外,阅读 jQuery
的原始档,慢慢能从中看出一些设计上的巧思,不再是当年那个刚踏入程序开发的自己了,同时对自己的成长感到开心。
明天将介绍 Structural patterns 的第六个模式:Flyweight 模式。
纠团的功能我把它切成两个部分 使用者输入讯息 背景执行 今天介绍背景执行的部分 背景执行 这个部分主...
Machine Learning 上篇文章有简单提及Machine Learning的定义: Ma...
D6: for回圈 最基本的for回圈样式是: for (变数初始值; 判断式; 递增式){ 陈述句...
杯里的水并不多,再加上中途受其他外力改变倾倒的方向,所以只有键盘边缘沾上几滴水珠。 「⋯⋯学姐,刚刚...
Nnọọ,我是Charlie! 在Day23当中我们完成了订单资料的後端API,而今天我们将完成订单...