设计模式之美:Type Object(类型对象)
索引
意图
允许在运行时动态灵活的创建新的 "类",而这些类的实例代表着一种不同的对象类型。
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模式来解决。
实现
实现方式(一):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(类型对象)的更多相关文章
- Type Object——类型对象
clr会为应用程序使用的每个类型创建一个内部数据结构,这种数据结构称为类型对象. 具有泛型类型参数的类型称为开放类型(open type),CLR禁止构造开放类型的任何实例. 代码引用一个泛型类型时, ...
- 设计模式之美:Object Pool(对象池)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):实现 DatabaseConnectionPool 类. 实现方式(二):使用对象构造方法和预分配方式实现 ObjectPool ...
- object类型对象 ref参数如何理解?
class Program { static void Main(string[] args) { Student stu = new Student { Name = "老王" ...
- list,set中可以存放Object类型对象
List<JSONObject> series = new ArrayList<JSONObject>();
- C#动态设置匿名类型对象的属性
用C#写WPF程序, 实现功能的过程中碰到一个需求: 动态设置对象的属性,属性名称是未知的,在运行时才能确定. 本来这种需求可以用 Dictionary<string, object> 实 ...
- 设计模式之美:Extension Object(扩展对象)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):使用示例结构实现 Extension Object. 实现方式(二):使用泛型实现 IExtensibleObject<T ...
- 设计模式之美:Null Object(空对象)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Null Object 的示例实现. 意图 通过对缺失对象的封装,以提供默认无任何行为的对象替代品. Encapsulate t ...
- 设计模式之美:Role Object(角色对象)
索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):Role Object 的示例实现. 意图 通过明确地附加角色对象到目标对象中,以使对象可以适配不同的客户需求.每个角色对象都代 ...
- 直接修改托管堆栈中的type object pointer(类型对象指针)
都知道.NET是一个强对象类型的框架. 那么对于对象类型又是怎么确定的呢. 最初的我简单认为数据的类型就是定义时字段的类型修饰决定的(回来发现这种观点是绝对错误的) 我们知道引用对象存储在托管堆栈中, ...
随机推荐
- 配置tomcat解压版
配置Tomcat[解压版] 选择解压版的Tomcat的理由是可以让我们使用多个Tomcat,但是配置上就会出现一些问题,需要我们手动进行更改配置.我的Tomcat版本是:apache-tomcat-6 ...
- Spring Boot + Bootstrap 出现"Failed to decode downloaded font"和"OTS parsing error: Failed to convert WOFF 2.0 font to SFNT"
准确来讲,应该是maven项目使用Bootstrap时,出现 "Failed to decode downloaded font"和"OTS parsing error: ...
- fname
from lxml import etreeimport requestsdef getHtml(html): novelcontent = requests.get(html).content re ...
- 在vCenter5.5中为用户创建角色,管理虚拟机
在vSphere的使用中,如有只有vCenter+ESXi节点的两级配置,为了达到多租户管理及权限分配,可以在vCenter5.5中为用户创建角色,管理虚拟机 1.以管理员身份登陆vCenter 2. ...
- C# Form.Close 的释放问题
今天使用From窗口Close后,发现From的资源还存在,并没有释放资源,只有在程序关闭的时候才去释放. Form1:button按钮 private void button1_Click(obje ...
- 通过通知监听键盘的状态来改变View的位置
#import "ViewController.h" @interface ViewController ()<UITextFieldDelegate>{ UIV ...
- php设计模式--单例模式
单例模式(Singleton Pattern 单件模式或单元素模式) 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式是一种常见的设计模式,在计算机系统中,线程池.缓 ...
- yaf框架学习笔记
1.yaf框架支持简单的试图引擎,并且支持用户自定义视图引擎,比如smarty. 2.Yaf_Request_Http::getQuery ,Yaf_Request_Http::getQuery ( ...
- GCC4.8.2升级安装
一.查看本机GCC版本: 使用gcc -v 查看本机版本信息,我的gcc版本为: gcc 版本 4.4.6 20120305 (Red Hat 4.4.6-4) (GCC) 二.升级或安装编译器: 1 ...
- AngularJS之ng-options的best practise
废话不多说,直接上代码. function MySelectCtrl($scope) { $scope.Model = [ { id: 10002, MainCategory: '男', Produc ...