索引

意图

允许在运行时动态灵活的创建新的 "类",而这些类的实例代表着一种不同的对象类型。

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 模式有些类似于 StrategyState模式。这三种模式都是通过将对象内部的一些行为代理到外部的对象中。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模式来解决。

实现

实现方式(一):Type Object 的经典介绍。

  • TypeClass - Movie
  • TypeObject - Star Wars, The Terminator, Independence Day
  • Class - Videotape
  • Object - John's Star Wars, Sue's Star Wars
 namespace TypeObjectPattern.Implementation1
{
public class Movie
{
public string Title { get; set; }
public float RentalPrice { get; set; }
} public class Videotape
{
public Videotape(Movie movie)
{
this.Movie = movie;
} public Movie Movie { get; private set; } public Customer Renter { get; private set; }
public bool IsRented { get; private set; } public void RentTo(Customer customer)
{
IsRented = true;
Renter = customer;
Renter.ChargeForRental(this.Movie.RentalPrice);
}
} public class Customer
{
public string Name { get; set; } public void ChargeForRental(float rental)
{
// pay money
}
} public class Client
{
public void TestCase1()
{
Customer john = new Customer() { Name = "John" };
Customer sue = new Customer() { Name = "Sue" }; Movie starWars = new Movie()
{
Title = "Star Wars",
RentalPrice = ,
};
Movie terminator = new Movie()
{
Title = "The Terminator",
RentalPrice = ,
}; Videotape starWarsVideotapeForJohn = new Videotape(starWars);
starWarsVideotapeForJohn.RentTo(john); Videotape starWarsVideotapeForSue = new Videotape(starWars);
starWarsVideotapeForSue.RentTo(john); Videotape terminatorVideotapeForJohn = new Videotape(terminator);
terminatorVideotapeForJohn.RentTo(john);
}
}
}

实现方式(二):Type Object 在游戏设计中的使用。

想象我们正在制作在一个虚拟角色扮演游戏。我们的任务是设计一些邪恶的怪兽(Monster)来试图杀掉我们的英雄(Hero)。怪兽有着一些不同的属性,例如生命值(Health)、攻击力(Attacks)、图像、声音等,但以举例为目的我们仅考虑前两个属性。

游戏中的每个怪兽都有自己的生命值。生命值从满血开始,每次怪兽被创伤,生命值减少。怪兽会有一个用于描述攻击的字符串,当怪兽攻击英雄时,这个字符串会被显示到用户屏幕上。

游戏设计师告诉我们,怪兽会有不同的品种(Breed),例如:猛龙(Dragon)和巨魔(Troll)。每个怪兽品种都描述了一种怪兽,在一个场景下会有多个同一种的怪兽遍布在地牢(Dungeon)中。

怪兽的品种(Breed)决定的怪兽的起始生命值,比如猛龙(Dragon)的生命值会比巨魔(Troll)的高,以使猛龙更难被杀掉。同时,同一个品种的怪兽的攻击字符串也是相同的。

通过典型的 OO 设计,我们能得到下面这段代码:

 namespace TypeObjectPattern.Implementation2
{
public abstract class Monster
{
public Monster(int startingHealth)
{
Health = startingHealth;
} public int Health { get; private set; }
public abstract string AttackString { get; }
} public class Dragon : Monster
{
public Dragon()
: base()
{
} public override string AttackString
{
get { return "The dragon breathes fire!"; }
}
} public class Troll : Monster
{
public Troll()
: base()
{
} public override string AttackString
{
get { return "The troll clubs you!"; }
}
} public class Client
{
public void TestCase2()
{
Monster dragon = new Dragon();
Monster troll = new Troll();
}
}
}

这段代码浅显易懂,使用继承的方式设计类的层级结构。一个 Dragon 是一个 Monster,满足了 "is a" 的关系。每一个怪物的品种都会用一个子类来实现。

如果游戏中有成百上千的怪物种类,则类的继承关系变得庞大。同时也意味着,增加新的怪物品种就需要增加新的子类代码。

这是可以工作的,但并不是唯一的选择。我们可以尝试另外一种架构。

因为变化较多的部分是品种(Breed)的属性配置,包括生命值和攻击字符串。

所以我们可以将品种(Breed)抽取成单独的类,每个怪物类(Monster)包含一个品种类(Breed)。

Breed 类用于定义 Monster 的 "type"。每一个 Breed 的实例描述着一种 Monster 对象的概念上的 "type"。

 namespace TypeObjectPattern.Implementation3
{
public class Breed
{
public int Health { get; set; }
public string AttackString { get; set; }
} public class Monster
{
private Breed _breed; public Monster(Breed breed)
{
_breed = breed;
} public int Health
{
get { return _breed.Health; }
} public string AttackString
{
get { return _breed.AttackString; }
}
} public class Client
{
public void TestCase3()
{
Breed dragonBreed = new Breed()
{
Health = ,
AttackString = "The dragon breathes fire!",
};
Breed trollBreed = new Breed()
{
Health = ,
AttackString = "The troll clubs you!",
}; Monster dragon = new Monster(dragonBreed);
Monster breed = new Monster(trollBreed);
}
}
}

Type Object 在这里的优势在于,我们可以定义新的类型的怪物,而不用修改代码。并且可以在运行时动态生成新的对象和修改对象的属性。

参考资料

设计模式之美》为 Dennis Gao 发布于博客园的系列文章,任何未经作者本人同意的人为或爬虫转载均为耍流氓。

设计模式之美:Type Object(类型对象)的更多相关文章

  1. Type Object——类型对象

    clr会为应用程序使用的每个类型创建一个内部数据结构,这种数据结构称为类型对象. 具有泛型类型参数的类型称为开放类型(open type),CLR禁止构造开放类型的任何实例. 代码引用一个泛型类型时, ...

  2. 设计模式之美:Object Pool(对象池)

    索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):实现 DatabaseConnectionPool 类. 实现方式(二):使用对象构造方法和预分配方式实现 ObjectPool ...

  3. object类型对象 ref参数如何理解?

    class Program { static void Main(string[] args) { Student stu = new Student { Name = "老王" ...

  4. list,set中可以存放Object类型对象

    List<JSONObject> series = new ArrayList<JSONObject>();

  5. C#动态设置匿名类型对象的属性

    用C#写WPF程序, 实现功能的过程中碰到一个需求: 动态设置对象的属性,属性名称是未知的,在运行时才能确定. 本来这种需求可以用 Dictionary<string, object> 实 ...

  6. 设计模式之美:Extension Object(扩展对象)

    索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):使用示例结构实现 Extension Object. 实现方式(二):使用泛型实现 IExtensibleObject<T ...

  7. 设计模式之美:Null Object(空对象)

    索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Null Object 的示例实现. 意图 通过对缺失对象的封装,以提供默认无任何行为的对象替代品. Encapsulate t ...

  8. 设计模式之美:Role Object(角色对象)

    索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Role Object 的示例实现. 意图 通过明确地附加角色对象到目标对象中,以使对象可以适配不同的客户需求.每个角色对象都代 ...

  9. 直接修改托管堆栈中的type object pointer(类型对象指针)

    都知道.NET是一个强对象类型的框架. 那么对于对象类型又是怎么确定的呢. 最初的我简单认为数据的类型就是定义时字段的类型修饰决定的(回来发现这种观点是绝对错误的) 我们知道引用对象存储在托管堆栈中, ...

随机推荐

  1. 读 《.Net 之美》解析.Net Remoting (应用程序域)-- Part.1

    读 <.Net 之美>解析.Net Remoting (应用程序域)-Part1 理解 .Net Remoting 前言: 看张子阳老师的文章,总是给自己很大的信心,这个专题基本上以张老师 ...

  2. jsp文件引入js文件的方式(项目部署于web容器中)

    在页面中引入javascript文件的方式是多种多样的,本文介绍两种. 通过<script>标签插入js文件 通过这种方式引入的js,写对js文件和jsp文件的路径很重要.下面给出一个项目 ...

  3. Linux启动与登陆环境

    linux启动流程 参考:http://www.ruanyifeng.com/blog/2013/08/linux_boot_process.html 加载内核,首先读入/boot 目录下的内核文件. ...

  4. CMake 使用方法(转)

    CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程).他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的 ...

  5. js如何找到方法在哪个js文件

    在Console窗口中输入var f = methodA.prototype.constructor;console.log(f); 网络搜索到的方法.

  6. 122. Best Time to Buy and Sell Stock(二) leetcode解题笔记

    122. Best Time to Buy and Sell Stock II Say you have an array for which the ith element is the price ...

  7. Linux中的工作管理(Job Control )

    以前使用Linux老是会不小心按下Ctrl + z,然后就出现看不懂的情况,以为程序突然就没了,今天专门研究了下Linux下的几个快捷键和工作管理. 其中找到一篇很不错的文章,大部分是里面转载的. 原 ...

  8. make: Nothing to be done for `first'

    在qt目录下make后出现以下错误: make: Nothing to be done for `first' 解决:将你当前目录下的,删除你程序主要的 *.cpp 和 *.h文件以外的所有文件. 接 ...

  9. 使用Application Insights 做分析

    Application Insights on Windows Desktop apps, services and worker roles : https://azure.microsoft.co ...

  10. 优化后的 google提供的汉字转拼音类(针对某些htc等手机的不兼容情况)

    /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Versi ...