索引
允許在運行時動態靈活的創建新的 "類",而這些類的實例代表著一種不同的對象類型。
Allow the flexible creation of new “classes” by creating a single class, each instance of which represents a different type of object.
Type Object 模式包含兩個具體類。一個用于描述對象,另一個用于描述類型。每個對象都包含一個指向其類型的指針。
TypeClass
- 是 TypeObject 的種類。
- 每個種類都會有一個單獨的類。
TypeObject
- 是 TypeClass 的實例。
- 代表著一種對象。定義一種對象所包含的屬性和行為。
當以下情況成立時可以使用 Type Object 模式:
- 類的實例需要根據它們的通用屬性或者行為進行分組。
- 類需要為每個分組定義一個子類來實現該分組的通用屬性和行為。
- 類需要大量的子類或者多種變化的子類甚至無法預期子類的變化。
- 你需要有能力在運行時創建一些無法在設計階段預測的新的分組。
- 你需要有能力在類已經被實例化的條件下更改一個對象的子類。
- 運行時創建新的類型對象。
- 避免子類膨脹。
- 客戶程序無需了解實例與類型的分離。
- 可以動態的更改類型。
- Type Object 模式有些類似于 Strategy 和 State 模式。這三種模式都是通過將對象內部的一些行為代理到外部的對象中。Stategy 和 State 通常是純行為的代理,而 Type Object 則包含更多個共享數據狀態。State 可以被頻繁的更改,Type Object 則很少被改變。Strategy 通常僅包含一個職責,Type Object 則通常包含多個職責。
- Type Object 的實現與 Bridge 模式中的 Abstraction 和 Implementor 的關系很像。區別在于,客戶程序可以與 Type Object 直接協作,而不會直接與 Implementor 進行交互。
- Type Object 有點像 Flyweight 一樣處理它的對象。兩個對象使用相同的 Type Object 可能看起來是使用的各自的實例,但實際是共享的對象。
- Type Object 可以解決多個對象共享數據和行為的問題。類似的問題也可以用 Prototype 模式來解決。
- TypeClass - Movie
- TypeObject - Star Wars, The Terminator, Independence Day
- Class - Videotape
- Object - John's Star Wars, Sue's Star Wars
1 namespace TypeObjectPattern.Implementation1 2 { 3 public class Movie 4 { 5 public string Title { get; set; } 6 public float RentalPrice { get; set; } 7 } 8 9 public class Videotape 10 { 11 public Videotape(Movie movie) 12 { 13 this.Movie = movie; 14 } 15 16 public Movie Movie { get; private set; } 17 18 public Customer Renter { get; private set; } 19 public bool IsRented { get; private set; } 20 21 public void RentTo(Customer customer) 22 { 23 IsRented = true; 24 Renter = customer; 25 Renter.ChargeForRental(this.Movie.RentalPrice); 26 } 27 } 28 29 public class Customer 30 { 31 public string Name { get; set; } 32 33 public void ChargeForRental(float rental) 34 { 35 // pay money 36 } 37 } 38 39 public class Client 40 { 41 public void TestCase1() 42 { 43 Customer john = new Customer() { Name = "John" }; 44 Customer sue = new Customer() { Name = "Sue" }; 45 46 Movie starWars = new Movie() 47 { 48 Title = "Star Wars", 49 RentalPrice = 100, 50 }; 51 Movie terminator = new Movie() 52 { 53 Title = "The Terminator", 54 RentalPrice = 200, 55 }; 56 57 Videotape starWarsVideotapeForJohn = new Videotape(starWars); 58 starWarsVideotapeForJohn.RentTo(john); 59 60 Videotape starWarsVideotapeForSue = new Videotape(starWars); 61 starWarsVideotapeForSue.RentTo(john); 62 63 Videotape terminatorVideotapeForJohn = new Videotape(terminator); 64 terminatorVideotapeForJohn.RentTo(john); 65 } 66 } 67 }
實現方式(二):Type Object 在游戲設計中的使用。
想象我們正在制作在一個虛擬角色扮演游戲。我們的任務是設計一些邪惡的怪獸(Monster)來試圖殺掉我們的英雄(Hero)。怪獸有著一些不同的屬性,例如生命值(Health)、攻擊力(Attacks)、圖像、聲音等,但以舉例為目的我們僅考慮前兩個屬性。
游戲中的每個怪獸都有自己的生命值。生命值從滿血開始,每次怪獸被創傷,生命值減少。怪獸會有一個用于描述攻擊的字符串,當怪獸攻擊英雄時,這個字符串會被顯示到用戶屏幕上。
游戲設計師告訴我們,怪獸會有不同的品種(Breed),例如:猛龍(Dragon)和巨魔(Troll)。每個怪獸品種都描述了一種怪獸,在一個場景下會有多個同一種的怪獸遍布在地牢(Dungeon)中。
怪獸的品種(Breed)決定的怪獸的起始生命值,比如猛龍(Dragon)的生命值會比巨魔(Troll)的高,以使猛龍更難被殺掉。同時,同一個品種的怪獸的攻擊字符串也是相同的。
通過典型的 OO 設計,我們能得到下面這段代碼:
1 namespace TypeObjectPattern.Implementation2 2 { 3 public abstract class Monster 4 { 5 public Monster(int startingHealth) 6 { 7 Health = startingHealth; 8 } 9 10 public int Health { get; private set; } 11 public abstract string AttackString { get; } 12 } 13 14 public class Dragon : Monster 15 { 16 public Dragon() 17 : base(500) 18 { 19 } 20 21 public override string AttackString 22 { 23 get { return "The dragon breathes fire!"; } 24 } 25 } 26 27 public class Troll : Monster 28 { 29 public Troll() 30 : base(300) 31 { 32 } 33 34 public override string AttackString 35 { 36 get { return "The troll clubs you!"; } 37 } 38 } 39 40 public class Client 41 { 42 public void TestCase2() 43 { 44 Monster dragon = new Dragon(); 45 Monster troll = new Troll(); 46 } 47 } 48 }
這段代碼淺顯易懂,使用繼承的方式設計類的層級結構。一個 Dragon 是一個 Monster,滿足了 "is a" 的關系。每一個怪物的品種都會用一個子類來實現。
如果游戲中有成百上千的怪物種類,則類的繼承關系變得龐大。同時也意味著,增加新的怪物品種就需要增加新的子類代碼。
這是可以工作的,但并不是唯一的選擇。我們可以嘗試另外一種架構。
因為變化較多的部分是品種(Breed)的屬性配置,包括生命值和攻擊字符串。
所以我們可以將品種(Breed)抽取成單獨的類,每個怪物類(Monster)包含一個品種類(Breed)。
Breed 類用于定義 Monster 的 "type"。每一個 Breed 的實例描述著一種 Monster 對象的概念上的 "type"。
1 namespace TypeObjectPattern.Implementation3 2 { 3 public class Breed 4 { 5 public int Health { get; set; } 6 public string AttackString { get; set; } 7 } 8 9 public class Monster 10 { 11 private Breed _breed; 12 13 public Monster(Breed breed) 14 { 15 _breed = breed; 16 } 17 18 public int Health 19 { 20 get { return _breed.Health; } 21 } 22 23 public string AttackString 24 { 25 get { return _breed.AttackString; } 26 } 27 } 28 29 public class Client 30 { 31 public void TestCase3() 32 { 33 Breed dragonBreed = new Breed() 34 { 35 Health = 500, 36 AttackString = "The dragon breathes fire!", 37 }; 38 Breed trollBreed = new Breed() 39 { 40 Health = 300, 41 AttackString = "The troll clubs you!", 42 }; 43 44 Monster dragon = new Monster(dragonBreed); 45 Monster breed = new Monster(trollBreed); 46 } 47 } 48 }
Type Object 在這里的優勢在于,我們可以定義新的類型的怪物,而不用修改代碼。并且可以在運行時動態生成新的對象和修改對象的屬性。
參考資料
《設計模式之美》為 Dennis Gao 發布于博客園的系列文章,任何未經作者本人同意的人為或爬蟲轉載均為耍流氓。
文章列表