繼承與多型 (Inheritance and Polymorphism)

Slides:



Advertisements
Similar presentations
第五章 类的继承和派生 Inheritance/extends/derive. 教学目标 理解继承的概念和作用 派生类的定义 理解访问修饰符 protected 方法的重写 继承下的构造函数的使用 继承下的 finalize 方法的使用 理解超类和子类的关系.
Advertisements

系統分析與設計 楊子青 H-1 H 、物件導向技術 n 物件導向的基本概念 – 物件、類別 – 封裝、繼承 – 同名異式 ( 多型 ) 、超荷 ( 過載 ) n 物件導向分析與設計及塑模工具 n UML 塑模工具.
JAVA 编 程 技 术 主编 贾振华 2010年1月.
第一單元 建立java 程式.
项目7 面向对象高级.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
第十八讲 设计原理(Design Principles)
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
四資二甲 第三週作業 物件導向程式設計.
C#程序设计案例教程 第3章 程 序 结 构.
第一章 面向对象程序设计.
程設一.
C#程序设计 10软件1、2班 王槐彬 计算机工程学院.
類別與物件 Class & Object.
Ch07 介面與多重繼承 物件導向程式設計(II).
第5章 Java中类、对象、接口 及包的概念 5.1 类的基本概念 5.2 类的继承概念 5.3 抽象类和接口 5.4 包.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
程設一.
第5章 面向对象程序设计 本章要点 5.1 面向对象程序设计概述 5.2 Java语言的面向对象程序设计 5.3 方法的使用和对象数组
第八章 C#高级编程.
H、物件導向技術 物件導向的基本概念 物件、類別 封裝、繼承 同名異式(多型) 、超荷(過載) 物件導向分析與設計及塑模工具 UML塑模工具.
第二章 C# 基础知识.
第四章 在 C# 中实现 OOP 概念.
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
Classes Lecturer: 曾學文.
第六章 类的扩展与继承.
程式敘述執行順序的轉移 控制與重複、方法 Lecturer:曾學文.
Ch10 類別與物件-方法 Java程式設計(2).
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
類別(class) 類別class與物件object.
Java程序设计 第9章 继承和多态.
Object-Oriented Programming: Polymorphism
C#面向对象程序设计 $7 继承和多态性.
類別的繼承 Vehicle Car.
中国矿大计算机学院杨东平 第5章 接口和包 中国矿大计算机学院杨东平
第6章 继承和接口设计 6.1 继 承 6.2 多态性 6.3 抽象类 6.4 接口 6.5 接口在集合排序中的应用.
視窗程式設計 (Windows Programming)
例外處理與 物件序列化(Exception Handling and Serialization of Objects)
檔案讀寫與例外處理 (File IO and Exception Handling)
2019/1/16 Java语言程序设计-类与对象 教师:段鹏飞.
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
Java程序设计 第2章 基本数据类型及操作.
第5讲 使用类和对象编程(三) 内部类 实例 程序控制结构 选择语句.
第六章 属性、索引器、委托和事件.
類別與物件 I (Classes and Objects I)
第一單元 建立java 程式.
標籤、按鈕、工具列、狀態列 (Labels, Buttons, Tool Strips, and Status Strips)
第16章 虛擬與多形 16-1 虛擬函數 16-2 純虛擬函數與抽象類別 16-3 多形 16-4 虛擬繼承與虛擬解構子.
10 多載函數 10.1 多載概論 多載一般函數 多載成員函數 10-3
第三章 C# 基础知识.
第7章 繼承/多型/介面 注意: 本投影片僅供本書上課教師使用,非經同意請勿上網轉載或供拷貝.
C#程序设计基础 $3 成员、变量和常量.
繼承與多型 (Inheritance and Polymorphism)
第二章 Java基本语法 讲师:复凡.
Inheritance -II.
Interfaces and Packages
Object-Oriented Programming in C++ 第二章 类和对象
<编程达人入门课程> 本节内容 为什么要使用变量? 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ:
第二章 Java语法基础.
目标 流程控制 字符串处理 C# 的类和对象 C# 访问修饰符 C# 构造函数和析构函数.
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
方法進階及物件導向基礎 Lecturer: 楊昌樺.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
Go 语言编程 —— 平台研发部 吴植民.
第6單元 6-1 類別的繼承 (Class Inheritance) 6-2 抽象類別 (Abstract Class)
JAVA 程式設計與資料結構 第三章 物件的設計.
第2章 Java语言基础.
第6章 继承和多态 伍孝金
Presentation transcript:

繼承與多型 (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; public int Multiply(int a, int b) int result = a * b;

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

表示繼承的UML類別圖

類別繼承之階層關係 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

物件記憶體分配模型 A a = new A(); data1 a B b = new B(); C c = new C(); data2 c

練習 實作並測試下列繼承關係

綱要 繼承 修飾語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類別圖

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

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類別圖

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; 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 類別圖

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()); 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) "); string answer = Console.ReadLine(); return (answer == "Y" || answer == "y"); }

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

綱要 繼承 修飾語protected 限制繼承 繼承架構下的建構函式呼叫 OCP:開放-封閉原理 多型 二十一點模擬程式0.1版 覆寫與隱藏

UsingBase.Program類別圖

UsingBase.Program片段 (1/3) // Define the base class class Car { public virtual void DescribeCar() System.Console.WriteLine( "Four wheels and an engine."); }

UsingBase.Program片段 (2/3) // Define the derived classes class ConvertibleCar : Car { public new virtual void DescribeCar() base.DescribeCar(); Console.WriteLine( "A roof that opens up."); }

UsingBase.Program片段 (3/3) class Minivan : Car { public override void DescribeCar() base.DescribeCar(); Console.WriteLine( "Carries seven people."); }

UsingBase.Program. 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();

UsingBase.Program. 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 隱藏: new 主要用於Polymorphism (多型) 執行時連結 只是取代基底類別同名之成員變數與方法 仍是編譯時連結

練習 利用偵錯器體驗了解程式UsingBase

綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理 多重介面 *多重介面鑄形

預設類別System.Object 一致化型別 成員函式 Equals GetHashCode GetType ReferenceEquals ToString Finalize

InheritingObject.Program. 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.Program 片段 class Test { override public string ToString() return "覆寫InheritingObject.Test"; }

Boxing 與 Unboxing int x = 10; Object obj = (Object) x; // boxing 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

IS-A 關係與繼承

LSPViolationExample.Program 片段 (1/4) class Rectangle { private int width; private int height; virtual public int Width set { width = value; } } virtual public int Height set { height = value; }

LSPViolationExample.Program 片段 (2/4) public int Area() { return width * height; }

LSPViolationExample.Program 片段 (3/4) class Square : Rectangle { override public int Width set { base.Width = value; base.Height = value; } } override public int Height

LSPViolationExample.Program 片段 (4/4) 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的離開條件 符合LSP之子類別函式覆寫的要求 Debug.Assert( (width == value) && (height == old.height)); 符合LSP之子類別函式覆寫的要求 進入條件需等於父類別被覆寫函式之進入條件,或更寬鬆 離開條件需等於父類別被覆寫函式之離開條件,或更嚴格

綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理 多重介面 *多重介面鑄形

抽象類別

AbstractClassExample.Program. 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.Program 片段 (1/3) public abstract class Shape { private string shape; public Shape(string shape) { this.shape = shape; Console.WriteLine("建立" + shape); } abstract public double Area();

AbstractClassExample.Program 片段 (2/3) public class Square : Shape { double a; public Square(double a): base("正方形") this.a = a; } public override double Area() return a * a;

AbstractClassExample.Program 片段 (3/3) 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

一個違反DIP的例子

狀態圖(State Chart)

DIPViolationExample.Program 片段 (1/4) public enum LampStatus { OFF = 0, ON = 1 } public enum ButtonStatus RELEASED = 0, PRESSED = 1

DIPViolationExample.Program 片段 (2/4) 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.Program 片段 (3/4) 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.Program 片段 (4/4) 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. 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. Program.Main() 片段 (2/3) if (rand.Next() % 2 == 1) { if (button.Status == ButtonStatus.PRESSED) button.Release(); } else button.Press();

DIPViolationExample.Program. 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. 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());

UsingInterface.Program片段(1/3) interface Shape { double Area(); }

UsingInterface.Program片段(2/3) public class Square : Shape { double a; public Square(double a) this.a = a; } public double Area() return a * a;

UsingInterface.Program片段(3/3) 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 override double Area() {

一個符合DIP的設計

ButtonAndLamp.Program (1/4) public interface SwitchableDevice { void TurnOn(); void TurnOff(); } public enum LampStatus { OFF = 0, ON = 1 public enum ButtonStatus { RELEASED = 0, PRESSED = 1

ButtonAndLamp.Program (2/4) 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.Program (3/4) 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.Program (4/4) 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 避免同一介面中有目的不同的多群函式宣告

一個違反ISP的例子

ISPViolationExample.Program 片段 (1/4) interface TimerClient { void TimeOut(); } interface Door : TimerClient { void Lock(); void Unlock(); bool IsOpen(); enum DoorStatus { CLOSED = 0, OPEN = 1

ISPViolationExample.Program 片段 (2/4) 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.Program 片段 (3/4) public void Advance() { ++t; if (t % timeout == 0) { client.TimeOut(); } class TimedDoor : Door { private DoorStatus status = DoorStatus.CLOSED;

ISPViolationExample.Program 片段 (4/4) public bool IsOpen() { return (status == DoorStatus.OPEN); } public void Lock() { if (IsOpen()) status = DoorStatus.CLOSED; public void Unlock() { if (!IsOpen()) status = DoorStatus.OPEN; public void TimeOut() { Lock();

ISPViolationExample.Program. 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. Program.Main() 片段(2/2) if (tDoor.IsOpen()) { Console.WriteLine( "n = " + n + "\t tDoor is open"); } else "n = " + n + "\t tDoor is closed"); timer.Advance();

綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理 多重介面 *多重介面鑄形

程式MultiInterface類別圖

MultiInterface.Program. Program.Main()片段 Floatplane fp = new Floatplane(); fp.Sail(); fp.Fly();

MultiInterface.Program片段 (1/2) interface Plane { void Fly(); } interface Ship void Sail();

MultiInterface.Program片段 (2/2) public class Floatplane : Plane, Ship { public Floatplane() { Console.WriteLine("建立水上飛機"); } public void Sail() { Console.WriteLine("水上滑行"); public void Fly() { Console.WriteLine("空中飛行");

練習 利用多重介面, 設計並測試一個類別Clock_Radio,兼具介面Clock之GetTime()與介面Radio之PlayMusic()功能

一個符合ISP的設計

TimedDoorSimulation.Program 片段 interface TimerClient { void TimeOut(); } interface Door { void Lock(); void Unlock(); bool IsOpen(); . . . class TimedDoor : Door, TimerClient {

綱要 預設類別System.Object LSP: Liskov替代性原理 抽象類別 DIP: 依存性反轉原理 介面 ISP: 介面分離原理 多重介面 *多重介面鑄形

程式CastMultiInterfaces類別圖

CastMultiInterfaces.Program. 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() ); }

CastMultiInterfaces.Program 片段(1/3) interface Rectangle { double Area(); } interface Rhombus

CastMultiInterfaces.Program 片段(2/3) 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.Program 片段(3/3) double Rectangle.Area() { return a * a; } double Rhombus.Area() return 0.5 * d * d;