引子:牛市還會虧錢的散戶#
小菜公司同事顧韻梅在大牛市裡卻天天虧錢:
- 看好一隻快漲停的股票買進,第二天就跌
- 換另一隻好股票,幾天不漲
- 一賣出,馬上漲停
不會炒股票卻急著進場,買進賣出頻繁——這是典型新股民特徵。
散戶一打開股票軟體,一千多支股票紅紅綠綠、又是 K 線又是基本面,頭暈眼花、迷茫困惑。
基金的解法#
基金(Fund):將投資者分散的資金集中起來,交由專業的經理人進行管理,投資於股票、債券、外匯等領域。
投資者不必直接面對上千支股票,只需關心基金的漲跌,由基金經理人代為操作。
對應到軟體:
- 投資者直接買股票 = 客戶端與所有具體類別都耦合 → 耦合度太高
- 投資者買基金 = 客戶端只與一個簡化介面互動 → 耦合度大幅降低
第一版:股民直接炒股的程式#
class Stock1 { public void Sell() { ... } public void Buy() { ... } }
class Stock2 { /* 同 */ }
class Stock3 { /* 同 */ }
class NationalDebt1 { /* 國債 */ }
class Realty1 { /* 房地產 */ }客戶端:
Stock1 gu1 = new Stock1();
Stock2 gu2 = new Stock2();
Stock3 gu3 = new Stock3();
NationalDebt1 nd1 = new NationalDebt1();
Realty1 rt1 = new Realty1();
gu1.Buy(); gu2.Buy(); gu3.Buy();
nd1.Buy(); rt1.Buy();
gu1.Sell(); gu2.Sell(); gu3.Sell();
nd1.Sell(); rt1.Sell();客戶端必須認識所有投資產品類別,需要參與每一筆買賣的細節。
第二版:投資基金的程式#
加入 Fund 類別把所有產品包起來:
class Fund
{
Stock1 gu1; Stock2 gu2; Stock3 gu3;
NationalDebt1 nd1; Realty1 rt1;
public Fund()
{
gu1 = new Stock1();
gu2 = new Stock2();
gu3 = new Stock3();
nd1 = new NationalDebt1();
rt1 = new Realty1();
}
public void BuyFund() { gu1.Buy(); gu2.Buy(); gu3.Buy(); nd1.Buy(); rt1.Buy(); }
public void SellFund() { gu1.Sell(); gu2.Sell(); gu3.Sell(); nd1.Sell(); rt1.Sell(); }
}客戶端:
Fund jijin = new Fund();
jijin.BuyFund();
jijin.SellFund();客戶端不再需要了解股票、國債、房地產的細節——買了基金回家睡覺,由基金經理人完成所有操作。
外觀模式#
外觀模式(Facade Pattern),又稱門面模式:為子系統中的一組介面提供一個一致的界面,此模式定義了一個高層介面,使得子系統更容易使用。[DP]
結構#
- Facade(外觀):知道哪些子系統類別負責處理請求,將客戶請求代理給適當的子系統物件
- SubSystem Classes(子系統類別群):實現子系統功能,處理 Facade 物件指派的工作;對 Facade 沒有任何認識,子系統不知道 Facade 的存在
classDiagram
class Client
class Facade {
+MethodA()
+MethodB()
}
class SubSystemOne {
+MethodOne()
}
class SubSystemTwo {
+MethodTwo()
}
class SubSystemThree {
+MethodThree()
}
class SubSystemFour {
+MethodFour()
}
Client --> Facade
Facade --> SubSystemOne
Facade --> SubSystemTwo
Facade --> SubSystemThree
Facade --> SubSystemFourclass SubSystemOne { public void MethodOne() { ... } }
class SubSystemTwo { public void MethodTwo() { ... } }
class SubSystemThree { public void MethodThree() { ... } }
class SubSystemFour { public void MethodFour() { ... } }
class Facade
{
SubSystemOne one;
SubSystemTwo two;
SubSystemThree three;
SubSystemFour four;
public Facade()
{
one = new SubSystemOne();
two = new SubSystemTwo();
three = new SubSystemThree();
four = new SubSystemFour();
}
public void MethodA() { one.MethodOne(); two.MethodTwo(); four.MethodFour(); }
public void MethodB() { two.MethodTwo(); three.MethodThree(); }
}客戶端只與 Facade 互動:
Facade facade = new Facade();
facade.MethodA();
facade.MethodB();外觀模式完美體現了依賴倒轉原則與迪米特法則——它是非常常用的模式之一。
何時使用外觀模式#
1. 設計初期:層與層之間建立外觀#
經典三層架構(資料訪問層 / 業務邏輯層 / 表示層)之間應該建立外觀(Facade)。
為複雜的子系統提供簡單介面,使層與層的耦合大幅降低。
2. 開發階段:抑制子系統膨脹#
子系統會因為不斷重構而變得越來越複雜,大多模式使用時都會產生許多小類別。
- 這些小類別本身是好事,但會讓外部呼叫的客戶程式不容易使用
- 增加 Facade 提供簡單介面,減少類別之間的依賴
3. 維護遺留系統:作為新舊橋樑#
維護一個非常難以擴展但仍包含重要功能的遺留系統時,可以為新系統開發一個 Facade:
- 新系統與 Facade 互動
- Facade 處理與遺留程式碼的所有複雜工作[R2P]
兩個小組分工:
- 一組開發 Facade 與老系統的交互
- 另一組只需了解 Facade 的介面,直接開發新系統呼叫
這能減少很多不必要的麻煩。