概念引入 ●什麼是介面? 介面是包含一組虛方法的抽象類型,其中每一種方法都有其名稱、參數和返回值。介面方法不能包含任何實現,CLR允許介面可以包含事件、屬性、索引 器、靜態方法、靜態欄位、靜態構造函數以及常數。但是註意:C#中不能包含任何靜態成員。一個類可以實現多個介面,當一個類繼承某個介面時,它不 ...
1)在繼承抽象類時,必須覆蓋該類中的每一個抽象方法,而每個已實現的方法必須和抽象類中指定的方法一樣,接收相同數目和類型的參數,具有同樣的返回值,這一點與介面相同。
2)當父類已有實際功能的方法時,該方法在子類中可以不必實現,直接引用的方法,子類也可以重寫該父類的方法(繼承的概念)。
3)而實現 (implement)一個介面(interface)的時候,是一定要實現介面中所定義的所有方法,而不可遺漏任何一個。
4)另外,抽象類不能產生對象的,但可以由它的實現類來聲明對象。
A. 兩者都是抽象類,都不能實例化。
B. interface實現類及abstrct class的子類都必須要實現已經聲明的抽象方法。2. 不同
A. interface需要實現,要用implements,而abstract class需要繼承,要用extends。
B. 一個類可以實現多個interface,但一個類只能繼承一個abstract class。
C. interface強調特定功能的實現,而abstract class強調所屬關係。
D. 儘管interface實現類及abstrct class的子類都必須要實現相應的抽象方法,但實現的形式不同。interface中的每一個方法都是抽象方法,都只是聲明的(declaration, 沒有方法體),實現類必須要實現。而abstract class的子類可以有選擇地實現。這個選擇有兩點含義:
一是Abastract class中並非所有的方法都是抽象的,只有那些冠有abstract的方法才是抽象的,子類必須實現。那些沒有abstract的方法,在Abstrct class中必須定義方法體。
二是abstract class的子類在繼承它時,對非抽象方法既可以直接繼承,也可以覆蓋;而對抽象方法,可以選擇實現,也可以通過再次聲明其方法為抽象的方式,無需實現,留給其子類來實現,但此類必須也聲明為抽象類。既是抽象類,當然也不能實例化。
E. abstract class是interface與Class的中介。
interface是完全抽象的,只能聲明方法,而且只能聲明pulic的方法,不能聲明private及protected的方法,不能定義方法體,也不能聲明實例變數。然而,interface卻可以聲明常量變數,並且在JDK中不難找出這種例子。但將常量變數放在interface中違背了其作為介面的作用而存在的宗旨,也混淆了interface與類的不同價值。如果的確需要,可以將其放在相應的abstract class或Class中。
abstract class在interface及Class中起到了承上啟下的作用。一方面,abstract class是抽象的,可以聲明抽象方法,以規範子類必須實現的功能;另一方面,它又可以定義預設的方法體,供子類直接使用或覆蓋。另外,它還可以定義自己的實例變數,以供子類通過繼承來使用。3. interface的應用場合
A. 類與類之前需要特定的介面進行協調,而不在乎其如何實現。
B. 作為能夠實現特定功能的標識存在,也可以是什麼介面方法都沒有的純粹標識。
C. 需要將一組類視為單一的類,而調用者只通過介面來與這組類發生聯繫。
D. 需要實現特定的多項功能,而這些功能之間可能完全沒有任何聯繫。4. abstract class的應用場合
一句話,在既需要統一的介面,又需要實例變數或預設的方法的情況下,就可以使用它。最常見的有:
A. 定義了一組介面,但又不想強迫每個實現類都必須實現所有的介面。可以用abstract class定義一組方法體,甚至可以是空方法體,然後由子類選擇自己所感興趣的方法來覆蓋。
B. 某些場合下,只靠純粹的介面不能滿足類與類之間的協調,還必需類中表示狀態的變數來區別不同的關係。abstract的中介作用可以很好地滿足這一點。
C. 規範了一組相互協調的方法,其中一些方法是共同的,與狀態無關的,可以共用的,無需子類分別實現;而另一些方法卻需要各個子類根據自己特定的狀態來實現特定的功能。
概念引入
●什麼是介面?
介面是包含一組虛方法的抽象類型,其中每一種方法都有其名稱、參數和返回值。介面方法不能包含任何實現,CLR允許介面可以包含事件、屬性、索引 器、靜態方法、靜態欄位、靜態構造函數以及常數。但是註意:C#中不能包含任何靜態成員。一個類可以實現多個介面,當一個類繼承某個介面時,它不僅要實現 該介面定義的所有方法,還要實現該介面從其他介面中繼承的所有方法。
定義方法為:
以下為引用的內容:
public interface System.IComparable
{
int CompareTo(object o);
}
public class TestCls: IComparable
{
public TestCls()
{
}
private int _value;
public int Value
{
get { return _value; }
set { _value = value; }
}
public int CompareTo(object o)
{
//使用as模式進行轉型判斷
TestCls aCls = o as TestCls;
if (aCls != null)
{
//實現抽象方法
return _value.CompareTo(aCls._value);
}
}
}
●什麼是抽象類?
抽象類提供多個派生類共用基類的公共定義,它既可以提供抽象方法,也可以提供非抽象方法。抽象類不能實例化,必須通過繼承由派生類實現其抽象方法, 因此對抽象類不能使用new關鍵字,也不能被密封。如果派生類沒有實現所有的抽象方法,則該派生類也必須聲明為抽象類。另外,實現抽象方法由 overriding方法來實現。
定義方法為:
以下為引用的內容:
/// <summary>
/// 定義抽象類
/// </summary>
abstract public class Animal
{
//定義靜態欄位
static protected int _id;
//定義屬性
public abstract static int Id
{
get;
set;
}
//定義方法
public abstract void Eat();
//定義索引器
public string this[int i]
{
get;
set;
}
}
/// <summary>
/// 實現抽象類
/// </summary>
public class Dog: Animal
{
public static override int Id
{
get {return _id;}
set {_id = value;}
}
public override void Eat()
{
Console.Write("Dog Eats.")
}
}
3. 相同點和不同點
3.1 相同點
●都不能被直接實例化,都可以通過繼承實現其抽象方法。
●都是面向抽象編程的技術基礎,實現了諸多的設計模式。
3.2 不同點
●介面支持多繼承;抽象類不能實現多繼承。
●介面只能定義抽象規則;抽象類既可以定義規則,還可能提供已實現的成員。
●介面是一組行為規範;抽象類是一個不完全的類,著重族的概念。
●介面可以用於支持回調;抽象類不能實現回調,因為繼承不支持。
●介面只包含方法、屬性、索引器、事件的簽名,但不能定義欄位和包含實現的方法;抽象類可以定義欄位、屬性、包含有實現的方法。
●介面可以作用於值類型和引用類型;抽象類只能作用於引用類型。例如,Struct就可以繼承介面,而不能繼承類。
通過相同與不同的比較,我們只能說介面和抽象類,各有所長,但無優略。在實際的編程實踐中,我們要視具體情況來酌情量才,但是以下的經驗和積累,或 許能給大家一些啟示,除了我的一些積累之外,很多都來源於經典,我相信經得起考驗。所以在規則與場合中,我們學習這些經典,最重要的是學以致用,當然我將 以一家之言博大家之笑,看官請繼續。
3.3 規則與場合
1.請記住,面向對象思想的一個最重要的原則就是:面向介面編程。
2.藉助介面和抽象類,23個設計模式中的很多思想被巧妙的實現了,我認為其精髓簡單說來就是:面向抽象編程。
3.抽象類應主要用於關係密切的對象,而介面最適合為不相關的類提供通用功能。
4.介面著重於CAN-DO關係類型,而抽象類則偏重於IS-A式的關係;
5.介面多定義對象的行為;抽象類多定義對象的屬性;
6.介面定義可以使用public、protected、internal 和private修飾符,但是幾乎所有的介面都定義為public,原因就不必多說了。
7.“介面不變”,是應該考慮的重要因素。所以,在由介面增加擴展時,應該增加新的介面,而不能更改現有介面。
8.儘量將介面設計成功能單一的功能塊,以.NET Framework為例,IDisposable、IDisposable、IComparable、IEquatable、IEnumerable等都只包含一個公共方法。
9.介面名稱前面的大寫字母“I”是一個約定,正如欄位名以下劃線開頭一樣,請堅持這些原則。
10.在介面中,所有的方法都預設為public。
11. 如果預計會出現版本問題,可以創建“抽象類”。例如,創建了狗(Dog)、雞(Chicken)和鴨(Duck),那麼應該考慮抽象出動物 (Animal)來應對以後可能出現風馬牛的事情。而向介面中添加新成員則會強制要求修改所有派生類,並重新編譯,所以版本式的問題最好以抽象類來實現。
12.從抽象類派生的非抽象類必須包括繼承的所有抽象方法和抽象訪問器的實實現。
13.對抽象類不能使用new關鍵字,也不能被密封,原因是抽象類不能被實例化。
14.在抽象方法聲明中不能使用 static 或 virtual 修飾符。
以上的規則,我就厚顏無恥的暫定為T14條吧,寫的這麼累,就當一時的獎賞吧。大家也可以互通有無,我將及時修訂。
4. 經典示例
4.1 絕對經典
.NET Framework是學習的最好資源,有意識的研究FCL是每個.NET程式員的必修課,關於介面和抽象類在FCL中的使用,我有以下的建議:
1.FCL對集合類使用了基於介面的設計,所以請關註System.Collections中關於介面的設計實現;
2.FCL對數據流相關類使用了基於抽象類的設計,所以請關註System.IO.Stream類的抽象類設計機制。
4.2 別樣小菜
下麵的實例,因為是我的理解,因此給經典打上“相對”的記號,至於什麼時候晉升為“絕對”,就看我在.NET追求的路上,是否能夠一如既往的如此執 著,因此我將把相對重構到絕對為止(呵呵)。 本示例沒有闡述抽象類和介面在設計模式中的應用,因為那將是另一篇有討論價值的文本,本文著眼與概念和原則的把握,但是真正的應用來自於具體的需求規範。
設計結構如圖所示:
1. 定義抽象類
以下為引用的內容:
public abstract class Animal
{
protected string _name;
//聲明抽象屬性
public abstract string Name
{
get;
}
//聲明抽象方法
public abstract void Show();
//實現一般方法
public void MakeVoice()
{
Console.WriteLine("All animals can make voice!");
}
}
2. 定義介面
以下為引用的內容:
public interface IAction
{
//定義公共方法標簽
void Move();
}
3. 實現抽象類和介面
以下為引用的內容:
public class Duck : Animal, IAction
{
public Duck(string name)
{
_name = name;
}
//重載抽象方法
public override void Show()
{
Console.WriteLine(_name + " is showing for you.");
}
//重載抽象屬性
public override string Name
{
get { return _name;}
}
//實現介面方法
public void Move()
{
Console.WriteLine("Duck also can swim.");
}
}
public class Dog : Animal, IAction
{
public Dog(string name)
{
_name = name;
}
public override void Show()
{
Console.WriteLine(_name + " is showing for you.");
}
public override string Name
{
get { return _name; }
}
public void Move()
{
Console.WriteLine(_name + " also can run.");
}
}
4. 客戶端實現
以下為引用的內容:
public class TestAnmial
{
public static void Main(string [] args)
{
Animal duck = new Duck("Duck");
duck.MakeVoice();
duck.Show();
Animal dog = new Dog("Dog");
dog.MakeVoice();
dog.Show();
IAction dogAction = new Dog("A big dog");
dogAction.Move();
}
}
5. 他山之石
正所謂真理是大家看出來的,所以將園子里有創新性的觀點潛列於此,一是感謝大家的共用,二是完善一家之言的不足,希望能夠將領域形成知識,受用於我,受用於眾。
●[url=]dunai[/url]認為:抽象類是提取具體類的公因式,而介面是為了將一些不相關的類“雜湊”成一個共同的群體。至於他們在各個語言中的句法,語言細節並不是我關心的重點。
●樺山澗的收藏也很不錯。
●Artech認為:所代碼共用和可擴展性考慮,儘量使用Abstract Class。當然介面在其他方面的優勢,我認為也不可忽視。
●shenfx認為:當在差異較大的對象間尋求功能上的共性時,使用介面;當在共性較多的對象間尋求功能上的差異時,使用抽象基類。
最後,MSDN的建議是:
●如果預計要創建組件的多個版本,則創建抽象類。抽象類提供簡單易行的方法來控制組件版本。通過更新基類,所有繼承類都隨更改自動更新。另一方面,介面一旦創建就不能更改。如果需要介面的新版本,必須創建一個全新的介面。
●如果創建的功能將在大範圍的全異對象間使用,則使用介面。抽象類應主要用於關係密切的對象,而介面最適合為不相關的類提供通用功能。
●如果要設計小而簡練的功能塊,則使用介面。如果要設計大的功能單元,則使用抽象類。
●如果要在組件的所有實現間提供通用的已實現功能,則使用抽象類。抽象類允許部分實現類,而介面不包含任何成員的實現。
6. 結論
介面和抽象類,是論壇上、課堂間討論最多的話題之一,之所以將這個老話題拿出來再議,是因為從我的體會來說,深刻的理解這兩個面向對象的基本內容, 對於盤活面向對象的抽象化編程思想至關重要。本文基本概況了介面和抽象類的概念、異同和使用規則,從學習的觀點來看,我認為這些總結已經足以表達其核心。 但是,對於面向對象和軟體設計的深入理解,還是建立在不斷實踐的基礎上,Scott說自己每天堅持一個小時用來寫Demo,那麼我們是不是更應該勤於鍵盤 呢。對於介面和抽象類,請多用而知其然,多想而知其奧吧。