經過幾個禮拜的思考,我認爲jon hoffman先生說得有理,我們應該傾向簡單的設計,而Swift不是C++,因此不應該試圖模仿
回到Poilcy的設計上,我們最初的期望是:在編譯期選擇型別,並且透過隱式約束,而不是顯式
先看看C++的案例
struct SQLServer {
void connect();
// ...
}
struct MySQL {
void connect();
// ...
}
class DB<DBImpl> {
public:
void connect() {
DBImpl().connect();
}
}
我們建構泛型DBImpl的實例並且呼叫其方法,我們知道C++的樣板會在"使用"時展開並檢查
因此我們不會有執行期(runtime)成本,且使用時檢查此點非常重要,這是我們之後會在Swift遇到的難題
那麼我們要如何在Swift中實現一樣的能力呢?
首先我們要了解到Swift並非在使用時檢查,而是宣告時檢查,這只得剛才的程式寫法完全無法編譯通過:首先編譯器會告訴你DBImpl沒有建構式(在C++,如果你沒有宣告建構式,編譯器會幫你寫一個),接著編譯器認為DBImpl沒有connect方法
我最開始的解決方案是利用constraint讓編譯器得到這些資訊
protocol DBPolicy {
init()
func connect()
}
class SQLServer : DBPolicy {
required init() { ... }
func connect() { ... }
// ...
}
class MySQL : DBPolicy {
required init() { ... }
func connect() { ... }
// ...
}
class DB<DBImpl: DBPolicy> {
func connect() {
DBImpl().connect()
}
}
可是這樣一來,程式就變得沒有彈性,而且這樣搞,寫多型不就好了(我們只享受到一點效能優化,但是沒有得到最重要的彈性)
接下來我學習到where clauses這個特性,我終於找到了正確的實作方式
class SQLServer {
required init() { ... }
func connect() { ... }
// ...
}
class MySQL {
required init() { ... }
func connect() { ... }
// ...
}
class DB<DBImpl> { ... }
extension DB where DBImpl == MySQL {
func connect() {
MySQL().connect()
}
}
這個方案與我們所想像的差不多,確實符合了隱式約束以及編譯期選擇
但是卻有很明顯的重複性,我們有幾個Policy就需要extension幾次,且所做的事都差不多,只能說Swift不是C++,XD
沒有留言:
張貼留言