前些陣子有人問我「Abstract 與 Interface 的區別?」,腦袋中閃過過去所有使用過 Abstract 及 Interface 的情境,整理之後給出我實務上設計的時候是怎麼操作 Abstract 與 Interface 的答案,回頭想想這樣有點文不對題,對方似乎也沒有得到答案,不過這樣的過程讓我有種見山不是山的感覺,促使我回頭想想在思考這個問題答案的過程當中不單純的點是什麼?以及為何我給出這樣的答案?
Abstract 與 Interface 的區別?
MSDN 的定義
來自 IBM developerWorks 的見解
IBM developerWorks 的文章裡面舉了一個例子
abstract class Door
{
abstract void Open();
abstract void Close();
}
interface IAlarm
{
void Alarm();
}
class AlarmDoor : Door, IAlarm
{
void Open() { }
void Close() { }
void Alarm() { }
}
概念性的 Door 要增加 Alarm 的功能,應該將 Alarm() 方法定義在 IAlarm 的 interface 裡面,而 AlarmDoor 這個 concrete class 則要繼承 Door、實作 IAlarm。
這個世界並不是這麼的單純
剛剛舉的例子乍看之下沒有問題,這是正確的設計方式,但是今天如果是一家做安全系統的公司,而這家公司所生產的門全部都有警報器,是不是就不那麼單純了?
不單純的點在於遇到的問題都不太一樣,沒辦法用一套解決方案適用所有的問題,如果公司所生產的門全部都有警報器,而那樣的設計可能就不太恰當了。
再舉個例子
abstract class Order
{
public int Id { get; set; }
public ICollection<Product> ProductList { get; set; }
public abstract Customer GetCustomer(int customerId);
}
interface IDelivery
{
string GetAddress();
string GetStatus();
}
class AuctionOrder : Order, IDelivery
{
public override Customer GetCustomer(int customerId)
{
DoSomething();
}
public string GetAddress()
{
DoSomething();
}
public string GetStatus()
{
DoSomething();
}
}
概念性的訂單(Order) 有 Id, ProductList, GetCustomer() 屬性及方法,IDelivery 有 GetAddress(), GetStatus() 方法,而 AuctionOrder 繼承自 Order、實作 IDelivery,這樣設計好像還可以。
但是實務上我會這樣設計
abstract class Order
{
public int Id { get; set; }
public ICollection<Product> ProductList { get; set; }
}
interface IOrdering
{
Customer GetCustomer(int customerId);
}
interface IDelivery
{
string GetAddress();
string GetStatus();
}
class AuctionOrder : Order, IOrdering, IDelivery
{
public Customer GetCustomer(int customerId)
{
DoSomething();
}
public string GetAddress()
{
DoSomething();
}
public string GetStatus()
{
DoSomething();
}
}
外加一個 IOrdering 定義 Order 的行為,C# 及 Java 的類別有一個限制,就是只能繼承自一個親生爸爸,但是可以有很多乾爸爸(Interface),在開發的過程當中,我經常需要重構、使用者經常變更需求,因此在設計時會率先使用 Interface 定義合約,視需求有必要時再抽出來定義成 Abstract。
不單純的點
可是有人就是會又有不同的意見,這個就是我要提的重點「問題不是 XXX 這麼單純而已」,每個人遇到的情境不一樣,在設計軟體的過程當中我們應該要不停地問自己三個問題:
- 使用者的需求是什麼?
- 使用者遇到的問題是什麼?
- 有沒有辦法舉幾個實際例子?多給幾個情境?
結論
學習模式三階段:「守、破、離」,如果我們一直處於在「守」的階段,當問題變得複雜時,難保不會發生這種情況。
這個工具應該怎麼被用?以及,如何使用這個工具?
上面這兩個問題我個人比較重視後者,當然前者也很重要,我們必須先透過學習,了解工具的使用方式,然後再透過思考解決方案的過程去解放我們對這個工具的成見,後者才是我學習這個工具的價值。
任何的理論、原理、技術都是工具而已,學習它們就好像在自己的意識中種下一顆種子,因緣具足成熟之時,自會有所連結而生根發芽、開花結果,人類的大腦普遍記憶力有限,但是連結力很強,連結愈多想像力就愈多,可選擇的項目就愈多,面對問題時連結力會帶領我們連結這些工具,產生解決方案,進而讓工具受到活用。
有黑、有白,有高、有矮,有男、有女…,併發出很多複雜的問題,這個世界才顯得精彩,太單純,這個世界就太單調了。