不談抽象類可以有實現等語法糖的問題,本文主要講在語義層面抽象類和介面的本質區別、以及使用以及選擇。 一、介紹 抽象類,首先是個類,類是對現實世界中對象的建模模型,抽象類是對類整體的抽象描述,包含方法,以及屬性。介面是對類某特性行為的抽象。 對抽象類的繼承才是Is-A的關係,對介面的實現,則是“有沒有 ...
不談抽象類可以有實現等語法糖的問題,本文主要講在語義層面抽象類和介面的本質區別、以及使用以及選擇。
一、介紹
抽象類,首先是個類,類是對現實世界中對象的建模模型,抽象類是對類整體的抽象描述,包含方法,以及屬性。介面是對類某特性行為的抽象。
對抽象類的繼承才是Is-A的關係,對介面的實現,則是“有沒有”的關係。比如鳥和飛機都有飛行這個特性,這個時候可以把飛行這個特性設計為介面:IFly。然後再讓Airplane和Bird實現IFly這個介面,這樣Airplane和Bird則擁有了飛行這個屬性。
接下來飛機可能有多種,鳥也有多種,他們飛行的方法完全不同,這樣就可以把Airplan和Bird設計成抽象類,讓不同的飛機和鳥進行繼承。
類圖:
代碼:
public abstract class AbstractAirplane : IFly { public void Fly() { this.PrepareToFly(); this.Step1(); this.Step2(); } private void PrepareToFly() { } protected abstract void Step1(); protected abstract void Step2(); }
public abstract class AbstractBird : IFly { public void Fly() { this.PrepareToFly(); this.Step1(); this.Step2(); } private void PrepareToFly() { } protected abstract void Step1(); protected abstract void Step2(); }
在上面的設計中,IFly定義了飛的行為:
- AbstractAirplane是實現了該行為,並且定義了飛機飛行的模型,PrepareToFly,Step1,Step2。由於不同飛機的Step1和Step2各不相同,所以定義為Abstract,讓子類自己實現
- AbstractBird實現了該行為,並且定義了鳥的飛行模型,PrepareToFly,Step1,Step2。由於不同鳥的Step1和Step2各不相同,所以定義為Abstract,讓子類自己實現
上面的例子可以看出,抽象類一種模版式的設計,介面是一種行為規範。
模版式設計:如果共用部分要修改,比如上圖中的PrepareToFly,則只需要修改共用部分,不需要修改其他,對於抽象類來說,如果要增加方法,可以直接操作父類,子類可以不知情。
行為規範:如果介面要變更,實現這個介面的類都要修改。
二、介面與抽象類的選擇
現實中門有兩個方法Open(), Close(),定義方式分別如下所示:
使用介面方式定義門:
interface IDoor { void Open(); void Close(); }
使用抽象類方式定義門:
abstract class AbstractDoor { public abstract void Open(); public abstract void Close(); }
剛開始一切都OK,但是隨著用戶需求的發展,要給門加入報警(Alarm)功能,現在怎麼做?
- 把Alarm放入AbstractDoor中,這樣所有繼承自這個類的門都會有Alarm功能,但是並不是所有的門都有報警功能,這樣違反了liskov替換原則
- 把Alarm放入IDoor介面中,需要Alarm的門都實現這個介面,那麼需要報警的門得同時實現Open和Close功能,其實有些報警的門不一定需要Open和Close功能,
分析:
從上面可以看出,Open和Close是一個門的固有屬性,Alarm屬於門的延伸附加行為,最好的解決辦法是針對Alarm單獨設計一個介面:
public interface IAlarm { void Alarm(); }
普通門的實現:
public class NormalDoor : AbstractDoor { public override void Open() { } public override void Close() { } }
具有報警功能的門的實現:
public class AlarmDoor : AbstractDoor,IAlarm { public override void Open() { } public override void Close() { } public void Alarm() { } }
只能報警的設備的實現:
public class AlarmDevice : IAlarm { public void Alarm() { } }