索引

意图

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

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. ceph实践: 搭建环境

    作者:吴香伟 发表于 2014/09/26 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 本节主要参考官网的ADDING/REMOVING OSDS章节. 同步 ...

  2. python安装使用talib

    安装主要在http://www.lfd.uci.edu/~gohlke/pythonlibs/这个网站找到 按照需要的python版本和平台位数下载,然后直接用pip install 进行安装 包含的 ...

  3. ZKM混淆工具

    原创文章,尊重劳动,转载请标明出处 ZKM 介绍 一般使用步骤 直接使用 ZKM 脚本 使用 GUI 工具混淆,同时生成 ZKM 脚本 参考 ZKM 介绍 zkm 是一款付费的代码混淆工具. 一般使用 ...

  4. [C#.net] SendMessage

    函数功能:该函数将指定的消息发送到一个或多个窗口.此函数为指定的窗口调用窗口程序,直到窗口程序处理完消息再返回.该函数是应用程序和应用程序之间进行消息传递的主要手段之一.     函数原型:LRESU ...

  5. delphi XE5皮肤的使用

      做皮肤其实是项浩大的工程,从美工设计.到贴图.到程序设计,都非常的麻烦,如果不是一个非常成熟的产品且有很大的用户群体,并且公司具有相当实力,一般都不会去自己做皮肤,毕竟涉及的东西太多,一旦出现问题 ...

  6. C#实现按键精灵的'找图' '找色' '找字'的功能

    背景:游戏辅助功能通常使用按键精灵编写脚本,按键精灵的最大卖点就是能够找到画面中字,图,色,这对于模拟用户鼠标操作至关重要,这能找到道具,找到血量,实现自动打怪,自动补血,自动买卖道具,博主闲来无聊, ...

  7. C#中SQL Server数据库连接池使用及连接字符串部分关键字使用说明

    (1) 数据库的连接使用后,必须采用close()连接等效的方法关闭连接.只有关闭后,连接才能进入连接池. 参见微软的使用连接池说明:https://msdn.microsoft.com/zh-cn/ ...

  8. 线程池ExecutorService

    说到java开发,免不了跟多线程打交道.Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动.执行和关闭, ...

  9. PHP-仿ecshop生成验证码

    <?php /* 生成验证码 */ // 1.创建画布(基于已有图像) $n = mt_rand(1,5); $im = imagecreatefromjpeg('./images/captch ...

  10. [转] 多进程下数据库环境的恢复:DB_REGISTER

    http://www.cnblogs.com/promise6522/archive/2012/05/09/2493542.html