http://www.tracefact.net/Design-Pattern/Abstract-Factory.aspx
引言
在前一节,我们介绍了Strategy模式,并使用此模式实现了一个根据角色的职业来分配技能的范例(实际也就是动态地为类分配方法)。作为一款奇幻RPG,有了职业,我们还应当可以为角色选择种族,比如说:人类(Human)、精灵(Elf)、矮人(Dwarf)、兽人(Orc)等等。而这四个种族又有着截然不同的外形,精灵皮肤灰白、有着长长的耳朵、没有体毛和胡须;矮人的皮肤与人类近似,但是身材矮小、通常留着浓密的胡子;兽人则有着绿色的皮肤和高大的身躯,并且面目丑陋。本文将讨论如何使用GOF的Abstract Factory抽象工厂来实现这样的角色外形设计。
面向实现的方式
简单起见,我们假设角色身体由三部分构成,分别是:头(Head)、身材(Stature)、皮肤(Skin)。那么对于人类的构造,我们的第一反应自然而然地想到:它应当由 HumanHead、HumanStature、HumanSkin三个类复合而成。对于精灵也是类似的设计,于是,我们实现了下面这样的设计(本文将仅以人类和精灵为例介绍):
抽象组成身体的实体类
我们发现这样做,每个角色与他的身体部件是牢牢绑定在一起的,每创建一个角色,我们都需要为它先行创建所有其复合的类(组成身体的实体类(Concret Class),比如HumanHead)。按照面向对象的思想,我们想到应该对这一过程进行封装,将创建角色部件这件事委派给其他的类来完成。观察上图,我们发现尽管角色不同,但它们都是由三个部分构成,所以,我们所能想到的实现这一过程的第一步,就是对组成身体的实体类进行抽象,我们定义三个接口:Head、Stature、Skin,代表身体的三个部分,并且让Human和Elf的实体类去实现这个接口:
观察上图,我们发现尽管定义了接口,但是如果角色Human和Elf仍然与接口的实体类关联,那么效果与面向实现完全相同。现在,是时候对设计做些改动,我们让Human和Elf与接口关联,而不是与接口的实现关联(OO思想:面向接口编程,而不是面向实现编程)。这一次,我们的设计变成下图:
一眼望去,我们发现的第一个问题就是:Human与Elf惊人地相似,再仔细看看,我们注意到它们除了名字不同其余的完全一样。我们不禁思考:有必要把两个完全一样的类起两个名字分别保存么?答案是没有必要,我们将它们合并成一个类,起名叫Race,设计再次变成下面这样:
创建工厂类
看到这里,我们可能会想:现在结构似乎已经很完善了,我们定义了接口来解决问题,也没有为不同的角色创建多个不同的类,而只要在Race的构造函数中为代表身体部件的变量赋不同的值,就可以创建不同种族的角色。好的,那么我们来看看如果要创建一个Human 代码需要如何编写:
Head head = new HumanHead();
Stature stature = new HumanStature();
Skin skin = new HumanSkin();
Race human = new Race(head, stature, skin);
而Race的构造函数是这样的:
public Race(head, stature, skin){
this.head = head;
this.stature = stature;
this.skin = skin;
}
我们看到,仅仅创建一个类这样似乎太麻烦了,而且身体的部分类是角色的组成部分,为什么它们要先于角色创建呢?
这时候,我们想到如果有一个类可以专门负责创建身体部件这件事,当我们想要创建角色的时候,将这个类传递给Race的构造函数就可以了。我们管创建Human身体组成部分的类称作:HumanPartsFactory,创建Elf身体部分的类称作ElfPartsFacotry。那么它们应该是这样的:
现在,我们再要创建一个Human,代码变成了这样:
HumanPartsFactory humanFactory = new HumanPartsFactory();
Race Human = new Race(humanFactory);
相应的,我们的构造函数也需要改一改:
public Race(HumanPartsFactory humanFacotry){
head = humanFactory.CreateHead();
stature = humanFactory.CreateStature();
skin = humanFactory.CreateSkin();
}
一切似乎都很好,直到我们需要创建一个Elf的时候... 我们发现Race的构造函数只能接受一个HumanPartsFactory类型的参数,为了传递ElfPartsFactory,我们将不得不再添加一个接受ElfPartsFactory类型的构造函数。这样显然不好,这一次,有了之前的经验,我们知道我们可以通过同样的方法来解决。
看到这里,你是否能够体会到一些“面向接口”编程的意味?注意到RacePartsFactory,它内部的方法返回的都是接口类型,而其实体子类的方法返回的都是接口的实体类。如果我们之前不声明那看似无用的接口,这里是无法实现的。
现在,我们再次修改Race的构造函数:
public Race(RacePartsFactory raceFacotry){
head = raceFacotry.CreateHead();
stature = raceFacotry.CreateStature();
skin = raceFacotry.CreateSkin();
}
当我们需要一个Human的时候:
RacePartsFactory humanFactory = new HumanPartsFactory();
Race human = new Race(humanFactory);
当我们需要一个Elf的时候:
RacePartsFactory elfFactory = new ElfPartsFactory();
Race elf = new Race(elfFactory);
Abstract Factory设计模式
上面做的这些,使我们又完成了一个设计模式:Abstract Factory。它的正式定义是这样的:提供一个接口用于创建一系列相互关联或者相互依赖的对象,而不需要指定它们的实体类。
下面是本例中 Abstract Factory模式的最终图:
代码实现和测试
using System;
using System.Collections.Generic;
using System.Text;
namespace AbstractFactory {
// 定义构成身体部分的接口
public interface IHead { string name { get;} }
public interface IStature { string name { get;} }
public interface ISkin { string name { get;} }
// 组成 Human 的类
public class HumanHead : IHead { public string name { get { return "Human Head"; } } }
public class HumanStature : IStature { public string name { get { return "Human Stature"; } } }
public class HumanSkin : ISkin { public string name { get { return "Human Skin"; } } }
// 组成 Elf 的类
public class ElfHead : IHead { public string name { get { return "Elf Head"; } } }
public class ElfStature : IStature { public string name { get { return "Elf Stature"; } } }
public class ElfSkin : ISkin { public string name { get { return "Elf Skin"; } } }
// 定义工厂接口
public interface IRacePartsFactory {
IHead CreateHead();
ISkin CreateSkin();
IStature CreateStature();
}
// 定义Human身体的工厂类
public class HumanPartsFactory : IRacePartsFactory {
public IHead CreateHead() {
return new HumanHead();
}
public IStature CreateStature() {
return new HumanStature();
}
public ISkin CreateSkin() {
return new HumanSkin();
}
}
// 定义Elf身体的工厂类
public class ElfPartsFactory : IRacePartsFactory {
public IHead CreateHead() {
return new ElfHead();
}
public IStature CreateStature() {
return new ElfStature();
}
public ISkin CreateSkin() {
return new ElfSkin();
}
}
// 定义 Race 类
public class Race {
public IHead Head; // 做示范用,所以没有构建属性
public IStature Stature;
public ISkin Skin;
public Race(IRacePartsFactory raceFactory) {
Head = raceFactory.CreateHead();
Stature = raceFactory.CreateStature();
Skin = raceFactory.CreateSkin();
}
}
class Program {
static void Main(string[] args) {
// 创建一个 精灵(Elf)
Race Elf = new Race(new HumanPartsFactory());
Console.WriteLine(Elf.Head.name);
Console.WriteLine(Elf.Stature.name);
Console.WriteLine(Elf.Skin.name);
}
}
}
总结
本文中我们一步步学习了Abstract Factory抽象工厂模式的实现。
我首先介绍了我们奇幻RPG所面临的一个问题:我们需要创建形态各异的角色。随后,我们通过面向实现的方式来完成了这一过程,并讨论了它的不足。随后,我们先通过接口的使用对种族进行了抽象。接着,我们由将创建角色组成部分类的过程进行了封装,将这一过程委派给了工厂类。最后,我们又对工厂类进行了抽象,最终实现了Abstract Factory工厂模式。
希望本文能给你带来帮助。
相关推荐
本项目是基于多种编程语言的卡拉音乐播放器设计源码,包含625个文件,其中包括389个JavaScript文件、70个TypeScript文件、52个PNG图像文件、22个JSON配置文件、19个Vue文件、12个MAP文件、11个WXSS样式文件、9个WXML模板文件、7个SVG文件和5个EOT字体文件。系统专注于提供卡拉音乐播放器功能,支持歌曲播放、播放列表管理等功能,为用户提供了一个稳定、高效的卡拉音乐播放器。
qt 用法链接:https://menghui666.blog.csdn.net/article/details/139276655?spm=1001.2014.3001.5502 该项目基于QTabWidget和QTabBar实现了灵活的标签工具栏,主要包含如下功能: 1、标签栏可以收起,可以展开 2、可以在标签栏中添加新的标签界面 3、可以从标签工具栏中把界面拖出来,也可以拖回去 4、关闭拖出来的界面会自动回到标签工具栏 5、可以调换标签工具栏中界面的顺序
计算机专业毕业设计VB精品论文资源
开发语言:Java JDK版本:JDK1.8(或11) 服务器:tomcat 数据库:mysql 5.6/5.7(或8.0) 数据库工具:Navicat 开发软件:idea 依赖管理包:Maven 代码+数据库保证完整可用,可提供远程调试并指导运行服务(额外付费)~ 如果对系统的中的某些部分感到不合适可提供修改服务,比如题目、界面、功能等等... 声明: 1.项目已经调试过,完美运行 2.需要远程帮忙部署项目,需要额外付费 3.本项目有演示视频,如果需要观看,请联系我v:19306446185 4.调试过程中可帮忙安装IDEA,eclipse,MySQL,JDK,Tomcat等软件 重点: 需要其他Java源码联系我,更多源码任你选,你想要的源码我都有! https://img-blog.csdnimg.cn/direct/e73dc0ac8d27434b86d886db5a438c71.jpeg
Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。
Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。
Navicat Premium 16.3.4数据库管理工具,适配macos sonoma系统,解压安装即可使用 注: 1、需要允许下载和安装任何来源的App(系统设置>安全和隐私); 2、安装后打开提示已损坏,需要点取消,然后点已损坏修复.
Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。
计算机专业毕业设计VB精品论文资源
Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。
testtest1111
计算机专业毕业时间之VB精品论文源代码资源
源代码-FIS简单文章文章管理系统.zip 源代码-FIS简单文章文章管理系统.zip 源代码-FIS简单文章文章管理系统.zip 源代码-FIS简单文章文章管理系统.zip 源代码-FIS简单文章文章管理系统.zip 源代码-FIS简单文章文章管理系统.zip 源代码-FIS简单文章文章管理系统.zip 源代码-FIS简单文章文章管理系统.zip 源代码-FIS简单文章文章管理系统.zip
计算机专业毕业时间之VB精品论文源代码资源
计算机专业毕业设计VB精品论文资源
机械设计-壳体冲压工艺及模具设计【全套16张CAD图】+毕设说明书文档.zip
TMS320F28027资料
SQLyong.exe 各个版本,免费下载 SQLyog是业界著名的Webyog公司出品的一款简洁高效、功能强大的图形化MySQL数据库管理工具。使用SQLyog可以快速直观地让您从世界的任何角落通过网络来维护远端的MySQL数据库。
SQLyong.exe 各个版本,免费下载 SQLyog是业界著名的Webyog公司出品的一款简洁高效、功能强大的图形化MySQL数据库管理工具。使用SQLyog可以快速直观地让您从世界的任何角落通过网络来维护远端的MySQL数据库。
c语言火车票订票管理源码