重溫《深入淺出設計模式》外觀模式 (Book Review of Head First Design Pattern, Facade Pattern)

2020-07-10

外觀模式就是將眾多的類別 (整體系統中的一部分,例如整體是播放電影系統與系統部分類別則包括電視機、擴大機、音響、電腦的關係) 利用合成產生一個外觀類別,並令 Client 與外觀類別互動,取代 Client 直接與眾多的類別互動,有使用類別 (表象模式) 封裝類別 (系統的部分) 的概念。

logo

外觀模式

Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

外觀模式的 UML

After Pattern

HometheaterFacade.cs

Facade 類別用來封裝系統部分功能的類別,並定義演算方法來操作被合成的類別,對於使用者來說只需要面對 Facade 即可,細部的運作機制交由黑箱完成。

public class HomeTheatreFacade
{
    private Dimmer _dimmer;
    private Dvd _dvd;
    private DvdPlayer _dvdPlayer;

    public HomeTheatreFacade(Dimmer dimmer, Dvd dvd, DvdPlayer dvdPlayer)
    {
        _dvd = dvd;
        _dimmer = dimmer;
        _dvdPlayer = dvdPlayer;
    }

    public void WatchMovie()
    {
        _dimmer.Dim(5);
        _dvdPlayer.On();
        _dvdPlayer.Insert(_dvd);
        _dvdPlayer.Play();
    }

    public void Pause()
    {
        _dimmer.Dim(10);
        _dvdPlayer.Pause();
    }

    public void Resume()
    {
        _dimmer.Dim(5);
        _dvdPlayer.Resume();
    }
}

Dimmer.cs
Dvd.cs
DvdPlayer.cs

系統各部分功能的類別,全部利用合成的方式封裝在 Facade Class 中。

public class Dvd
{
    public Dvd(string name)
    {
        Movie = name;
    }
    public string Movie { get; set; }
}

public class Dimmer
{
    internal void Dim(int val)
    {
        Console.WriteLine(val == 10 ? "Turning Lights On" : $"Dimming lights to {val}");
    }

    internal void Off() => Console.WriteLine("Switching off lights");
}

public class DvdPlayer
{
    private Dvd _dvd;
    private int _time = 0;
    public void On() => Console.WriteLine("DVD Player powered on");

    public void Insert(Dvd dvd)
    {
        _dvd = dvd;
        Console.WriteLine($"Inserting {dvd.Movie}");

    }

    public void Play() => Console.WriteLine($"Playing {_dvd.Movie}");

    public void Pause()
    {
        Console.WriteLine($"Pausing at {_time = (new Random()).Next(_time,_time + 120)}");
    }

    public void Resume()
    {
        Console.WriteLine($"Resuming from {_time}");
    }
}

Program.cs

var dimmer = new Dimmer();
var dvdPlayer = new DvdPlayer();
var dvd = new Dvd("Gone with the Wind 2 : Electric Bugaloo");
var homeTheater = new HomeTheatreFacade(dimmer, dvd, dvdPlayer);

homeTheater.WatchMovie();
homeTheater.Pause();
homeTheater.Resume();
homeTheater.Pause();

小結

✔️將變動的部分封裝起來
不只是屬性與方法,類別也可以是封裝的對象,外觀模式將構成整體系統的部分類類別封裝在單一類別中。

✔️多用合成,少用繼承
外觀模式利用合成的方式封裝類別。

✔️讓需要互動的物件之間的關係鬆綁
Client 需要使用的系統的部分類別但藉由外觀模式就不需要直接與部分類別作互動。

✔️類別應該對需求保持開放;應該對既有的程式碼修改保持關閉
如果有有新的類別產生,只需要異動外觀模式類別, Client 不需要異動。

參考資料