程序设计模式(二)创建型模式

2023-10-30

一、一个MAZE的构造过程

Maze的UML diagram如图所示


可以发现MapSite是斜体表示抽象类,有Room, Door, Wall三个子类继承,这三个就是用继承封装了MapSite的多向性。然而一个Maze,就是Room的聚合,所以Maze通过AddRoom方法来添加这些聚合这些Room. 最后通过MazeGame这个类,来构造这个Maze, 也就是帮助Maze这个对象执行它的AddRoom方法,帮Maze传递消息。所以我说, MazeGame就是Maze的创建类。重点看这个MazeGame创建类

class MazeGame {
  public:
    Maze* CreateMaze();//本节将要介绍的创建方法
    
    Maze* CreateSimpleMaze();
    Maze* CreateMaze(MazeFactory&);//后面讲的AbstractFactory模式
    Maze* CreateMaze(MazeBuilder&);//后面讲的Builder模式
    
    Maze* CreateComplexMaze (MazeBuilder& builder);
    //构建各种不同迷宫的方法入口
};

看看CreateMaze()方法如何实现:

Maze* MazeGame::CreateMaze() {
    Maze* aMaze = new Maze;
    Room* r1 = new Room(1);
    Room* r2 = new Room(2);
    Door *theDoor = new Door(r1, r2);

    aMaze->AddRoom(r1);
    aMaze->AddRoom(r2);

    r1->SetSide(North, new Wall);
    r1->SetSide(East, theDoor);
    r1->SetSide(South, new Wall);
    r1->SetSide(West, new Wall);
    r2->SetSide(North, new Wall);
    r2->SetSide(East, new Wall);
    r2->SetSide(South, new Wall);
    r2->SetSide(West, theDoor);

    return aMaze;
}

可以看出,这个Maze被create出来的结构是单一的,就只可能是两个房间,如何自由定义房间空间个数和四壁的内容而不重写代码,这个问题后面是不能解决的。同时,the room, the wall, the door的性质,在SetSide里面已经定义死了,如果下面我们需要给这个Maze定义两种风格,第一种风格是room加上魔法room,door加上用魔法才能打开的魔法Maze, 第二种风格room里面可能有炸弹,走到炸弹里面就死了的炸弹Maze, 用非继承的方法,那么我们呢需要重新写两个Room这个类,在EnchantedMaze中要扩展一个EnchantedRoom类,在BombedMaze中要扩展RoomWithABoom类,如下图所示。


一般的思维,是在CreateMaze的时候,输入的对象还是那个Maze, 但需要给Maze添加一个风格属性,同时在CreateMaze()开头就要添加一行语句判断创建的是哪种风格的Maze, 然后分别对不同的Maze.style, 需要改Room* r1 = new Room(1) 为 Room* r1 = new EnchantedRoom(1) 和 Room* r1 = new RoomWithABomb(1) ,这样一下子就增加了很多的代码量,因为case变多了。所以第一个想法就是调整CreateMaze的输入,这样引出了第一种模式AbstractFactory.


二、AbstractFactory

在我们定义一个基础类AbstractFactory, 然后再派生出ConcreteFactory, 这每一个ConcreteFactory的类里面都定义了各自创属于自己风格的Room和Wall的方法。
带入到Maze里面的代码如下
class MazeFactory {
public:
    MazeFactory();
    virtual Maze* MakeMaze() const;
    virtual Wall* MakeWall() const;
    virtual Room* MakeRoom(int n) const;
    virtual Door* MakeDoor(Room* r1, Room* r2) const;
}; //这个是基础的AbstractFactory,以后不同风格的ConcreteFactory都是继承此。
//AbstractFactory提供创建Maze类对象的方法,并且提供创建Wall,Room,Door的方法,然后让CreateMaze()来调用这个Factory中的方法创建Maze
Maze* MazeFactory::MakeMaze() const 
        { return new Maze; }
Wall* MazeFactory::MakeWall() const
        { return new Wall; }
Room* MazeFactory::MakeRoom(int n) const
        { return new Room(n); }
Door* MazeFactory::MakeDoor(Room* r1, Room* r2) const
        { return new Door(r1, r2); }
下面是实现了BomdedMazeFactory
class BombedMazeFactory : public MazeFactory {
public:
    BombedMazeFactory();
    virtual Wall* MakeWall() const;
    virtual Room* MakeRoom(int n) const;
};
Wall* BombedMazeFactory::MakeWall () const {
    return new BombedWall;
} 
Room* BombedMazeFactory::MakeRoom (int n) const {
    return new RoomWithABomb(n);
}注意这里虚函数的重写,没有把Abstract中的所有虚函数都重写,就重写了需要改变的Room 和 Wall

同理是EnchantedMazeFactory的实现
class EnchantedMazeFactory : public MazeFactory {
public:
    EnchantedMazeFactory();
    virtual Room* MakeRoom(int n)  const;
    virtual Door* MakeDoor(Room* r1, Room* r2) const;
protected:
    Spell* CastSpell() const;
};
Room* EnchantedMazeFactory::MakeRoom(int n)  const
        { return new EnchantedRoom(n, CastSpell()); }
Door* EnchantedMazeFactory::MakeDoor(Room* r1, Room* r2)  const
        { return new DoorNeedingSpell(r1, r2); }
把上面这个放到CreateMaze()中就是如下所示,注意我们完全没有必要粉case讨论,直接输入AbstractFactory基类类型就可以,
Maze* MazeGame::CreateMaze (MazeFactory& factory) {
    Maze* aMaze = factory.MakeMaze();
    Room* r1 = factory.MakeRoom(1);
    Room* r2 = factory.MakeRoom(2);
    Door* aDoor = factory.MakeDoor(r1, r2);

    aMaze->AddRoom(r1);
    aMaze->AddRoom(r2);

    r1->SetSide(North, factory.MakeWall());
    r1->SetSide(East, aDoor);
    r1->SetSide(South, factory.MakeWall());
    r1->SetSide(West, factory.MakeWall());
 
    r2->SetSide(North, factory.MakeWall());
    r2->SetSide(East, factory.MakeWall());
    r2->SetSide(South, factory.MakeWall());
    r2->SetSide(West, aDoor);
    return aMaze;
}
然后想要创建哪种风格的Maze,就输入相应的ConcreteFactory, 输入进去了就可以形成多态,CreateMaze()里面的代码就一行不用改了。创建代码如下
BombedMazeFactory  BFactory;
EnchantedMazeFactory EFactory;

Maze* pBMaze = MazeGame::CreateMaze(BFactory);
Maze* pEMaze = MazeGame::CreateMaze(EFactory);
所以,现在就知道了如何利用AbstractFactory来通过AbstractFactory派生到ConcreteFactroy的过程,就在这些类里面封装了我们所需要的变化,因此在CreateMaze里面就可以用单一的代码动态的展现我们的多种风格Maze的生成,这就是模式的精华。
这个里面还要注意的是,MazeFactory这个类里面不含纯虚函数,可以直接生成对象,所以他不是抽象类。
但是注意,我们这里一旦选定了factory的种类,CreateMaze的任务比如AddRoom(), SetSide()还是在CreateMaze()方法中显示完成的,就与接下来的Builder模式不一样了,因为Builder模式把上面Room的具体实现形式都封装到了Builder对象中了。

2.Builder模式

迷宫Builder的基类
class MazeBuilder {
public:
    virtual void BuildMaze() { }
    virtual void BuildRoom(int room) { }
    virtual void BuildDoor(int roomFrom, int roomTo) { }

    virtual Maze* GetMaze() { return 0; }
protected:
    MazeBuilder();
};
MazeGame中CreateMaze()方法重新定义如下
Maze* MazeGame::CreateMaze (MazeBuilder& builder)
 {
    builder.BuildMaze();

    builder.BuildRoom(1);
    builder.BuildRoom(2);
    builder.BuildDoor(1, 2);

    return builder.GetMaze();
}
对比与AbstractFactory模式,可以发现CreateMaze中所有的对Maze制造过程都只依赖builder对象,不需要对Maze对象直接操作,而是把对Maze对象的操作放到了Builder里面的方法中完成。当我们具体定义一个Builder的时候,通常要把基类的Builder继承,如派生一个StandardMazeBuilder
class StandardMazeBuilder : public MazeBuilder {
public:
    StandardMazeBuilder();
    
    virtual void BuildMaze();
    virtual void BuildRoom(int);
    virtual void BuildDoor(int, int);
    
     virtual Maze* GetMaze();
private:
    Direction CommonWall(Room*, Room*);
    Maze* _currentMaze;
};
下面看建造Maze的方法,本来是要调用Maze对象来完成,但是现在是被封装到了Builder对象中了
StandardMazeBuilder::StandardMazeBuilder () {
    _currentMaze = 0;
}
void StandardMazeBuilder::BuildMaze () {
    _currentMaze = new Maze;
}
Maze *StandardMazeBuilder::GetMaze () {
    Maze* maze =  _currentMaze;
    return maze;
}
void StandardMazeBuilder::BuildRoom (int n) {
    if (!_currentMaze->RoomNo(n)) {
        Room* room = new Room(n);
        _currentMaze->AddRoom(room);
        
        room->SetSide(North, new Wall);
        room->SetSide(South, new Wall);
        room->SetSide(East, new Wall);
        room->SetSide(West, new Wall);
    }
}
void StandardMazeBuilder::BuildDoor (int n1, int n2) {
    Room* r1 = _currentMaze->RoomNo(n1);
    Room* r2 = _currentMaze->RoomNo(n2);
    Door* d = new Door(r1, r2);
    
    r1->SetSide(CommonWall(r1,r2), d);
    r2->SetSide(CommonWall(r2,r1), d);
}
这样一来,在CreateMaze()方法中代码就更简单了,这样完全将Maze的构建和Maze对象可以隔离开来,单纯用builder对象就可以完成了。同理,对于不同风格的Maze的创建,需要继承不同种类的Builder. 而且,builder的基类也不是抽象类,因为里面如果有纯虚函数一定要被实现,而实际中builder的派生出来的conceate builder中差别比较大,不一定每个都会用到基类中的纯虚函数, 所以就没必要采用抽象类来作为基类。
最终,采用下面代码来实现一个maze对象
void Test1() {
Maze* maze;
MazeGame game;
StandardMazeBuilder builder;

game.CreateMaze(builder);
maze = builder.GetMaze();
}
最后总结一下AbstractFactroy和Builder的联系和区别:
1. 两个基类都不是抽象类
2. Builder比Abstract的封装更隐蔽













本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

程序设计模式(二)创建型模式 的相关文章

  • 如何在UML类图中描述包含的映射?

    我有一个 MyServer 类 其中包含一个 Map 其键是 MyClientType 对象 其值是 MyClient 对象 我想在类图中描述这种关系 但我找不到一种干净的方法来做到这一点 您可以使用合格的协会 1 MyServer Key
  • 关联的遍历方向

    我正在读这本书领域驱动设计 of 埃里克 埃文斯 第5章 关于协会 他降低模型复杂性的建议之一是为关联施加遍历方向 I quote 尽可能地限制关系很重要 A 双向关联意味着两个对象都可以被理解 只有在一起 当应用需求不需要遍历时 在两个方
  • 如何避免类图中的循环关系

    Hi I have a question about some circular relationships that I am facing with my database design I read a few more simila
  • BPMN的优点和缺点?

    我希望您能告诉我从开发人员的角度来看 BPMN 的优点和缺点是什么 我将 UML 与 BPMN 进行比较 发现 UML 有很多优点和缺点 但 BPMN 却没有 这很大程度上取决于观众和目的 在建模语言方面 BPMN 和 UML 活动图涵盖了
  • 活动图 定时事件

    我正在尝试建模以下内容 填写提交表单时 系统每 5 分钟自动保存一次用户进度 这是我尝试过的 但我认为这是不正确的 就我而言 仅在 填写提交 活动完成后才会询问条件 另外 我不想表明用户正在再次开始 填写提交 活动 您将使用由虚线框表示的可
  • 子集约束在 UML 类图中意味着什么

    有subset在部门类和人员类之间 但我不知道比是什么意思 家庭作业机会 查看由子集约束链接的两个关系 每个部门有多名成员 每个部门设经理一名 子集表示这两者之间的约束 如果后一个关系的元素是第一个关系的子集 那么你会如何描述其含义 EDI
  • 使用基于关联数组的 UML 建模类型

    假设有一个 PHP 子系统 它仅使用关联数组来存储某种结构化数据 因此 实际上在语言级别上没有机制来约束这些结构 但您希望在设计中使用 UML 来定义它们 E g 在 PHP 中 联系人可能是这样的关联数组 name gt John Doe
  • 我应该使用什么图来表示模块中功能之间的交互?

    我需要使用 UML 或 SysML 符号创建图表 我有由函数组成的模块 有些函数仅在模块 内部 使用 其他函数则由其他模块使用 Example MODULE 1有两个功能 func1 and func2 func2 uses func1 i
  • 用例图包括

    我有一个关于用例图的问题 如图所示 用户可以输入或更新他的姓名和问题 正如您所看到的 用户在第一次输入信息时需要输入姓名和问题 因此包括在内 但是 如果他希望更新他的信息 图表是否表明他必须修改名称和问题 因为它们包含在内 例如 如果他拼错
  • 类图转换为关系模型;继承和匹配表

    对于一个学校项目 我应该设计上学期项目的系统 我们使用 UML 创建一个极其简单的用例图 没有 lt
  • 传统的基于代码的工程和模型驱动的工程方法有什么区别?

    我在文献中找到了这段话 但我不明白它的含义 设计实践正在从传统的基于代码的工程转向 在整个开发生命周期中进行严格的分工 模型驱动的工程方法 其中所有人员都参与设计 流程可以对模型有发言权 任何帮助将不胜感激 基于代码的工程意味着大部分工作是
  • 如何在 UML 活动图上显示异步操作

    我即将绘制 记录一些客户端 服务器连接建立代码 以更好地理解它 有几个操作是在单独的线程中异步完成的 连接线程 数据接收线程等 我应该在单独的图表上显示它们吗 我更愿意将其放在单个图表上以掌握整体视图 但不知道如何在活动图上表示它 我不确定
  • 聚合、组合、关联、直接关联

    我正在复习面向对象编程方面的知识 在类之间的关系主题下 我遇到了一些对我来说有点模糊的关系 我知道依赖项 uses a 和继承 is a 但我对聚合 组合 关联和直接关联有点不熟悉 另外 其中哪一个是 has a 关系 有些可以与关联互换使
  • staruml 抽象类?

    有谁知道如何使用 StarUML 创建抽象类 我在文档中找不到任何提及 是否有某种反映抽象类的更一般的概念 从工具箱中选择类并将其添加到画布中 然后转到属性选择 isAbstract 复选框 然后类名显示为斜体
  • UML 聚合可以是双向的吗?

    我正在 Python 中实现一个具有 MVC 模式的应用程序 具有以下类定义 class Controller object def init self model view self model model self view view
  • 如何用UML表示通信协议?

    在我的 UML 模型中 我有一个系统及其相互通信的子组件 例如 我有一台计算机和一个遥控机器人 它们通过蓝牙进行通信 目前图中的流程类似于 计算机 触发 遥控车 的 setVelocity 函数 在这一点上 我想通过说以下的话来完善沟通 计
  • 多个参与者,相同的用例 [用例]

    我试图描述一个用例 其中系统内的多个参与者可以执行相同的活动 例如 假设 作为示例 我想要使用 更新客户端 用例 但几个已确定的参与者可以执行此操作 Manager Chief Executive Customer Service Repr
  • UML 设计类图:具有另一个类作为属性的类?

    我很难弄清楚如何将特定场景建模为 UML 设计类图 假设我有以下情况 我有一个名为 CPoint 的类 它有两个属性 x 和 y R2 平面中的坐标 另外 我有一个名为 CLine 的类 它应该有两个 CPoint 作为属性 这对代码来说非
  • 将 StarUML 图转换为 Visio 绘图

    我有一个使用 Star Uml 创建的类图 但现在的要求是将其移植到 MS Visio 是否可以将 uml 移植到具有可用 xml 信息的 vsd 提前致谢 导出为 Windows 图元文件 WMF 和增强型 Windows 图元文件 EM
  • UML 到 Java 代码生成工具 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi

随机推荐

  • idea数据库表生成实体类

    先说一下实现的功能 根据数据库 生成实体类 生成的实体类的格式可参看下图 生成的实体类中加入的注解有 Table注解 无getset方法 加入了lombok的 DATA Slf4j注解 支持 ID注解 当字段名为id时 如果数据库表中字段通
  • java比较string是否相同

    比较引用 equals 比较值 1 java中字符串的比较 我们经常习惯性的写上if str1 str2 这种写法在java中可能会带来问题 example1 String a abc String b abc 那么a b将返回true 因
  • 弹性盒子(flex布局)

    传统布局 兼容性好 布局繁琐 局限性 不能再移动端很好的布局 flex弹性布局 操作方便 布局极为简单 移动端应用广泛 PC端浏览器支持情况较差 布局原理 flex意为 弹性布局 用来为盒状模型提供最大的灵活性 任何一个容器都可以指定为fl
  • .net core 整洁架构入门

    Clean Architecture with NET Core Getting Started 使用 NET Core整洁架构 Clean Architecture 入门 Over the past two years I ve trav
  • 融合黄金正弦,十种混沌映射,搞定!把把最优值,本文思路可用于所有智能算法的改进...

    上一期的2023年最新优化算法之减法优化器算法 SABO 效果已经相当不错了 而且由于其十分简单的公式原理 更适用于刚接触智能优化算法的小伙伴 今天这篇文章为大家带来 融合黄金正弦的减法优化器 GSABO 本文会讲解一下改进思路 为各位小伙
  • 流程型和离散型制造行业mes系统的区别

    流程型和离散型制造行业mes系统的区别 在制造信息化的时代背景之下 MES系统 被越来越多的企业重视 虽然MES系统在制造行业中有着巨大的价值 但是不同的企业对MES系统的需求也存在很大的差异性 比较突出的就是流程型和离散型这两个方面 那么
  • 修改linux命令行终端的显示区行数列数

    stty rows 50 columns 200
  • Bugkuctf web 前女友

    老规矩 有新东西就记上一记 题目链接 这个有点儿意思 打开源码 好了 代码意思比较简单 重点看圈住的代码就好 满足v1v2的值不等但是md5相等 且v3 flag才行 好了 新知识来了 首先是md5 函数漏洞 第一种 GET a GET b
  • Web自动化Selenium-获取页面元素的相关信息

    获取页面元素的信息主要有两个目的 一是执行完步骤后进行断言 二是获取前一步骤的响应结果 将其作为后续步骤的输入或判断条件 获取元素的基本信息 目的输出元素的大小 文本 标签名 关键字 tag name 输出元素的标签名 size 输出元素的
  • TCP/IP协议,HTTP协议--面试必备

    经常面试被问到什么是http协议 什么是TCP协议 而且每次都弄不清楚 是时候记录一下了 一 什么是http协议 如果读者对计算机网络的体系结构比较了解的话应该清楚 IP协议位于网络层 TCP UDP协议位于传输层 HTTP位于应用层 如下
  • 本地存储基础

    本地存储特性 1 数据存储在用户浏览器中 2 设置 读取方便 甚至页面刷新不丢失数据 3 容量较大 sessionStorage约5M localStorage约20M 4 只能存储字符串 可以将对象JSON stringify 编码后存储
  • 如何使用Navicat修改mysql用户密码?

    https jingyan baidu com article 455a995054d490a167277858 html 下面介绍如何使用Navicat修改mysql用户密码的具体操作方法 工具 原料 Navicat Premium 方法
  • calico网络策略

    关于优先级order 为了与 Kubernetes 兼容 Calico 网络策略执行遵循 Kubernetes pod 的标准约定 如果没有网络策略适用于 Pod 则允许所有进出该 Pod 的流量 如果一个或多个网络策略应用于类型为 ing
  • TypeError: write() argument must be str, not bytes解决方法

    使用 HTMLTestRunner 输出测试报告 报错 TypeError write argument must be str not bytes 如下 原因 实例化HTMLTestRunner的runner runner HTMLTes
  • tmux插件管理器

    参考 tmux插件管理器tpm Tmux Plugin Manager 安装要求 tmux version 1 9以上 git bash 下载插件管理器 git clone https github com tmux plugins tpm
  • 2023Web自动化测试的技术框架和工具有哪些?

    Web 自动化测试是一种自动化测试方式 旨在模拟人工操作对 Web 应用程序进行测试 这种测试方式可以提高测试效率和测试精度 减少人工测试的工作量和测试成本 在 Web 自动化测试中 技术框架和工具起着至关重要的作用 本文将介绍几种常见的
  • linux实现复制文件的两种方法

    分享在linux系统下拷贝文件的两种方法 方法1 使用系统调用的read和write实现文件拷贝 include
  • 读书笔记: DetNet: Design Backbone for Object Detection

    读书笔记 DetNet Design Backbone for Object Detection 之前咱们不是读了adaptive convolution嘛 赶紧学以致用告诉我们为什么要用AC Abstract 背景 CNN已经被广泛应用于
  • android socket java_Android中Socket通信的实现方法概述

    本文实例简述了Android中Socket通信的实现方法 具体内容如下 一 socket通信概述 通俗的来说套接字 socket 是通信的基石 是支持TCP IP协议的网络通信的基本操作单元 它是网络通信过程中端点的抽象表示 包含进行网络通
  • 程序设计模式(二)创建型模式

    一 一个MAZE的构造过程 Maze的UML diagram如图所示 可以发现MapSite是斜体表示抽象类 有Room Door Wall三个子类继承 这三个就是用继承封装了MapSite的多向性 然而一个Maze 就是Room的聚合 所