前文使用 C++ template 改良策略模式,本文改用 C# 2.0 引入的泛型來達成相同目標。
在策略模式中,為讓策略完整存取物件的 public 方法/屬性,常傳遞 this 給策略:
public void drawShape() {
this.shape.draw(this);
}
介面因此必須定義為:
interface IShape {
void draw(Grapher grapher);
}
完整程式碼見下:
/**/*
(C) OOMusou 2007 https://oomusou.cnblogs.com
Filename : DP_StrategyPattern3_polymorphism_this.cs
Compiler : Visual Studio 2005 / C# 2.0
Description : Demo how to use Strategy Pattern with this
Release : 04/07/2007 1.0
*/
using System;
interface IShape {
void draw(Grapher grapher);
}
class Grapher {
private IShape shape;
private string text;
public Grapher() { }
public Grapher(IShape shape) : this(shape, "Hello Shape!!") { }
public Grapher(IShape shape, string text) {
this.shape = shape;
this.text = text;
}
public void drawShape() {
this.shape.draw(this);
}
public void setShape(IShape shape, string text) {
this.text = text;
this.shape = shape;
}
public string getText () {
return this.text;
}
}
class Triangle : IShape {
public void draw(Grapher grapher) {
Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
}
}
class Circle: IShape {
public void draw(Grapher grapher) {
Console.WriteLine("Draw {0:s} in Circle", grapher.getText());
}
}
class Square : IShape {
public void draw(Grapher grapher) {
Console.WriteLine("Draw {0:s} in Square", grapher.getText());
}
}
class main {
public static void Main() {
Grapher theGrapher = new Grapher(new Square());
theGrapher.drawShape();
theGrapher.setShape(new Circle(), "Hello C#!!");
theGrapher.drawShape();
}
}
執行結果:
Draw Hello Shape!! in Square
Draw Hello C#!! in Circle
IShape 介面綁定 Grapher 型別,若未來新增 Painter 類別(與 Grapher 無繼承關係)也想使用同樣策略,將無法實作,因為介面已鎖定型別。
將介面改為泛型:
interface IShape<T> {
void draw(T grapher);
}
實作類別亦需修改:
class Triangle<T> : IShape<T> where T : IGrapher {
public void draw(T grapher) {
Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
}
}
C# 的 where 關鍵字用於限制泛型型別參數(稱為 constraint),這是 C# 泛型與 C++ template 最大不同:C# 採「有限制的泛型」或「基於介面的泛型」(強型別泛型),泛型必須受限於介面;C++ 則無此限制。
where T : IGrapher 表示泛型 T 必須實作 IGrapher 介面。因為策略會呼叫 getText(),故定義:
interface IGrapher {
string getText();
}
未來若 Painter 也想使用策略,只需實作 IGrapher 即可。
/**/*
(C) OOMusou 2007 https://oomusou.cnblogs.com
Filename : DP_StrategyPattern3_polymorphism_this.cs
Compiler : Visual Studio 2005 / C# 2.0
Description : Demo how to use Strategy Pattern with this by Generic
Release : 04/07/2007 1.0
*/
using System;
interface IGrapher {
string getText();
}
interface IShape<T> {
void draw(T grapher);
}
class Grapher : IGrapher {
private IShape<Grapher> shape;
private string text;
public Grapher() { }
public Grapher(IShape<Grapher> shape) : this(shape, "Hello Shape!!") { }
public Grapher(IShape<Grapher> shape, string text) {
this.shape = shape;
this.text = text;
}
public void drawShape() {
this.shape.draw(this);
}
public void setShape(IShape<Grapher> shape, string text) {
this.text = text;
this.shape = shape;
}
public string getText () {
return this.text;
}
}
class Triangle<T> : IShape<T> where T : IGrapher {
public void draw(T grapher) {
Console.WriteLine("Draw {0:s} in Triangle", grapher.getText());
}
}
class Circle<T> : IShape<T> where T : IGrapher{
public void draw(T grapher) {
Console.WriteLine("Draw {0:s} in Circle", grapher.getText());
}
}
class Square<T> : IShape<T> where T : IGrapher {
public void draw(T grapher) {
Console.WriteLine("Draw {0:s} in Square", grapher.getText());
}
}
class main {
public static void Main() {
Grapher theGrapher = new Grapher(new Square<Grapher>());
theGrapher.drawShape();
theGrapher.setShape(new Circle<Grapher>(), "Hello C#!!");
theGrapher.drawShape();
}
}
執行結果:
Draw Hello Shape!! in Square
Draw Hello C#!! in Circle
C# 泛型優勢在於可指定多重限制:
where T : IGrapher, IPainter
這比單一介面的多型更強,可視為「更強的多型」。但 C# 泛型基於介面/類別,稱為「強型別泛型」,好處是編譯期發現錯誤、支援 IntelliSense;C++ 屬「弱型別泛型」,常在執行期才發現錯誤。C# 泛型較穩定,但限制較多,較無「泛型味道」。作者個人偏好 C++ template 的自由度。
即便如此,C# 泛型仍可視為「更強的多型」,許多原本用多型的設計,可考慮改用泛型擴大適用範圍。