# 繼承與多型 (Inheritance and Polymorphism) - PowerPoint PPT Presentation

1 / 159

I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.

- - - - - - - - - - - - - - - - - - - - - - - - - - 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.Main 片段(1/2)

switch (op)

{

case ‘+’:

Console.WriteLine("{0} + {1} = {2} ",

operand1, operand2, result);

break;

. . .

### UsingInheritance.Program.Main 片段(2/2)

case ‘^’:

result =

Console.WriteLine(" {0} ^ {1} = {2}",

operand1, operand2, result);

break;

. . .

}

{

public static int Power(int a, int b)

{

int result = (int)Math.Pow(a, b);

return result;

}

}

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

### 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;

}

}

### DataMemberInheritance.B

class B : A{

private int data3;

public B(){

data3 = 3;

}

public void GetBData(out int data3){

data3 = this.data3;

}

}

### 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;

}

}

### DataMemberInheritance.Program.Main 片段 (1/2)

A a = new A();

B b = new B();

C c = new C();

Debug.Assert(data1 == 1 && data2 == 2);

Debug.Assert(data1 == 1 && data2 == 2);

b.GetBData(out data3);

Debug.Assert(data3 == 3);

Debug.Assert(data1 == 1 && data2 == 2);

### 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);

### 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;

}

}

### MemberFunctionInheritance.B

class B : A{

private int data3;

public B(){

data3 = 3;

}

public int Data3{

get { return data3; }

}

public int GetSum() {

return (GetData2() + data3);

}

}

### 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;

}

### MemberFunctionInheritance.C (2/2)

public int GetData4(){

return data4;

}

return base.GetData1();

}

}

### 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);

### 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();

Debug.Assert(data1 == 5 && data2 == 2 &&

data3 == 3 && data4 == 4 &&

• 實作並測試下列繼承關係

• 繼承

• 修飾語protected

• 限制繼承

• 繼承架構下的建構函式呼叫

• OCP:開放-封閉原理

• 多型

• 覆寫與隱藏

• 二十一點模擬程式0.1版

### CalculatorInheritance.Program.Main片段

Calculator c = new Calculator();

c.Run();

ac.Run();

### CalculatorInheritance.Calculator片段

protected int register1;

protected int register2;

protected int display;

protected char op;

public Calculator(){

register1 = 0;

register2 = 0;

display = 0;

op = '+';

}

### CalculatorInheritance.Calculator.Run片段 (1/2)

Console.WriteLine("Calculator");

while(true){

Console.Write("Turning off? (Y/N): ");

Console.Write("Enter operand 1: ");

Console.Write(

"Enter operator +, -, *, /");

Console.Write("Enter operand 2: ");

### CalculatorInheritance.Calculator.Run片段 (2/2)

switch (op){

case '+':

break;

case '-':

Subtract();

break;

. . . . . .

default:

Console.WriteLine(

"Should not see this message. Debug!!!");

break;

}

Console.WriteLine(display);

}

### CalculatorInheritance.Calculator片段

display = register1 + register2;

}

protected void Subtract(){

display = register1 - register2;

}

protected void Multiply(){

display = register1 * register2;

}

protected void Divide(){

display = register1 / register2;

}

public new void Run(){

while(true){

Console.Write("Turning off? (Y/N): ");

break;

Console.Write("Enter operand 1: ");

Console.Write(

"Enter operator +, -, *, /, ^ ");

Console.Write("Enter operand 2:");

switch (op){

. . . . . .

case '^':

Power();

break;

default:

. . . . . .

}

Console.WriteLine(display);

}

}

protected void Power(){

display = (int) Math.Pow(register1,

register2);

}

}

• 繼承

• 修飾語protected

• 限制繼承

• 繼承架構下的建構函式呼叫

• OCP:開放-封閉原理

• 多型

• 覆寫與隱藏

• 二十一點模擬程式0.1版

### SealedClassExample.Parent

sealed class Parent{

private int data1;

public Parent(){

data1 = 0;

}

public int Data1{

get { return data1; }

}

}

### SealedClassExample.Child

class Child : Parent // Error!

{

private int data2;

public Child(){

data2 = 0;

}

public int Data2{

get { return data2; }

}

}

• 繼承

• 修飾語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.Animal

class Animal

{

private string species;

public Animal()

{

Console.WriteLine("Animal()");

species = "Animal";

}

public Animal( string s )

{

Console.WriteLine("Animal("+ s +")");

species = s;

}

}

### 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;

}

}

### UsingConstructorsForInheritanc.Human

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.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.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);

break;

}

}

}

### OCPViolationExample.Program.DrawCircle及DrawRectangle

static void DrawCircle(Circle c)

{

Console.WriteLine("Draw a circle");

}

static void DrawRectangle(Rectangle r)

{

Console.WriteLine("Draw a rectangle");

}

### OCPViolationExample

class Shape {

public ShapeType type;

public Shape(ShapeType t){

type = t;

}

}

class Circle : Shape{

private Point center;

:base(ShapeType.CIRCLE){

this.center = center;

}

}

### 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;

}

}

### 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.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: 矩形, 圓形");

### DrawingAllShapes.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.DrawAllShapes

static void DrawAllShapes(Shape[] list)

{

int i;

for (i = 0; i < list.Length; ++i)

{

list[i].Draw();

}

}

### DrawingAllShapes.Shape

class Shape

{

public Shape() { }

virtual public void Draw() { }

}

### DrawingAllShapes.Circle

class Circle : Shape

{

private Point center;

{

this.center = center;

}

override public void Draw()

{

Console.WriteLine("Draw a circle");

}

}

### 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");

}

}

c

this

Circle.Draw

c.center.x

r

c.center.y

this

Rectangle.Draw

r.width

r.height

r.topLeft.x

r.topLeft.y

### 練習

• 在程式DrawingAllShapes增加類別Triangle，使程式也能畫出三角形。

• 繼承

• 修飾語protected

• 限制繼承

• 繼承架構下的建構函式呼叫

• OCP:開放-封閉原理

• 多型

• 覆寫與隱藏

• 二十一點模擬程式0.1版

### NewVsOverride.Car

// Define the base class

class Car

{

public virtual void DescribeCar()

{

System.Console.WriteLine(

"Four wheels and an engine.");

}

}

### NewVsOverride.ConvertibleCar

// Define the derived classes

class ConvertibleCar : Car

{

public new virtual void DescribeCar()

{

base.DescribeCar();

Console.WriteLine(

"A roof that opens up.");

}

}

### NewVsOverride.Minivan

class Minivan : Car

{

public override void DescribeCar()

{

base.DescribeCar();

Console.WriteLine(

"Carries seven people.");

}

}

### 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();

Console.WriteLine("----------");

Minivan car3 = new Minivan();

car3.DescribeCar();

Console.WriteLine("----------");

### 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("----------");

}

### 覆寫與隱藏

• 覆寫: override

• 主要用於Polymorphism (多型)

• 執行時連結

• 隱藏: new

• 只是取代基底類別同名之成員變數與方法

• 仍是編譯時連結

### 練習

• 利用偵錯器體驗了解程式NewVsOverride

• 繼承

• 修飾語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();

}

### BlackJack_0_1.Game 片段 (3/6)

// 第二輪發牌

for (i=0; i < N_PLAYERS; ++i)

{

players[i].SaveACard(

deck.DealACard());

players[i].Dump();

}

### 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;

}

}

### 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);

}

}

}

### 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);

}

### 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 = "無名氏";

}

### BlackJack_0_1.Player 片段(2/5)

public Player(string name)

{

nCards = 0;

this.name = name;

}

public string Name

{

get { return name; }

}

### BlackJack_0_1.Player 片段(3/5)

virtual public bool WantOneMoreCard()

{

Console.Write("要再一張牌嗎? (y/n) ");

}

### 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();

}

### BlackJack_0_1.Player 片段(5/5)

Console.WriteLine();

Console.WriteLine(name + " 總點數: " +

totalPoints);

}

### BlackJack_0_1.Dealer片段

class Dealer : Player{

public Dealer() : base("莊家"){}

override public bool WantOneMoreCard()

{

return (base.GetTotalPoints() < 17);

}

}

### 綱要

• 預設類別System.Object

• LSP: Liskov替代性原理

• 抽象類別

• DIP: 依存性反轉原理

• 介面

• ISP: 介面分離原理

• 多重介面

• *多重介面鑄形

### 預設類別System.Object

• 一致化型別

• 成員函式

• Equals

• GetHashCode

• GetType

• ReferenceEquals

• ToString

• Finalize

### 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);

### InheritingObject.Test

class Test

{

override public string ToString()

{

return "覆寫InheritingObject.Test";

}

}

### Boxing 與 Unboxing

int x = 10;

Object obj = (Object) x; // boxing

obj = 20;

int j = (int)obj; // unboxing

### 綱要

• 預設類別System.Object

• LSP: Liskov替代性原理

• 抽象類別

• DIP: 依存性反轉原理

• 介面

• ISP: 介面分離原理

• 多重介面

• *多重介面鑄形

### 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

### 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; }

}

### LSPViolationExample.Rectangle (2/2)

public int Area()

{

return width * height;

}

}

### LSPViolationExample.Square

class Square : Rectangle

{

override public int Width

{

set { base.Width = value;

base.Height = value; }

}

override public int Height

{

set { base.Width = value;

base.Height = value; }

}

}

### 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);

}

}

### 函式的進入與離開條件

• 進入與離開函式時的假設

• Rectangle.Width的離開條件

Debug.Assert( (width == value) &&

(height == old.height));

• 符合LSP之子類別函式覆寫的要求

• 進入條件需等於父類別被覆寫函式之進入條件，或更寬鬆

• 離開條件需等於父類別被覆寫函式之離開條件，或更嚴格

### 綱要

• 預設類別System.Object

• LSP: Liskov替代性原理

• 抽象類別

• DIP: 依存性反轉原理

• 介面

• ISP: 介面分離原理

• 多重介面

• *多重介面鑄形

### 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());

### AbstractClassExample.Shape

public abstract class Shape{

private string shape;

public Shape(string shape){

this.shape = shape;

Console.WriteLine("建立" + shape);

}

abstract public double Area();

}

### AbstractClassExample.Square

public class Square : Shape

{

double a;

public Square(double a): base("正方形")

{

this.a = a;

}

public override double Area()

{

return a * a;

}

}

### 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;

}

}

### 練習

• 將程式DrawingAllShapes中的類別Shape改為抽象類別，並將Shape.Draw()改為抽象函式

• 修飾語

• 欄位變數

• 建構式

• 函式方法覆寫與實作

### 綱要

• 預設類別System.Object

• LSP: Liskov替代性原理

• 抽象類別

• DIP: 依存性反轉原理

• 介面

• ISP: 介面分離原理

• 多重介面

• *多重介面鑄形

### 依存性反轉原理(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

### DIPViolationExample.LampStatus及ButtonStatus

public enum LampStatus

{

OFF = 0,

ON = 1

}

public enum ButtonStatus

{

RELEASED = 0,

PRESSED = 1

}

### 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;

}

}

### 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; }

}

### 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();

}

}

}

### 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");

### DIPViolationExample.Program.Main() 片段 (2/3)

if (rand.Next() % 2 == 1)

{

if (button.Status ==

ButtonStatus.PRESSED)

{

button.Release();

}

else

{

button.Press();

}

}

### DIPViolationExample.Program.Main() 片段 (3/3)

if (lamp1.Status == LampStatus.OFF)

{

Console.WriteLine("lamp1 is off");

}

else

{

Console.WriteLine("lamp1 is on");

}

Console.WriteLine();

}

### 綱要

• 預設類別System.Object

• LSP: Liskov替代性原理

• 抽象類別

• DIP: 依存性反轉原理

• 介面

• ISP: 介面分離原理

• 多重介面

• *多重介面鑄形

### 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());

interface Shape

{

double Area();

}

### UsingInterface.Square

public class Square : Shape

{

double a;

public Square(double a)

{

this.a = a;

}

public double Area()

{

return a * a;

}

}

### UsingInterface.Circle

public class Circle : Shape

{

double r;

public Circle(double r)

{

this.r = r;

}

public double Area()

{

return Math.PI * r * r;

}

}

### 介面 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();

}

### 介面 vs. 抽象類別 (2/2)

public class Square : Shape{

. . .

public double Area(){

return a * a;

}

}

--------------------------------------------

public class Square : Shape{

. . .

public override double Area(){

return a * a;

}

}

### ButtonAndLamp

public interface SwitchableDevice{

void TurnOn();

void TurnOff();

}

public enum LampStatus{

OFF = 0,

ON = 1

}

public enum ButtonStatus{

RELEASED = 0,

PRESSED = 1

}

### 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;

}

}

### 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; }

}

### 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();

}

}

}

### 練習

• 在程式ButtonAndLamp增加類別Fan，使Button也可以控制Fan的開與關

### 綱要

• 預設類別System.Object

• LSP: Liskov替代性原理

• 抽象類別

• DIP: 依存性反轉原理

• 介面

• ISP: 介面分離原理

• 多重介面

• *多重介面鑄形

### 介面分離原理(ISP: Interface-Segregation Principle)

• Clients should not be forced to depend on methods that they do not use

• 避免同一介面中有目的不同的多群函式宣告

### ISPViolationExample

interface TimerClient{

void TimeOut();

}

interface Door : TimerClient{

void Lock();

void Unlock();

bool IsOpen();

}

enum DoorStatus{

CLOSED = 0,

OPEN = 1

}

### 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;

}

### ISPViolationExample.Timer (2/2)

++t;

if (t % timeout == 0){

client.TimeOut();

}

}

}

### 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;

}

### ISPViolationExample.TimedDoor (2/2)

public void TimeOut(){

Lock();

}

}

### 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)

{

### ISPViolationExample.Program.Main片段(2/2)

if (tDoor.IsOpen())

{

Console.WriteLine(

"n = " + n + "\t tDoor is open");

}

else

{

Console.WriteLine(

"n = " + n + "\t tDoor is closed");

}

}

### 綱要

• 預設類別System.Object

• LSP: Liskov替代性原理

• 抽象類別

• DIP: 依存性反轉原理

• 介面

• ISP: 介面分離原理

• 多重介面

• *多重介面鑄形

### MultiInterface.Program.Main片段

Floatplane fp = new Floatplane();

fp.Sail();

fp.Fly();

interface Plane

{

void Fly();

}

interface Ship

{

void Sail();

}

### MultiInterface.Floatplane

public class Floatplane : Plane, Ship{

public Floatplane(){

Console.WriteLine("建立水上飛機");

}

public void Sail(){

Console.WriteLine("水上滑行");

}

public void Fly(){

Console.WriteLine("空中飛行");

}

}

### TimedDoorSimulation片段

interface TimerClient{

void TimeOut();

}

interface Door{

void Lock();

void Unlock();

bool IsOpen();

}

. . .

class TimedDoor : Door, TimerClient

{

. . .

}

### 綱要

• 預設類別System.Object

• LSP: Liskov替代性原理

• 抽象類別

• DIP: 依存性反轉原理

• 介面

• ISP: 介面分離原理

• 多重介面

• *多重介面鑄形

### 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;

Console.WriteLine(

"sq的面積以矩形公式計算得"+rec.Area() );

}

### CastMultiInterfaces

interface Rectangle

{

double Area();

}

interface Rhombus

{

double Area();

}

### 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;

}

### CastMultiInterfaces.Square (2/2)

double Rectangle.Area()

{

return a * a;

}

double Rhombus.Area()

{

return 0.5 * d * d;

}

}