簡單介紹設計模式 - 外觀模式
外觀模式為一種結構型設計模式,主要為複雜的函式庫(Library)或是複雜的類別提供一個統一且簡單的高級介面。
要處理的問題
假設你的程式需要使用某個複雜的函式庫。一般來說,你需要負責函式庫中所需物件的初始化工作,除了需要管理這些物件的依賴關係,還需要按照順序執行這些物件的方法來幫助你完成目的。
但這麼做的結果,有可能讓你的程式碼業務邏輯與函式庫緊密的耦合在一起,導致日後維護上的困難。
解決方案
如果你的程式碼需要用到幾十種功能複雜的函式庫,但卻只需要這些函式庫中的少部分功能,那麼使用外觀模式會非常方便。
新建一個外觀類別,這個類別會為許多複雜的子系統提供一個簡單的介面,與直接調用子系統相比,外觀類別提供的功能雖然比較有限,但卻是客戶端真正需要的功能。
例子
上傳影片到社交媒體網站的應用程式,上傳影片的部分需要使用專業的影片編碼函式庫,編碼函式庫雖然提供很多功能,但我們只需要函示庫中的 encode(filename, format)
方法 (以檔案名稱與格式為參數進行編碼)。
在這樣的情況下,你可以創建一個外觀類別來操作影片編碼函示庫,這樣客戶端只需要調用外觀類別,可以與編碼函式庫隔離開來。
架構
- Facade (外觀類別):為多個複雜子系統提供一個簡單的高級介面,會負責子系統的初始化與調用。
- Additional Facade (附加外觀):因為外觀類別需要依賴多個子系統,為了避免讓外觀類別太複雜,較為不相關的功能可以再切出來做成一個外觀。
- Complex Subsystem (複雜子系統):由數個不同的子系統組成的專門系統,如果要使用這些子系統完成工作,你可能需要深入了解每個子系統,例如按照正確的順序初始化物件或為其提供正確的參數。
使用 PHP 實作外觀模式
假設我們有一個下載 Youtube 影片的應用程式,這個下載影片的過程需要操作 Youtube API 與 FFmpeg 函式庫。
我們可以使用外觀模式操作 Youtube API 與 FFmpeg 實作一個下載影片的方法,客戶端只需要操作外觀類別即可。
假設我們需要調用 Youtube API 與 FFmpeg 這樣較為複雜的子系統。
// The YouTube API subsystem.
class YouTube
{
public function fetchVideo(): string
{
// do some stuff
}
public function saveAs(string $path): void
{
// do some stuff
}
// ...more methods
}
// The FFmpeg subsystem (a complex video/audio conversion library).
class FFMpeg
{
public static function create(): FFMpeg
{
// do some stuff
}
public function open(string $video): void
{
// do some stuff
}
// ...more methods
}
class FFMpegVideo
{
public function filters(): self
{
// do some stuff
}
public function resize(): self
{
// do some stuff
}
public function synchronize(): self
{
// do some stuff
}
public function frame(): self
{
// do some stuff
}
public function save(string $path): self
{
// do some stuff
}
// ...more methods
}
這時候我們可以實作一個外觀類別 YoutubeDownloader
,操作 Youtube API 與 FFmpeg 來下載影片。
class YouTubeDownloader
{
protected $youtube;
protected $ffmpeg;
public function __construct(string $youtubeApiKey)
{
$this->youtube = new YouTube($youtubeApiKey);
$this->ffmpeg = new FFMpeg();
}
public function downloadVideo(string $url): void
{
echo "Fetching video metadata from youtube...\\n";
// $title = $this->youtube->fetchVideo($url)->getTitle();
echo "Saving video file to a temporary file...\\n";
// $this->youtube->saveAs($url, "video.mpg");
echo "Processing source video...\\n";
// $video = $this->ffmpeg->open('video.mpg');
echo "Normalizing and resizing the video to smaller dimensions...\\n";
// $video
// ->filters()
// ->resize(new FFMpeg\\Coordinate\\Dimension(320, 240))
// ->synchronize();
echo "Capturing preview image...\\n";
// $video
// ->frame(FFMpeg\\Coordinate\\TimeCode::fromSeconds(10))
// ->save($title . 'frame.jpg');
echo "Saving video in target formats...\\n";
// $video
// ->save(new FFMpeg\\Format\\Video\\X264(), $title . '.mp4')
// ->save(new FFMpeg\\Format\\Video\\WMV(), $title . '.wmv')
// ->save(new FFMpeg\\Format\\Video\\WebM(), $title . '.webm');
echo "Done!\\n";
}
}
客戶端只需要操作外觀類別,可以與複雜的 Youtube API 還有 FFmpeg 函式庫隔離開來。
function clientCode(YouTubeDownloader $facade)
{
// ...
$facade->downloadVideo("https://www.youtube.com/watch?v=example");
// ...
}
$facade = new YouTubeDownloader("APIKEY-XXXXXXXXX");
clientCode($facade);
外觀模式的優點
- 讓客戶端能與複雜子系統隔離開來
- 為複雜子系統的常用功能,提供一個快捷使用的方式