Download presentation
Presentation is loading. Please wait.
1
繼承與多型 (Inheritance and Polymorphism)
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
2
綱要 繼承 修飾語protected 限制繼承 繼承架構下的建構函式呼叫 OCP:開放-封閉原理 多型 覆寫與隱藏 二十一點模擬程式0.1版
3
綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理
多重介面 *多重介面鑄形
4
綱要 繼承 修飾語protected 限制繼承 繼承架構下的建構函式呼叫 OCP:開放-封閉原理 多型 覆寫與隱藏 二十一點模擬程式0.1版
5
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; public int Multiply(int a, int b) int result = a * b;
6
UsingInheritance. Program.Main 片段(1/2)
switch (op) { case ‘+’: result = AdvancedCalculator.Add(operand1,operand2); Console.WriteLine("{0} + {1} = {2} ", operand1, operand2, result); break; . . .
7
UsingInheritance. Program.Main 片段(2/2)
case ‘^’: result = AdvancedCalculator.Power(operand1,operand2); Console.WriteLine( " {0} ^ {1} = {2}", operand1, operand2, result); break; . . . }
8
UsingInheritance.AdvancedCalculator 片段
class AdvancedCalculator : Calculator { public static int Power(int a, int b) int result = (int)Math.Pow(a, b); return result; }
9
表示繼承的UML類別圖
10
類別繼承之階層關係 A B C class A { private int data1; private int data2;
//…other members are methods } class B : A { private int data3; class C : B { private int data4; A B C
11
物件記憶體分配模型 data1 a data2 c data1 b data1 data2 data2 data3 data3 data1
A a = new A(); B b = new B(); C c = new C(); data1 a data2 c data1 b data1 data2 data2 data3 data3 data1 data4
12
DataMemberInheritance.A
class A { private int data1; private int data2; public A() { data1 = 1; data2 = 2; } public void GetAData(out int data1, out int data2) { data1 = this.data1; data2 = this.data2;
13
DataMemberInheritance.B
class B : A { private int data3; public B() { data3 = 3; } public void GetBData(out int data3) { data3 = this.data3;
14
DataMemberInheritance.C
class C : B { private int data1; private int data4; public C() { data1 = 5; data4 = 4; } public void GetCData(out int data1, out int data4) { data1 = this.data1; data4 = this.data4;
15
DataMemberInheritance.Program. Main 片段 (1/2)
A a = new A(); B b = new B(); C c = new C(); a.GetAData(out data1, out data2); Debug.Assert(data1 == 1 && data2 == 2); b.GetAData(out data1, out data2); b.GetBData(out data3); Debug.Assert(data3 == 3); c.GetAData(out data1, out data2);
16
DataMemberInheritance.Program. Main 片段 (2/2)
c.GetBData(out data3); Debug.Assert(data3 == 3); c.GetCData(out data1, out data4); Debug.Assert(data1 == 5 && data4 == 4);
17
MemberFunctionInheritance.A
class A { private int data1; private int data2; public A() { data1 = 1; data2 = 2; } public int GetData1() { return data1; public int GetData2() { return data2;
18
MemberFunctionInheritance.B
class B : A { private int data3; public B() { data3 = 3; } public int Data3 { get { return data3; } public int GetSum() { return (GetData2() + data3);
19
MemberFunctionInheritance.C (1/2)
class C : B { private int data1; private int data4; public C() { data1 = 5; data4 = 4; } public new int GetData1() { return data1;
20
MemberFunctionInheritance.C (2/2)
public int GetData4() { return data4; } public int GetAData1() { return base.GetData1();
21
MemberFunctionInheritance. Program.Main片段 (1/2)
A a = new A(); B b = new B(); C c = new C(); data1 = a.GetData1(); data2 = a.GetData2(); Debug.Assert(data1 == 1 && data2 == 2); data1 = b.GetData1(); data2 = b.GetData2(); data3 = b.Data3; Debug.Assert(data1 == 1 && data2 == 2 && data3 == 3);
22
MemberFunctionInheritance. Program.Main片段 (2/2)
int sum = b.GetSum(); Debug.Assert(sum == 5); data1 = c.GetData1(); data2 = c.GetData2(); data3 = c.Data3; data4 = c.GetData4(); int aAData1 = c.GetAData1(); Debug.Assert(data1 == 5 && data2 == 2 && data3 == 3 && data4 == 4 && aAData1 == 1);
23
練習 實作並測試下列繼承關係
24
綱要 繼承 修飾語protected 限制繼承 繼承架構下的建構函式呼叫 OCP:開放-封閉原理 多型 覆寫與隱藏 二十一點模擬程式0.1版
25
CalculatorInheritance類別圖
26
CalculatorInheritance.Program.Main片段
Calculator c = new Calculator(); c.Run(); AdvancedCalculator ac = new AdvancedCalculator(); ac.Run();
27
CalculatorInheritance.Calculator 片段
protected int register1; protected int register2; protected int display; protected char op; public Calculator() { register1 = 0; register2 = 0; display = 0; op = '+'; }
28
CalculatorInheritance.Calculator. Run片段 (1/2)
Console.WriteLine("Calculator"); while(true) { Console.Write("Turning off? (Y/N): "); answer = char.Parse(Console.ReadLine()); if( answer == 'Y' || answer == 'y' ) break; Console.Write("Enter operand 1: "); register1 = int.Parse(Console.ReadLine()); Console.Write( "Enter operator +, -, *, / "); op = char.Parse(Console.ReadLine()); Console.Write("Enter operand 2: "); register2 = int.Parse(Console.ReadLine());
29
CalculatorInheritance.Calculator. Run片段 (2/2)
switch (op) { case '+': Add(); break; case '-': Subtract(); default: Console.WriteLine( "Should not see this message. Debug!!!"); } Console.WriteLine(display);
30
CalculatorInheritance.Calculator 片段
protected void Add() { display = register1 + register2; } protected void Subtract() { display = register1 - register2; protected void Multiply() { display = register1 * register2; protected void Divide() { display = register1 / register2;
31
CalculatorInheritance. AdvancedCalculator (1/3)
class AdvancedCalculator : Calculator { public new void Run() { Console.WriteLine("Advanced Calculator"); while(true) { Console.Write("Turning off? (Y/N): "); answer = char.Parse(Console.ReadLine()); if( answer == 'Y' || answer == 'y' ) break; Console.Write("Enter operand 1: "); register1=int.Parse(Console.ReadLine()); Console.Write( "Enter operator +, -, *, /, ^ "); op = char.Parse(Console.ReadLine());
32
CalculatorInheritance. AdvancedCalculator (2/3)
Console.Write("Enter operand 2:"); register2=int.Parse(Console.ReadLine()); switch (op) { case '^': Power(); break; default: } Console.WriteLine(display);
33
CalculatorInheritance. AdvancedCalculator (3/3)
protected void Power() { display = (int) Math.Pow(register1, register2); }
34
綱要 繼承 修飾語protected 限制繼承 繼承架構下的建構函式呼叫 OCP:開放-封閉原理 多型 覆寫與隱藏 二十一點模擬程式0.1版
35
SealedClassExample.Parent
sealed class Parent { private int data1; public Parent() { data1 = 0; } public int Data1 { get { return data1; }
36
SealedClassExample.Child
class Child : Parent // Error! { private int data2; public Child() { data2 = 0; } public int Data2 { get { return data2; }
37
綱要 繼承 修飾語protected 限制繼承 繼承架構下的建構函式呼叫 OCP:開放-封閉原理 多型 覆寫與隱藏 二十一點模擬程式0.1版
38
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();
39
UsingConstructorsForInheritance.Animal
class Animal { private string species; public Animal() Console.WriteLine("Animal()"); species = "Animal"; } public Animal( string s ) Console.WriteLine("Animal("+ s +")"); species = s;
40
UsingConstructorsForInheritanc.Primate
class Primate : Animal { private int heartCham; public Primate() : base() Console.WriteLine( "Primate()" ); } public Primate( int n ) : base( "Primate" ) Console.WriteLine("Primate(" + n +")"); heartCham = n;
41
UsingConstructorsForInheritanc.Human
class Human : Primate { public Human() : base( 4 ) Console.WriteLine( "Human()" ); }
42
衍生物件產生流程 Primate human = new Primate( 4 );
public Primate( int n ) : base( "Primate" ) { . . . } public Animal( string s ) { . . . }
43
練習 利用偵錯器體驗了解程式UsingConstructorsForInheritance
44
綱要 繼承 修飾語protected 限制繼承 繼承架構下的建構函式呼叫 OCP:開放-封閉原理 多型 覆寫與隱藏 二十一點模擬程式0.1版
45
開放-封閉原理 (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
46
程式OCPViolationExample類別圖
47
OCPViolationExample. 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);
48
OCPViolationExample.Program.DrawAllShapes
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); }
49
OCPViolationExample.Program.DrawCircle及DrawRectangle
static void DrawCircle(Circle c) { Console.WriteLine("Draw a circle"); } static void DrawRectangle(Rectangle r) Console.WriteLine("Draw a rectangle");
50
OCPViolationExample 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;
51
OCPViolationExample.Rectangle
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; }
52
OCPViolationExample的主要問題
添加任何有關Shape的演算(例如,拖曳、伸縮、移動、刪除)都要重複麻煩的switch敘述 增加一種新的Shape子類別,必須修改enum敘述、各處的switch敘述,並在類別Program增加對應的Draw函式
53
綱要 繼承 修飾語protected 限制繼承 繼承架構下的建構函式呼叫 OCP:開放-封閉原理 多型 覆寫與隱藏 二十一點模擬程式0.1版
54
物件導向的關鍵技術 封裝(packaging) 繼承(inheritance) 多型(polymorphism)
55
多型 編譯時連結(compile-time binding)與執行時連結(run-time binding) 多型之要求 多型之優缺點
靜態連結(static binding)與動態連結(dynamic binding) 多型之要求 繼承階層體系 虛擬(virtual)與覆寫(override) 基礎類別之參考 多型之優缺點
56
DrawingAllShapes類別圖
57
DrawingAllShapes. 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());
58
DrawingAllShapes. Program.Main片段(2/2)
switch (ans) { case 1: list[0] = c; list[1] = r; break; case 2: list[0] = r; list[1] = c; default: . . . } DrawAllShapes(list);
59
DrawingAllShapes.Program. DrawAllShapes
static void DrawAllShapes(Shape[] list) { int i; for (i = 0; i < list.Length; ++i) list[i].Draw(); }
60
DrawingAllShapes.Shape
class Shape { public Shape() { } virtual public void Draw() { } }
61
DrawingAllShapes.Circle
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");
62
DrawingAllShapes.Rectangle
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");
63
DrawingAllShapes記憶配置
堆積記憶區 堆疊記憶區 c this Circle.Draw c.radius c.center.x r c.center.y this Rectangle.Draw r.width r.height r.topLeft.x r.topLeft.y
64
練習 在程式DrawingAllShapes增加類別Triangle,使程式也能畫出三角形。
65
綱要 繼承 修飾語protected 限制繼承 繼承架構下的建構函式呼叫 OCP:開放-封閉原理 多型 覆寫與隱藏 二十一點模擬程式0.1版
66
NewVsOverride.Program類別圖
67
NewVsOverride.Car // Define the base class class Car {
public virtual void DescribeCar() System.Console.WriteLine( "Four wheels and an engine."); }
68
NewVsOverride.ConvertibleCar
// Define the derived classes class ConvertibleCar : Car { public new virtual void DescribeCar() base.DescribeCar(); Console.WriteLine( "A roof that opens up."); }
69
NewVsOverride.Minivan
class Minivan : Car { public override void DescribeCar() base.DescribeCar(); Console.WriteLine( "Carries seven people."); }
70
NewVsOverride.Program.Main 片段 (1/2)
// new and override make no differences here Car car1 = new Car(); car1.DescribeCar(); Console.WriteLine(" "); ConvertibleCar car2 = new ConvertibleCar(); car2.DescribeCar(); Minivan car3 = new Minivan(); car3.DescribeCar();
71
NewVsOverride.Program.Main 片段 (2/2)
// they are different in polymorphysm Car[] cars = new Car[3]; cars[0] = new Car(); cars[1] = new ConvertibleCar(); cars[2] = new Minivan(); foreach (Car vehicle in cars) { Console.WriteLine("Car object: " + vehicle.GetType()); vehicle.DescribeCar(); Console.WriteLine(" "); }
72
覆寫與隱藏 覆寫: override 隱藏: new 主要用於Polymorphism (多型) 執行時連結
只是取代基底類別同名之成員變數與方法 仍是編譯時連結
73
練習 利用偵錯器體驗了解程式NewVsOverride
74
綱要 繼承 修飾語protected 限制繼承 繼承架構下的建構函式呼叫 OCP:開放-封閉原理 多型 覆寫與隱藏 二十一點模擬程式0.1版
75
BlackJack_0_1 類別圖
76
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();
77
BlackJack_0_1.BlackJackTest 片段(2/2)
player.SaveACard(deck.DealACard()); dealer.SaveACard(deck.DealACard()); return( player.GetStatus() == Status.BLACK_JACK && dealer.GetStatus() == Status.PASS); }
78
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(); }
79
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(); }
80
BlackJack_0_1.Game 片段 (3/6)
// 第二輪發牌 for (i=0; i < N_PLAYERS; ++i) { players[i].SaveACard( deck.DealACard()); players[i].Dump(); }
81
BlackJack_0_1.Game 片段 (4/6)
// 開始要牌計牌 for(i=0; i<N_PLAYERS; ++i) { while (players[i].GetStatus() == Status.PASS && players[i].WantOneMoreCard() && deck.HasMoreCard()) players[i].SaveACard(deck.DealACard()); players[i].Dump(); if(IsBlackJackOrBurst(players[i])) return; }
82
BlackJack_0_1.Game 片段 (5/6)
// 計點分勝負 Player dealer = players[N_PLAYERS-1]; for(i=0; i<N_PLAYERS-1; ++i) { if (dealer.GetTotalPoints() >= players[i].GetTotalPoints()) { Console.WriteLine( dealer.Name + "勝"+players[i].Name); } else { Console.WriteLine( players[i].Name+"勝"+dealer.Name); }
83
BlackJack_0_1.Game 片段 (6/6)
private bool IsBlackJackOrBurst( Player player) { bool isBlackJack = false; if (player.GetStatus()==Status.BLACK_JACK) { isBlackJack = true; Console.WriteLine(player.Name+ " BlackJack!!!"); } bool isBurst = false; if (player.GetStatus() == Status.BURST){ isBurst = true; Console.WriteLine(player.Name+" 爆!!!"); return (isBlackJack || isBurst);
84
BlackJack_0_1.Player 片段(1/5)
private Card[] hand = new Card[11]; private int nCards; private Status status; private int totalPoints; private string name; public Player() { nCards = 0; name = "無名氏"; }
85
BlackJack_0_1.Player 片段(2/5)
public Player(string name) { nCards = 0; this.name = name; } public string Name get { return name; }
86
BlackJack_0_1.Player 片段(3/5)
virtual public bool WantOneMoreCard() { Console.Write("要再一張牌嗎? (y/n) "); string answer = Console.ReadLine(); return (answer == "Y" || answer == "y"); }
87
BlackJack_0_1.Player 片段(4/5)
public void Dump() { int i; Console.Write(name+" 牌: "); for (i = 0; i < nCards; ++i) hand[i].Dump(); Console.Write("\t"); if ((i + 1) % 5 == 0) Console.WriteLine(); }
88
BlackJack_0_1.Player 片段(5/5)
Console.WriteLine(); Console.WriteLine(name + " 總點數: " + totalPoints); }
89
BlackJack_0_1.Dealer片段 class Dealer : Player {
public Dealer() : base("莊家") {} override public bool WantOneMoreCard() { return (base.GetTotalPoints() < 17); }
90
綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理
多重介面 *多重介面鑄形
91
預設類別System.Object 一致化型別 成員函式 Equals GetHashCode GetType
ReferenceEquals ToString Finalize
92
InheritingObject.Program.Main片段
Test t1 = new Test(); Test t2 = new Test(); bool isEqual = t1.Equals(t2); Console.WriteLine(t1.ToString()); Console.WriteLine("t1 與t2 相等為" + isEqual);
93
InheritingObject.Test class Test { override public string ToString()
return "覆寫InheritingObject.Test"; }
94
Boxing 與 Unboxing int x = 10; Object obj = (Object) x; // boxing
int j = (int)obj; // unboxing
95
綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理
多重介面 *多重介面鑄形
96
Liskov 代替性原理 (LSP: Liskov Substitution Principle)
Subtype must be substitutable for their base types 良好的繼承設計 對形態S之每一物件o1,有形態T的物件o2,使在所有利用型態T物件的程式P中,P的行為不因以o1代替o2而改變 破壞LSP常也破壞OCP *Robert C. Martin, Agile Software Development: Principles, Patterns, and Practices, Pearson Education, 2003
97
IS-A 關係與繼承
98
LSPViolationExample.Rectangle (1/2)
class Rectangle { private int width; private int height; virtual public int Width set { width = value; } } virtual public int Height set { height = value; }
99
LSPViolationExample.Rectangle (2/2)
public int Area() { return width * height; }
100
LSPViolationExample.Square
class Square : Rectangle { override public int Width set { base.Width = value; base.Height = value; } } override public int Height
101
LSPViolationExample.Program
class Program { static void Main(string[] args) Square s = new Square(); Test(s); } static void Test(Rectangle r) r.Width = 5; r.Height = 4; Debug.Assert(r.Area() == 20);
102
函式的進入與離開條件 進入與離開函式時的假設 Rectangle.Width的離開條件 符合LSP之子類別函式覆寫的要求
Debug.Assert( (width == value) && (height == old.height)); 符合LSP之子類別函式覆寫的要求 進入條件需等於父類別被覆寫函式之進入條件,或更寬鬆 離開條件需等於父類別被覆寫函式之離開條件,或更嚴格
103
綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理
多重介面 *多重介面鑄形
104
抽象類別
105
AbstractClassExample. Program.Main() 片段
double a = 5.0; Square sq = new Square(a); Console.WriteLine("正方形sq之面積為" + sq.Area()); Circle c = new Circle(a); Console.WriteLine("圓形c之面積為" + c.Area());
106
AbstractClassExample.Shape
public abstract class Shape { private string shape; public Shape(string shape) { this.shape = shape; Console.WriteLine("建立" + shape); } abstract public double Area();
107
AbstractClassExample.Square
public class Square : Shape { double a; public Square(double a): base("正方形") this.a = a; } public override double Area() return a * a;
108
AbstractClassExample.Circle
public class Circle : Shape { double r; public Circle(double r): base("圓形") this.r = r; } public override double Area() return Math.PI * r * r;
109
練習 將程式DrawingAllShapes中的類別Shape改為抽象類別,並將Shape.Draw()改為抽象函式
110
抽象類別 修飾語 欄位變數 建構式 函式方法覆寫與實作
111
綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理
多重介面 *多重介面鑄形
112
依存性反轉原理 (DIP: Dependency-Inversion Principle)
High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions. *Robert C. Martin, Agile Software Development: Principles, Patterns, and Practices, Pearson Education, 2003
113
一個違反DIP的例子
114
狀態圖(State Chart)
115
DIPViolationExample. LampStatus及ButtonStatus
public enum LampStatus { OFF = 0, ON = 1 } public enum ButtonStatus RELEASED = 0, PRESSED = 1
116
DIPViolationExample.Lamp public class Lamp {
private LampStatus status = LampStatus.OFF; public LampStatus Status { get { return status; } } public void TurnOn() { status = LampStatus.ON; public void TurnOff() { status = LampStatus.OFF;
117
DIPViolationExample.Button (1/2)
public class Button { private ButtonStatus status = ButtonStatus.RELEASED; private Lamp lamp; public Button(Lamp lamp) this.lamp = lamp; } public ButtonStatus Status { get { return status; }
118
DIPViolationExample.Button (2/2)
public void Press() { if (status == ButtonStatus.RELEASED) { status = ButtonStatus.PRESSED; lamp.TurnOn(); } public void Release() { if( status == ButtonStatus.PRESSED ){ status = ButtonStatus.RELEASED; lamp.TurnOff();
119
DIPViolationExample. Program.Main() 片段 (1/3)
Lamp lamp1 = new Lamp(1); Button button = new Button(lamp1); Random rand = new Random(); for (int n = 0; n <= 100; ++n) { Console.Write("time n = " + n + "\t");
120
DIPViolationExample. Program.Main() 片段 (2/3)
if (rand.Next() % 2 == 1) { if (button.Status == ButtonStatus.PRESSED) button.Release(); } else button.Press();
121
DIPViolationExample. Program.Main() 片段 (3/3)
if (lamp1.Status == LampStatus.OFF) { Console.WriteLine("lamp1 is off"); } else Console.WriteLine("lamp1 is on"); Console.WriteLine();
122
綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理
多重介面 *多重介面鑄形
123
介面
124
UsingInterface. Program.Main片段
double a = 5.0; Square sq = new Square(a); Console.WriteLine("正方形sq之面積為" + sq.Area()); Circle c = new Circle(a); Console.WriteLine("圓形c之面積為" + c.Area());
125
UsingInterface.Shape interface Shape { double Area(); }
126
UsingInterface.Square
public class Square : Shape { double a; public Square(double a) this.a = a; } public double Area() return a * a;
127
UsingInterface.Circle
public class Circle : Shape { double r; public Circle(double r) this.r = r; } public double Area() return Math.PI * r * r;
128
介面 vs. 抽象類別 (1/2) interface Shape { double Area(); }
public abstract class Shape { private string shape; public Shape(string shape) { this.shape = shape; Console.WriteLine("建立" + shape); abstract public double Area();
129
介面 vs. 抽象類別 (2/2) public class Square : Shape { . . .
public double Area() { return a * a; } public override double Area() {
130
一個符合DIP的設計
131
ButtonAndLamp public interface SwitchableDevice { void TurnOn();
void TurnOff(); } public enum LampStatus { OFF = 0, ON = 1 public enum ButtonStatus { RELEASED = 0, PRESSED = 1
132
ButtonAndLamp.Lamp public class Lamp : SwitchableDevice {
private LampStatus status = LampStatus.OFF; public LampStatus Status { get { return status; } } public void TurnOn() { status = LampStatus.ON; public void TurnOff() { status = LampStatus.OFF;
133
ButtonAndLamp.Button (1/2)
public class Button { private ButtonStatus status = ButtonStatus.RELEASED; private SwitchableDevice device; public Button(SwitchableDevice device) this.device = device; } public ButtonStatus Status { get { return status; }
134
ButtonAndLamp.Button (2/2)
public void Press() { if (status == ButtonStatus.RELEASED) { status = ButtonStatus.PRESSED; device.TurnOn(); } public void Release() { if (status == ButtonStatus.PRESSED) { status = ButtonStatus.RELEASED; device.TurnOff();
135
練習 在程式ButtonAndLamp增加類別Fan,使Button也可以控制Fan的開與關
136
綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理
多重介面 *多重介面鑄形
137
介面分離原理 (ISP: Interface-Segregation Principle)
Clients should not be forced to depend on methods that they do not use 避免同一介面中有目的不同的多群函式宣告
138
一個違反ISP的例子
139
ISPViolationExample interface TimerClient { void TimeOut(); }
interface Door : TimerClient { void Lock(); void Unlock(); bool IsOpen(); enum DoorStatus { CLOSED = 0, OPEN = 1
140
ISPViolationExample.Timer (1/2)
class Timer { private int t; private int timeout; private TimerClient client; public Timer(int timeout, TimerClient client) this.timeout = timeout; this.client = client; t = 0; }
141
ISPViolationExample.Timer (2/2)
public void Advance() { ++t; if (t % timeout == 0) { client.TimeOut(); }
142
ISPViolationExample.TimedDoor (1/2)
class TimedDoor : Door { private DoorStatus status = DoorStatus.CLOSED; public bool IsOpen() { return (status == DoorStatus.OPEN); } public void Lock() { if (IsOpen()) status = DoorStatus.CLOSED; public void Unlock() { if (!IsOpen()) status = DoorStatus.OPEN;
143
ISPViolationExample.TimedDoor (2/2)
public void TimeOut() { Lock(); }
144
ISPViolationExample. Program.Main 片段(1/2)
TimedDoor tDoor = new TimedDoor(); int timeout = 10; Timer timer = new Timer(timeout, tDoor); int n; const int N = 20; tDoor.Unlock(); for (n = 0; n <= N; ++n) {
145
ISPViolationExample. Program.Main片段(2/2)
if (tDoor.IsOpen()) { Console.WriteLine( "n = " + n + "\t tDoor is open"); } else "n = " + n + "\t tDoor is closed"); timer.Advance();
146
綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理
多重介面 *多重介面鑄形
147
程式MultiInterface類別圖
148
MultiInterface.Program.Main 片段
Floatplane fp = new Floatplane(); fp.Sail(); fp.Fly();
149
MultiInterface interface Plane { void Fly(); } interface Ship
void Sail();
150
MultiInterface.Floatplane
public class Floatplane : Plane, Ship { public Floatplane() { Console.WriteLine("建立水上飛機"); } public void Sail() { Console.WriteLine("水上滑行"); public void Fly() { Console.WriteLine("空中飛行");
151
練習 利用多重介面, 設計並測試一個類別Clock_Radio,兼具介面Clock之GetTime()與介面Radio之PlayMusic()功能
152
一個符合ISP的設計
153
TimedDoorSimulation片段
interface TimerClient { void TimeOut(); } interface Door { void Lock(); void Unlock(); bool IsOpen(); . . . class TimedDoor : Door, TimerClient {
154
綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理
多重介面 *多重介面鑄形
155
程式CastMultiInterfaces類別圖
156
CastMultiInterfaces. Program.Main片段
double a = 5.0; Square sq = new Square(a); Rhombus rhomb = sq as Rhombus; Console.WriteLine( "sq的面積以菱形公式計算得"+rhomb.Area() ); if( sq is Rectangle ) { Rectangle rec = (Rectangle) sq; "sq的面積以矩形公式計算得"+rec.Area() ); }
157
CastMultiInterfaces interface Rectangle { double Area(); }
interface Rhombus
158
CastMultiInterfaces.Square (1/2)
public class Square : Rectangle, Rhombus { private double a; private double d; public Square(double a) this.a = a; d = Math.Sqrt(2.0) * a; }
159
CastMultiInterfaces.Square (2/2)
double Rectangle.Area() { return a * a; } double Rhombus.Area() return 0.5 * d * d;
Similar presentations