August 18 11:49~13:09
▲執行在macOS上的EiffelStudio
依合約設計
前幾天為了準備「敏捷開發懶人包:物件導向技能」課程範例,安裝了EiffelStudio。這是一個支援Eiffel語言的開發環境,有商業版和免費的社群版,Teddy安裝的是社群版。
Eiffel是一個支援Design By Contract(DBC;依合約設計)的語言,DBC的概念非常棒,可以協助開發人員思考與設計元件介面,透過合約(pre-condition、post-condition、class invariant)規範元件的行為。可惜的是主流語言並沒有支援DBC,原本Teddy想找Java和C#語言支援DBC的第三方元件,但後來發現幾乎都已經停止開發而且對於DBC的支援也不夠直接,所以幹脆不求人,回到Eiffel語言身上。
***
安裝EiffelStudio
EiffelStudio是一個跨平台的開發工具,支援Windows、macOS、Linux,但需要事先安裝C compiler。在Windows上可以使用Visual C++,在macOS使用Xcode,在Linux上用gcc。在Software Installation for EiffelStudio網頁有不同平台的安裝方式,Teddy在Windows 7 + Visual Studio 2015/2017以及macOS 10.12都安裝過。但是在Parallels中的Windows 10 + Visual Studio 2017安裝好之後出現找不到compiler的錯誤訊息,一時也不知道為什麼,後來就放棄Windows 10…Orz。
EiffelStudio開發環境和Eclipse、IntelliJ、Visual Studio 比起來相對「原始」很多,用起來有種時間回到10幾年前寫程式的感覺。不過當作練習DBC已經綽綽有餘,免費的就不要太挑剔。
***
合約
▼下圖中的MYSTACK類別是Teddy寫的用來展示最簡單DBC的例子,程式中的require、ensure、invariant是Eiffel語言的keyword,分別用來指定pre-condition、post-condition、class invariant。
***
里氏替代原則
合約除了在定義單一類別有用以外,在繼承的時候還可以用來確保是否違反Liskov substitution principle(LSP;里氏替代原則),又稱為subcontracting(子合約)。
▼看一個例子,PERSON類別的set_age函數有一個pre-condition,要求年紀必須大於或等於0。
▼CUSTOMER類別繼承自PERSON,它重新定義set_age函數,增加了一個pre-condition: 年齡大於等於18。
▼實際測試CUSTOMER的set_age函數,故意傳5進去,請問這個例子是否違反了LSP?答案是沒有違反,因為在既成的關係中,子類別的和父類別的pre-condition兩者是or關係。也就是說子類別只要符合父類別或自類別所定義的pre-condition其中之一即可,翻成白話文就是:子類別要求的不能比父類別要求的還要多。
再看一個《Agile Software Development》書上的例子, SQUARE繼承自RECTANGLE。
▼寫一個測試案例。
▼程式執行之後Eiffel告訴我們SQUARE的set_height函數的post-condition: width = old width 這一條被違反了。如果鄉民們有仔細看SQUARE的程式碼,可能會覺得很奇怪,SQUARE又沒有定義這一條post-condition?沒錯,這一條post-condition定義在RECTANGLE身上。合約應用在繼承的時候,pre-condition是or關係,post-condition是and關係。LSP用合約來解釋就可以看得很清楚。
***
雖然支援依合約設計的程式語言很少,但這個觀念卻非常簡單且實用,除了可以幫助我們設計元件介面,也可以因此自動產生測試案例,或者至少可以幫助我們設計測試案例。有空的時候安裝起來玩一下也不錯。
▼正港的Eiffel在這裡
***
友藏內心獨白:。合約不死。
沒有留言:
張貼留言