1.38k likes | 1.53k Views
繼承與多型 (Inheritance and Polymorphism). 鄭士康 國立台灣大學 電機工程學系 / 電信工程研究所 / 資訊網路與多媒體研究所. 綱要. 繼承 修飾語 protected 限制繼承 繼承架構下的建構函式呼叫 OCP: 開放 - 封閉原理 多型 二十一點模擬程式 0.1 版 *覆寫與隱藏. 綱要. 預設類別 System.Object LSP: Liskov 替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理 多重介面 *多重介面鑄形. 綱要. 繼承 修飾語 protected
E N D
繼承與多型(Inheritance and Polymorphism) 鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
綱要 • 繼承 • 修飾語protected • 限制繼承 • 繼承架構下的建構函式呼叫 • OCP:開放-封閉原理 • 多型 • 二十一點模擬程式0.1版 • *覆寫與隱藏
綱要 • 預設類別System.Object • LSP: Liskov替代性原理 • 抽象類別 • DIP: 依存性反轉原理 • 介面 • ISP: 介面分離原理 • 多重介面 • *多重介面鑄形
綱要 • 繼承 • 修飾語protected • 限制繼承 • 繼承架構下的建構函式呼叫 • OCP:開放-封閉原理 • 多型 • 二十一點模擬程式0.1版 • 覆寫與隱藏
UsingInheritance.Calculator 片段 public int Add(int a, int b) { int result = a + b; return result; } public int Subtract(int a, int b) { int result = a - b; return result; } public int Multiply(int a, int b) { int result = a * b; return result; }
UsingInheritance.Program.Program.Main() 片段(1/2) AdvancedCalculator calculator = new AdvancedCalculator(); . . . switch (op) { case 1: result =calculator.Add(operand1,operand2); Console.WriteLine("{0} + {1} = {2} ", operand1, operand2, result); break; . . .
UsingInheritance.Program.Program.Main() 片段(2/2) case 5: functionValue = calculator.GetSine(angle); Console.WriteLine( "Sine of {0} (deg) = {1}", angle, functionValue); break; . . . }
UsingInheritance.AdvancedCalculator 片段 public class AdvancedCalculator : Calculator { public double GetSine(double angle) { angle *= Math.PI / 180.0; return Math.Sin(angle); } . . . }
A B C 類別繼承之階層關係 class A { private int data1; private int data2; //…other members are methods } class B : A { private int data3; //…other members are methods } class C : B { private int data1; private int data4; //…other members are methods }
物件記憶體分配模型 A a = new A(); B b = new B(); C c = new C(); data1 a data2 c data1 data1 b data2 data2 data3 data3 data1 data4
練習 • 實作並測試下列繼承關係
綱要 • 繼承 • 修飾語protected • 限制繼承 • 繼承架構下的建構函式呼叫 • OCP:開放-封閉原理 • 多型 • 二十一點模擬程式0.1版 • 覆寫與隱藏
UsingProtected.Program.Program.Main()片段 DC d = new DC(); d.SetX(3); //Console.WriteLine( d.GetX() ) ; // Error! //d.x = 77; // Error! d.Add2();
UsingProtected.Program片段 class BC { private int x; public void SetX( int x ) { this.x = x; } protected int GetX() { return x; } } class DC : BC { public void Add2() { int c = GetX(); SetX( c+2 ); } }
綱要 • 繼承 • 修飾語protected • 限制繼承 • 繼承架構下的建構函式呼叫 • OCP:開放-封閉原理 • 多型 • 二十一點模擬程式0.1版 • 覆寫與隱藏
限制繼承 sealed class SClass { . . . . . . }
綱要 • 繼承 • 修飾語protected • 限制繼承 • 繼承架構下的建構函式呼叫 • OCP:開放-封閉原理 • 多型 • 二十一點模擬程式0.1版 • 覆寫與隱藏
UsingConstructorsForInheritance.Program.Main() 片段 Animal slug = new Animal(); Animal tweety = new Animal( "canary" ); Primate godzilla = new Primate(); Primate human = new Primate( 4 ); Human jill = new Human();
UsingConstructorsForInheritance.Program 片段(1/3) class Animal { private string species; public Animal() { Console.WriteLine("Animal()"); species = "Animal"; } public Animal( string s ) { Console.WriteLine("Animal("+ s +")"); species = s; } }
UsingConstructorsForInheritance.Program片段(2/3) class Primate : Animal { private int heartCham; public Primate() : base() { Console.WriteLine( "Primate()" ); } public Primate( int n ) : base( "Primate" ) { Console.WriteLine("Primate(" + n +")"); heartCham = n; } }
UsingConstructorsForInheritance.Program 片段(3/3) class Human : Primate { public Human() : base( 4 ) { Console.WriteLine( "Human()" ); } }
衍生物件產生流程 Primate human = new Primate( 4 ); public Primate( int n ) : base( "Primate" ) { . . . } public Animal( string s ) { . . . }
練習 • 利用偵錯器體驗了解程式UsingConstructorsForInheritance
綱要 • 繼承 • 修飾語protected • 限制繼承 • 繼承架構下的建構函式呼叫 • OCP:開放-封閉原理 • 多型 • 二十一點模擬程式0.1版 • 覆寫與隱藏
開放-封閉原理(OCP:Open-Closed Principle) • Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification • 增添軟體單元新功能,但不影響此軟體單元的程式碼 *Robert C. Martin, Agile Software Development: Principles, Patterns, and Practices, Pearson Education, 2003
OCPViolationExample.Program.Program.Main() 片段 Point center; center.x = 15; center.y = 20; Point topLeft; topLeft.x = 30; topLeft.y = 40; Shape[] list = { new Circle(2, center), new Rectangle(3, 4, topLeft) }; DrawAllShapes(list);
OCPViolationExample.Program.Program 片段 (1/2) static void DrawAllShapes(Shape[] list){ for (int i = 0; i < list.Length; ++i){ Shape s = list[i]; switch (s.type){ case ShapeType.CIRCLE: DrawCircle((Circle) s); break; case ShapeType.RECTANGLE: DrawRectangle((Rectangle) s); break; } } }
OCPViolationExample.Program.Program 片段 (2/2) static void DrawCircle(Circle c) { Console.WriteLine("Draw a circle"); } static void DrawRectangle(Rectangle r) { Console.WriteLine("Draw a rectangle"); }
OCPViolationExample.Program 片段 (1/2) class Shape { public ShapeType type; public Shape(ShapeType t){ type = t; } } class Circle : Shape{ private int radius; private Point center; public Circle(int radius, Point center) :base(ShapeType.CIRCLE){ this.radius = radius; this.center = center; } }
OCPViolationExample.Program 片段 (2/2) class Rectangle : Shape { private int width; private int height; private Point topLeft; public Rectangle(int width, int height, Point topLeft): base(ShapeType.RECTANGLE) { this.width = width; this.height = height; this.topLeft = topLeft; } }
OCPViolationExample的主要問題 • 添加任何有關Shape的演算(例如,拖曳、伸縮、移動、刪除)都要重複麻煩的switch敘述 • 增加一種新的Shape子類別,必須修改enum敘述、各處的switch敘述,並在類別Program增加對應的Draw函式
綱要 • 繼承 • 修飾語protected • 限制繼承 • 繼承架構下的建構函式呼叫 • OCP:開放-封閉原理 • 多型 • 二十一點模擬程式0.1版 • 覆寫與隱藏
物件導向的關鍵技術 • 封裝(packaging) • 繼承(inheritance) • 多型(polymorphism)
多型 • 編譯時連結(compile-time binding)與執行時連結(run-time binding) • 靜態連結(static binding)與動態連結(dynamic binding) • 多型之要求 • 繼承階層體系 • 虛擬(virtual)與覆寫(override) • 基礎類別之參考 • 多型之優缺點
DrawingAllShapes.Program.Program.Main() 片段(1/2) Shape[] list = new Shape[2]; Point center; center.x = 15; center.y = 20; Point topLeft; topLeft.x = 30; topLeft.y = 40; Circle c = new Circle(2, center); Rectangle r = new Rectangle(3, 4,topLeft); Console.WriteLine("決定畫圖順序, 輸入"); Console.WriteLine("1: 圓形, 矩形"); Console.WriteLine("2: 矩形, 圓形"); int ans = int.Parse(Console.ReadLine());
DrawingAllShapes.Program.Program.Main() 片段(2/2) switch (ans) { case 1: list[0] = c; list[1] = r; break; case 2: list[0] = r; list[1] = c; break; default: . . . } DrawAllShapes(list);
DrawingAllShapes.Program.Program 片段 static void DrawAllShapes(Shape[] list) { int i; for (i = 0; i < list.Length; ++i) { list[i].Draw(); } }
DrawingAllShapes.Program 片段(1/3) class Shape { public Shape() { } virtual public void Draw() { } }
DrawingAllShapes.Program 片段(2/3) class Circle : Shape { private int radius; private Point center; public Circle(int radius, Point center) { this.radius = radius; this.center = center; } override public void Draw() { Console.WriteLine("Draw a circle"); } }
DrawingAllShapes.Program 片段(3/3) class Rectangle : Shape{ private int width; private int height; private Point topLeft; public Rectangle(int width, int height, Point topLeft){ this.width = width; this.height = height; this.topLeft = topLeft; } override public void Draw(){ Console.WriteLine("Draw a rectangle"); } }
練習 • 在程式DrawingAllShapes增加類別Triangle,使程式也能畫出三角形。
綱要 • 繼承 • 修飾語protected • 限制繼承 • 繼承架構下的建構函式呼叫 • OCP:開放-封閉原理 • 多型 • 二十一點模擬程式0.1版 • 覆寫與隱藏
BlackJack_0_1.BlackJackTest 片段(1/2) public static bool Scenario1_OK() { Card[] cards = { new Card(Suit.SPADE, 1), new Card(Suit.HEART, 11), new Card(Suit.DIAMOND, 10) }; Deck deck = new Deck(cards); Player player = new Player(); Dealer dealer = new Dealer();
BlackJack_0_1.BlackJackTest 片段(2/2) player.SaveACard(deck.DealACard()); dealer.SaveACard(deck.DealACard()); player.SaveACard(deck.DealACard()); return( player.GetStatus() == Status.BLACK_JACK &&dealer.GetStatus() == Status.PASS); }
BlackJack_0_1.Game 片段 (1/6) const int N_PLAYERS = 2; Deck deck; Player[] players = new Player[N_PLAYERS]; public Game() { players[0] = new Player("Jeng"); players[N_PLAYERS-1] = new Dealer(); }
BlackJack_0_1.Game 片段 (2/6) private void Play() { int i; // 第一輪發牌 for (i = 0; i < N_PLAYERS; ++i) { players[i].SaveACard( deck.DealACard()); players[i].Dump(); }