第一節:從面向對象思想(oo)開發、接口、抽象類以及二者比較


一. 面向對象思想

1. 面向過程(OP)和面向對象(OO)的區別:

  (1):面向過程就是排着用最簡單的代碼一步一步寫下去,沒有封裝,當業務復雜的時候,改動就很麻煩了

  (2):面向對象將復雜的業務分離出來幾個類,然后將業務封裝到類中的方法進行實現,很簡潔。

2. 面向對象的三大特征

(1). 封裝:將一些行為以類為單位進行包裹起來,然后通過類進行調用(如People類),可以利用private、public、protected靈活控制屬性的可訪問性。

好處:

  ①:保證數據安全(靈活使用private、public、protected進行控制)

  ②:屏蔽細節,只要方法名不變,可以隨意擴展。

  ③:代碼重用

(2). 繼承:通過繼承,子類可以擁有父類的一切動作(如Student類繼承People類)

(3). 多態:多態有很多種。,

(補充“老楊”的經典語錄:父類類型的變量可以指向子類類型的對象,調用方法的時候調用的是子類對象的實現。里氏替換原則是多態的另一種叫法,如下面的例子④)

  ①:通過不同子類繼承同一個父類,實現多態(類似的還有子類繼承抽象類、或者實現接口)

  ②:方法的重載本身就是一種多態

  ③:利用默認參數實現方法多態(利用命名參數實現方法的重載,即方法的多態)

  ④:運行時的多態(里氏替換原則,聲明父類對象,調用虛方法,在子類覆寫或者不覆寫的情況下,分別調用子類方法或父類方法《只有在運行的時候才知道》)

 實體類代碼:

 1  /// <summary>
 2     /// People對一些行為進行封裝
 3     /// </summary>
 4     public class People
 5     {
 6         public int age { get; set; }
 7 
 8         private string name { get; set; }
 9 
10         protected string Sex { get; set; }
11 
12 
13         public void Play()
14         {
15             Console.WriteLine("play方法");
16         }
17 
18         /// <summary>
19         /// 子類可以對其覆寫也可以不進行覆寫
20         /// </summary>
21         public virtual void VirtualMethord1()
22         {
23             Console.WriteLine("我是父類中的虛方法");
24         }
25     }
26 
27  /// <summary>
28     /// 繼承People類
29     /// </summary>
30     public class Student : People
31     {
32 
33         /// <summary>
34         /// overload 方法重載實現方法的多態,同名方法不同實現形式
35         /// </summary>
36         /// <param name="id"></param>
37         public void Show(int id)
38         {
39             Console.WriteLine($"id為:{id}");
40         }
41         public void Show(string id)
42         {
43             Console.WriteLine($"id為:{id}");
44         }
45         public void Show(int id, string name)
46         {
47             Console.WriteLine($"id為:{id},name為:{name}");
48         }
49 
50         /// <summary>
51         /// 利用命名參數實現方法的重載,即方法的多態
52         /// </summary>
53         /// <param name="id"></param>
54         /// <param name="name"></param>
55         public void play(int id,string name = null)
56         {
57             if (name!=null)
58             {
59                 Console.WriteLine($"id為:{id},name為:{name}");
60             }
61             else
62             {
63                 Console.WriteLine($"id為:{id}");
64             }
65         }
66 
67 
68         public override void VirtualMethord1()
69         {
70             //base.VirtualMethord1();    //調用父類的虛方法
71             Console.WriteLine("我是子類中對方法的覆寫");
72         }
73 
74     }
75   /// <summary>
76     /// 繼承People類
77     /// </summary>
78     public class Teacher : People
79     {
80     }
View Code

多態的代碼:

 1           Console.WriteLine("主要是介紹多態的各種形式");
 2             //1. 繼承的方式實現多態,People有多種不同的實現形式(接口和抽象類的形式與之類似)
 3             People p1 = new People();
 4             People p2 = new Student();
 5             People p3 = new Teacher();
 6 
 7             //2. 方法的重載也是一種多態,即同名方法不同的實現形式
 8             Student s1 = new Student();
 9             s1.Show("1");
10             s1.Show(2);
11             s1.Show(1, "ypf");
12 
13             //3. 利用命名參數實現方法的重載,即方法的多態
14             Student s2 = new Student();
15             s2.play(12);
16             s2.play(12,"ypf");
17 
18             //4. 運行時的多態
19             People p4 = new Student();
20             p4.VirtualMethord1();        

調用結果:

 

 

二. 抽象類

  抽象類是提供多個派生類共享基類的公共定義,它既可以提供抽象方法,也可以提供非抽象方法。抽象類不能實例化,必須通過繼承由派生類實現其抽象方法,因此對抽象類不能使用new關鍵字,也不能被密封。如果派生類沒有實現所有的抽象方法,則該派生類也必須聲明為抽象類。另外,實現抽象方法由override方法來實現。

  抽象類的特點:

   (1). 抽象類是一個類,可以包含類的一切東西:屬性、字段、委托、方法。

   (2). 抽象方法必須包含在抽象類中,抽象方法沒有方法體,但抽象類中還可以包含普通的非抽象方法。

     (3). 抽象類和抽象方法的關鍵字均為abstract。

   (4). 繼承抽象類的子類中,必須顯式的覆寫(override)其父類中的所有抽象方法。

     (5). 抽象類不能直接被實例化,聲明的對象只能使用抽象類中已有的方法,不能使用子類新增的方法。

   (6). 同一個類只能繼承唯一一個父類。

 1. 下面新建一個BasePhone類,該類中有屬性、字段、委托、抽象方法、普通方法、虛方法、泛型抽象方法。

 1   public abstract class BasePhone
 2     {
 3         //1.屬性
 4         public string id { get; set; }
 5         public string name { get; set; }
 6         //2.字段
 7         public string userPwd = "maru";
 8         //3.委托
 9         public delegate void DoNothing();
10         //4.抽象方法
11         public abstract void Brand();
12         public abstract void System();
13         public abstract void Call();
14         //5.普通方法(繼承的子類無法對其進行覆寫,可以對其進行重寫)
15         public void show()
16         {
17             Console.WriteLine("這是父類show方法");
18         }
19         //6.虛方法(可以被任何繼承他的子類所覆寫)
20         public virtual void ShowVirtual()
21         {
22             Console.WriteLine("這是父類showVirtual方法");
23         }
24         //7.泛型方法抽象方法
25         public abstract void Do<T>();
26     }

 2. 新建一個iphone類,該類繼承了BasePhone類,則繼承了該類的屬性、字段、和方法,同時必須對其抽象方法(包括泛型抽象方法)進行覆寫,虛方法可以覆寫也可以不進行覆寫。

 1  public class iphone : BasePhone
 2     {
 3         /// <summary>
 4         /// 下面四個方法為對BasePhone中四個抽象方法的覆寫
 5         /// </summary>
 6         public override void Brand()
 7         {
 8             Console.WriteLine("iphone品牌");
 9         }
10 
11         public override void System()
12         {
13             Console.WriteLine("iphone系統");
14         }
15 
16         public override void Call()
17         {
18             Console.WriteLine("iphone電話");
19         }
20 
21         public override void Do<T>()
22         {
23             Console.WriteLine("iphone做的事情");
24         }
25         /// <summary>
26         /// 下面的ITunes方法為子類特有的方法
27         /// </summary>
28         public void ITunes()
29         {
30             Console.WriteLine("iphone連接到ITunes上");
31         }
32         /// <summary>
33         /// 下面的ShowVirtual方法覆寫父類中的虛方法
34         /// </summary>
35         public override void ShowVirtual()
36         {
37             Console.WriteLine("這是子類的showVirtual方法");
38         }
39         /// <summary>
40         /// 下面的show和父類中的一模一樣,但是覆蓋不了
41         /// </summary>
42         public void show()
43         {
44             Console.WriteLine("這是子類中的show方法");
45         }
46     
47     }

 3. 利用面向抽象的編程思想和里氏替換原則實例化一個iphone實例,該實例可以調用子類中4個覆寫父類中的抽象方法;可以調用子類對父類虛方法覆寫的方法;但對普通方法show,即使在子類中重新聲明了,無論加沒加new關鍵字(重寫 ),均是調用父類的方法;該實例不能調用子類中單獨新增的方法。

 1  {
 2                 Console.WriteLine("------------------------------ 一.抽象類的使用和特點 ---------------------------------------");
 3                 //面向抽象編程
 4                 BasePhone iphone = new iphone();   //里氏替換原則
 5                 //1. iphone類中四個覆寫的抽象方法
 6                 iphone.Brand();
 7                 iphone.System();
 8                 iphone.Call();
 9                 iphone.Do<iphone>();
10                 //2. BasePhone中的兩個方法
11                 iphone.show();     //調用的是父類中的方法
12                 iphone.ShowVirtual();  //調用的是子類覆寫以后的方法
13                 //3. 調用子類特有的方法(調用不了)
14                 // iphone.ITunes();
15 }

結果:

  對比父類中的虛方法(virtual)和抽象方法(abstract)的區別:

    (1). 抽象方法沒有方法體,其繼承子類必須對其進行覆寫(override).

            (2). 虛方法有方法體,其繼承子類可以對其進行覆寫(override),可以不進行覆寫。若進行覆寫,調用的則是子類中覆寫后的方法;若不進行覆寫,則調用的是父類     中的方法。

    (3). 抽象方法的關鍵字是abstract,且必須存在於抽象類中;虛方法的關鍵字是virtual,可以存在於任何類中。

三. 接口

  接口是包含一組虛方法的抽象類型,其中每一種方法都有其名稱、參數、返回值。接口方法不能包含任何實現,CLR允許接口可以包含事件、屬性、索引器、靜態方法、靜態字段、靜態構造函數以及常數。但是接口不能包含任何靜態成員。一個類可以實現多個接口,當一個類實現某個接口時,它不僅要實現該接口定義的所有方法,還要實現該接口從其他接口中繼承的所有方法。

   接口的特點:

    (1). 接口不是類,里面可以包含屬性、方法、事件,但不能包括字段和靜態成員。

    (2). 接口只能包含沒有實現的方法。

    (3). 子類實現接口,必須要實現該接口定義的所有方法,還要實現該接口從其他接口中繼承的所有方法。

    (4). 接口不能被直接實例化,聲明的對象只能使用接口中的方法,不能使用子類新增的方法。

    (5). 同一個類可以實現多個接口。

1. 下面新建兩個接口IExtend和IPay,分別包含沒有實現的方法Game和Play。

1   public interface IExtend
2     {
3         void Game();
4     }
5    public interface IPay
6     {
7         void Play();
8     }

2. 下面新建一個iphone子類,實現接口IExtend和IPay,則必須實現這兩個接口中的所有方法。

 1   public class iphone :  IExtend, IPay
 2     {
 3         /// <summary>
 4         /// 下面兩個方法為顯式的實現接口中的方法
 5         /// </summary>
 6         public void Play()
 7         {
 8             Console.WriteLine("這是子類顯式實現了接口中的Play方法");
 9         }
10 
11         public void Game()
12         {
13             Console.WriteLine("這是子類顯式實現了接口中的Game方法");
14         }
15     }

3. 下面分別用面向接口編程的方式和正常方式進行調用

 1 {
 2                 Console.WriteLine("------------------------------ 二.接口的使用和特點 ---------------------------------------");
 3                 //1.面向接口編程
 4                 Console.WriteLine("------------------------------ 1.面向接口編程 ---------------------------------------");
 5                 IExtend iphone = new iphone();   
 6                 iphone.Game();
 7                 IPay iphone2 = new iphone();
 8                 iphone2.Play();
 9                 //2.正常編程
10                 Console.WriteLine("------------------------------ 2.正常編程 ---------------------------------------");
11                 iphone iphone3 = new iphone();
12                 iphone3.Play();
13                 iphone3.Game();
14  }

運行結果:

  

四. 抽象類和接口的比較(搜集於網絡)

  (一).相同點和不同點

    相同點

      1. 都不能被直接實例化,都可以通過繼承實現其抽象方法。

      2. 都是面向抽象編程的技術基礎,實現了諸多的設計模式。

    不同點

      1. 接口支持多繼承;抽象類不能實現多繼承。

      2. 接口只能定義抽象規則;抽象類既可以定義規則,還可能提供已實現的成員。

      3. 接口是一組行為規范;抽象類是一個不完全的類,着重族的概念。

      4. 接口可以用於支持回調;抽象類不能實現回調,因為繼承不支持。

      5. 接口只包含方法、屬性、索引器、事件的簽名,但不能定義字段和包含實現的方法;抽象類可以定義字段、屬性、包含有實現的方法。

      6. 接口可以作用於值類型和引用類型;抽象類只能作用於引用類型。例如,Struct就可以繼承接口,而不能繼承類。

   (二).規則與場合

      1. 請記住,面向對象思想的一個最重要的原則就是:面向接口編程。

      2. 借助接口和抽象類,23個設計模式中的很多思想被巧妙的實現了,我認為其精髓簡單說來就是:面向抽象編程。

      3. 抽象類應主要用於關系密切的對象,而接口最適合為不相關的類提供通用功能。

      4. 接口着重於CAN-DO關系類型,而抽象類則偏重於IS-A式的關系;

      5. 接口多定義對象的行為;抽象類多定義對象的屬性;

      6. 接口定義可以使用public、protected、internal 和private修飾符,但是幾乎所有的接口都定義為public,原因就不必多說了。

      7. 在接口中,所有的方法都默認為public。

      8. “接口不變”,是應該考慮的重要因素。所以,在由接口增加擴展時,應該增加新的接口,而不能更改現有接口。

      9. 盡量將接口設計成功能單一的功能塊,以.NET Framework為例,IDisposable、IDisposable、IComparable、IEquatable、IEnumerable等都只包含一個公共方法。

      10. 接口名稱前面的大寫字母“I”是一個約定,正如字段名以下划線開頭一樣,請堅持這些原則。

      11. 如果預計會出現版本問題,可以創建“抽象類”。例如,創建了狗(Dog)、雞(Chicken)和鴨(Duck),那么應該考慮抽象出動物(Animal)

        來應對以后可能出現風馬牛的事情。

      而向接口中添加新成員則會強制要求修改所有派生類,並重新編譯,所以版本式的問題最好以抽象類來實現。

      12. 從抽象類派生的非抽象類必須包括繼承的所有抽象方法和抽象訪問器的實實現。

      13. 對抽象類不能使用new關鍵字,也不能被密封,原因是抽象類不能被實例化。

      14. 在抽象方法聲明中不能使用 static 或 virtual 修飾符。

    (三). MSDN這樣建議:

       1. 如果預計要創建組件的多個版本,則創建抽象類。抽象類提供簡單易行的方法來控制組件版本。通過更新基類,所有繼承類都隨更改自動更新。

        另一方面,接口一旦創建就不能更改。如果需要接口的新版本,必須創建一個全新的接口。

       2. 如果創建的功能將在大范圍的全異對象間使用,則使用接口。抽象類應主要用於關系密切的對象,而接口最適合為不相關的類提供通用功能。

       3. 如果要設計小而簡練的功能塊,則使用接口。如果要設計大的功能單元,則使用抽象類。

       4. 如果要在組件的所有實現間提供通用的已實現功能,則使用抽象類。抽象類允許部分實現類,而接口不包含任何成員的實現。

    (四)、經典示例

      .NET Framework中關於接口和抽象類的使用:

      集合類使用了基於接口的設計,請關注System.Collections中關於接口的設計實現;

      數據流相關類使用了基於抽象類的設計,請關注System.IO.Stream類的抽象類設計機制。

 

 

  

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 本人才疏學淺,用郭德綱的話說“我是一個小學生”,如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM