2.7.1 模式意圖:
系統中常含有多個子系統,隨著子系統不斷增多,如果始終保持對子系統的直接調用,會明顯提高業務邏輯和子系統間的耦合度,造成系統混亂,加大閱讀困難,這時我們可以使用外觀模式,用此模式持有多個子系統的引用,通過外觀這個中間層達到由原來的多對多轉變為一對多的情況,對多個子系統的調用就像使用菜單一樣,一目了然,利于代碼的閱讀理解和維護。
2.7.2 模式概念:
又稱為門面模式,屬于結構型模式。為子系統中的一組接口提供一個一致的界面,此模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
2.7.3 模式元素:
- 外觀(Facade)
- 子系統(IPhysicalSystem、IRenderingSystem、IMoveSystem)
2.7.4 代碼示例:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Custom.Log;
interface IPhysicalSystem
{
void Initialize();
void Update();
}
interface IRenderingSystem
{
void Initialize();
void Update();
}
interface IMoveSystem
{
void Initialize();
void Update();
}
public class PhysicalSystem : IPhysicalSystem
{
public void Initialize() { Degbug.Log($"初始化{nameof(PhysicalSystem)}"); }
public void Update() { Degbug.Log($"更新{nameof(PhysicalSystem)}"); }
}
public class RenderingSystem : IRenderingSystem
{
public void Initialize() { Degbug.Log($"初始化{nameof(RenderingSystem)}"); }
public void Update() { Degbug.Log($"更新{nameof(RenderingSystem)}"); }
}
public class MoveSystem : IMoveSystem
{
public void Initialize() { Degbug.Log($"初始化{nameof(MoveSystem)}"); }
public void Update() { Degbug.Log($"更新{nameof(MoveSystem)}"); }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Custom.Log;
public class Facade
{
#region Facade
static Facade() { }
private Facade() { }
private readonly static object staticsync = new object();
private volatile static Facade instance = null;
public static Facade Instance
{
get
{
if (instance == null)
{
lock (staticsync)
{
if (instance == null)
{
instance = new Facade();
}
}
}
return instance;
}
}
#endregion
private IMoveSystem moveSystem = new MoveSystem();
private IPhysicalSystem physicalSystem = new PhysicalSystem();
private IRenderingSystem renderingSystem = new RenderingSystem();
public void Initialize()
{
moveSystem.Initialize();
physicalSystem.Initialize();
renderingSystem.Initialize();
}
public void Update()
{
moveSystem.Update();
physicalSystem.Update();
renderingSystem.Update();
}
}
調用
public class FacadeComponent : MonoBehaviour
{
Facade facade = null;
void Start()
{
facade = Facade.Instance;
facade.Initialize();
}
private void Update()
{
facade.Update();
}
}
2.7.5 寫法對比:
略
2.7.6 模式分析:
外觀模式相對于其他設計模式來講是一種既簡單又實用的編程技巧。
其實外觀模式的這種思想在生活中也有很廣的應用,例如: 1.我們去餐廳吃飯時菜單和點菜器,這就是一種外觀模式的體現,我們不需要跟各類廚師進行交互,只需要對菜單發出“指令”即可。 2.電腦的開機鍵,一鍵開機,不需要通知CPU、顯卡、內存、硬盤等電子元件開始工作。
這種結構,使得我們上層調用的時候不需要知道明確的子系統,只需要知道Facade定義的對應函數即可,降低耦合度,符合迪米特法則,但它的缺點就是破壞了開閉原則,如果后期子系統有改動,Facade會變動比較頻繁。
還有一個要注意的是,Facade只能調用其子系統中含有的函數,完成對各個子系統自帶函數的拼裝,不能自建新的業務邏輯,例如我們的示例,要在Facade類里面根據
moveSystem
添加新的業務邏輯這個是不允許的,,而且外觀模式封裝的是單向交互,是從客戶端訪問系統的調用,不允許從系統中來訪問客戶端的調用。
這些注意要素其實在PureMVCFramework中就有很好的體現,在對應的Facade類中(如下所示),僅僅就是模型、視圖、控制層這些子系統的單向調用自身函數,并沒有添加一些其他的自建業務邏輯。
namespace PureMVC.Patterns.Facade
{
public class Facade : IFacade
{
//省略其他代碼***
public virtual void RegisterCommand(string notificationName, Func<ICommand> commandFunc)
{
controller.RegisterCommand(notificationName, commandFunc);
}
public virtual void RemoveCommand(string notificationName)
{
controller.RemoveCommand(notificationName);
}
public virtual bool HasCommand(string notificationName)
{
return controller.HasCommand(notificationName);
}
public virtual void RegisterProxy(IProxy proxy)
{
model.RegisterProxy(proxy);
}
public virtual IProxy RetrieveProxy(string proxyName)
{
return model.RetrieveProxy(proxyName);
}
public virtual IProxy RemoveProxy(string proxyName)
{
return model.RemoveProxy(proxyName);
}
public virtual bool HasProxy(string proxyName)
{
return model.HasProxy(proxyName);
}
}
}
2.7.7 應用場景:
需要對多個子系統的單向調用。
2.7.8 小結:
利用外觀模式來降低業務邏輯與各子系統間的耦合度,同時也要注意后續頻繁改動需求,引起的Facade類發生一系列改變而帶來的風險。