在大致規劃好目錄架構之後,就可以開始模擬使用情境,設計出一呼叫套件的方式,把這個專案作品的使用範例先寫出來。
你可能會想說:「哇勒,這瞎咪碗糕?什麼程式都還沒開始寫,直接跳到最後一步啦?」
且慢,先不要酸啊 ><|| 讓筆者先提個例子先。
不知道讀者們有無參觀過預售屋的經驗?
在銷售展示中心旁,只見開始挖地基、架設鷹架的工地被清綠色的鐵皮圍籬圍起來。萬丈高樓連平地都還沒開始起,就開始賣起這未來的房子啦。只見展示中心的中央擺著漂亮樣品屋的模型。電視螢幕撥放著房子蓋好後會是什麼樣子的 3D 模擬圖。
這個時候營造廠才正要按著建築設計圖施作,就為了要達到最後和 3D 模擬圖一模一樣。
雖然程式設計和前段生活經驗的例子中的建築工程,在本質上有很大的差異。但把事先模擬的這種概念拿來用,就和蓋房子一樣,最後的結果和一開始的架構都有了,就只差中間的過程了。
在真正寫程式之前,把要如何使用這個套件的範例寫出來,而這個範例就是最終的範例。而我們要做的就是依規劃好的架構,開始進行程式設計,讓最後的這個範例真的可以用。
基本架構在 Day 16 已經先行規劃好了。大概的架構如下。
.
├── src
│ └── SimpleCache
│ ├── Cache.php
│ ├── CacheProvider.php
│ ├── Driver
│ │ ├── File.php
│ │ ├── Myql.php
│ │ ├── Redis.php
│ │ └── Sqlite.php
│ └── Exception
│ ├── CacheArgumentException.php
│ └── CacheException.php
└── tests
└── SimpleCache
├── CacheTest.php
├── ...
└── ...
範例:/day-17/example-1/Main.php
<?php
use Shieldon\SimpleCache\Cache;
use Shieldon\SimpleCache\Driver\Redis;
class Main
{
public function example()
{
$cache = new Cache('redis');
$cache->set('foo', 'bar', 3600);
echo $cache->get('foo');
// echo: bar
}
}
Cache
類別的建構子參數直接塞一個字串 redis
,然後再內部實例化 Redis
?
這樣似乎綁死了這個類別只能使用這個專案作品中已實作的 Driver。
No, No, No。很不 OK 捏。
想到之前 Day 8 提到的依賴注入 設計模式。改寫成如下:
範例:/day-17/example-2/Main.php
<?php
use Shieldon\SimpleCache\Cache;
use Shieldon\SimpleCache\Driver\Redis;
class Main
{
public function example()
{
$redis = new Redis('127.0.0.1', 6379);
$cache = new Cache($redis);
$cache->set('foo', 'bar', 3600);
echo $cache->get('foo');
// echo: bar
}
}
先實例化 Redis
,然後注入 Cache
類別的建構子參數,這樣會不會比較彈性?
這樣做的話,就算不是專案裡已經有的 Driver,也以使用別人已經實作 PSR-16 的 CacheInterface
的類別,直接注入使用囉!
雖然依賴注入的點子很不錯,但就覺得那裡怪怪的?
參數需要順序排列,而不同的 Driver 例如 SQLite
,只需要一個參數,也就是一個絕對路徑目錄的字串。
而 Redis
這個 Driver 如果要連到外部伺服器的 Redis 服務,需要帳號及密碼,那麼參數又更多了。
範例:/day-17/example-3/Main.php
<?php
use Shieldon\SimpleCache\Cache;
use Shieldon\SimpleCache\Driver\Redis;
class Main
{
public function example()
{
$redis = new Redis([
'host' => '127.0.0.1',
'port' => 6379,
]);
$cache = new Cache($redis);
$cache->set('foo', 'bar', 3600);
echo $cache->get('foo');
// echo: bar
}
public function example2()
{
$sqlite = new Sqlite([
'path' => '/home/app/writable',
]);
$cache = new Cache($sqlite);
$cache->set('foo2', 'bar2', 3600);
echo $cache->get('foo2');
// echo: bar2
}
}
把 Driver 的建構子參數改成只接受一個陣列類型的參數吧。這樣使用者就可以只記得每個 Driver 只有一個參數,而參數的陣列欄位定義出明確的 key 值在文件中方便查閱。
程式設計好玩的地方就是,同樣的功能其實可以有很多不同的寫法。筆者的寫法並不是絕對。以前也常發生自己覺得很不錯的寫法,在一年後被自己嫌棄而整個重寫 XD。
不過在創造一個全新的套件時,用模擬的範例,或許會發現基本架構需要修改來配合期待的範例結果,也說不定。用這樣倒推回去思考整個設計的架構,在設計上是非常有幫助的唷!
本篇原始碼可在此瀏覽。我們明天見囉。
本文同步更新於 TerryL 部落格 Day 17 - PHP 套件設計實戰 (3) 模擬使用情境,歡迎前往討論。