C++学习笔记----3、设计专业的C++程序(八)---- 设计国际象棋程序

news2024/11/17 23:54:00

        今天我们就来介绍一个系统性的方法去设计一个C++程序,一个简单的国际象棋程序。为了提供完整的案例,有些步骤的概念目前还没有讲到。现在学习该案例来获得一一些人设计过程的整体印象,当你学习了那些概念后也可以再回头重新阅读本篇。

1、需求

        在开始程序设计之前,一定要对程序的功能与性能有一个清晰的需求。理想情况下,要有需求规格文档。国际象棋程序的需求应该包含以下几类规格,可能要更详细,类目更多:

  • 程序要支持国际象棋标准规则。
  • 程序要支持两个人玩。程序不提供人工智能计算机玩家。
  • 程序要支持字符界面:
    • 程序要给出字符界面的棋盘和棋子。
    • 玩家通过输入代表棋盘上的位置的数字来进行移动。

该需求确保按照用户期待的执行逻辑进行设计程序。

2、设计步骤

        要用系统性的方法来设计程序,从总体到细节。以下步骤并不适用于所有程序,但它提供了一个通用的指导。设计应该包括合适的图表。UML是做此类图表的工业标准。简单来讲,UML定义了标准图表的多维,可以用于将软件设计文档化,例如,类图,时序图等等。推荐使用UML或者至少在合适的地方使用类UML图表。然而,并不推荐完全按照UML的语法来进行,因为我们的目的是拥有一个清晰的、可以理解的图表,而这要比语法正确更重要。

3、将程序分解到子系统中

        第一步就是将程序分解为通用的功能子系统,给出子系统之间的接口与交互。做这一步时,还不需要过多考虑数据结构与算法,甚至类。只是要获得程序各个部分以及交互的通用感觉。可以使用表格列出子系统,表达子系统的高阶行为与功能,子系统与其他子系统之间爆露的接口,其他子系统消费使用的接口。

        国际象棋推荐的设计是在保存数据与显示数据之间通过使用MVC模式拥有一个清晰的界限。该模式将常用的应用数据用符号建模,对于数据的一个或多个视图,以及对数据的操作。在MVC中,数据被叫做模型,视图是对模型的可视化,控制器就是以事件的方式改变模型的代码。MVC的三个部分交互反馈循环如下:控制器处理动作,动作调整模型,改变视图。控制器也可以直接修改视图,例如UI的元素。如下图所示,使用该模式,不同部分被清晰地划分,允许在不影响其他部分的情况下进行修改。例如,在不影响数据模型或逻辑的情况下,你可以在使用字符界面和图形用户界面之间进行切换,或者是在运行在桌面电脑与手机的用户界面之间进行切换。

        下面的表格表示了国际象棋游戏可能的子系统的划分:

子系统名实例数功能对外暴露接口消费接口
玩游戏1

开始游戏

控制游戏流

控制落子

声名获胜者

结束游戏

游戏结束

下棋顺序(玩家)

落子(棋盘视图)

棋盘1

保存棋子

检测平局与将死

获得棋子

赋值棋子

游戏结束(玩游戏)
棋盘视图1画出关联的棋盘画(棋子视图)
棋子32

移动

检测合法移动

移动

检测移动

获得棋子(棋盘)

赋值棋子(棋盘)

棋子视图32画出关联的棋子
玩家2通过提示玩家下棋与玩家交互,获得玩家的下棋动作下棋

获得棋子(棋盘)

下棋(棋子)

检测下棋(棋子)

错误记录1将错误信息记录到文件中记录错误

        如表所示,国际象棋功能子系统包括:玩游戏子系统,棋盘与棋盘子系统,32个柜子与棋子视图,两个玩家,以及一个错误记录。当然了,这并不是唯一可行的方法。在软件设计中,程序设计本身,有许多不同的方法完成同样的目的,条条大路通罗马。各个解决方案也不相同;有些就比其他的好,但是,现实是有许多一样的有效的解决方案。

        好的对子系统的划分是将程序分成基本的功能部分。例如,玩家是区别于棋盘、棋子、或者玩游戏的子系统。将玩家放到玩游戏子系统中是没有道理的,因为它们是逻辑上分离的子系统。其他的选择可能并不是这么明显。

        在MVC设计中,棋盘与棋子子系统是模型部分。棋盘视图与棋子视图是视图部分,玩家是控制器部分。

        由于表格对子系统之间的关系很难可视化,在图中用线条来显示一个子系统调用另一个子系统来展示程序子系统通常是很有帮助的。下图基于UML的通信图可视化显示了国际象棋的子系统。

4、选择线程模型

        在设计阶段考虑怎么写算法中的多线程还是太早了。然而,在这一步,在程序中选择高阶线程的数量、给出交互。例如像UI线程、声音播放线程、网络通信线程等。

        在多线程设计中,要尽可能避免数据共享,因为这样的话设计就会更简单、更安全。如果不能避免数据共享的话,就要给出锁需求。

        如果你对多线程不熟悉或者你的平台不支持多线程,那就只能将程序设计成单线程的了。然而,如果你的程序有多个不同的任务,每个任务平行运行,那么多线程就是一个不错的选择。例如,图形用户界面应用经常使用一个线程执行主应用程序,另一个线程等待用户按键或者选择菜单执行。

        国际象棋程序只需要一个线程来控制游戏流。

5、给出子系统的类层次结构

        在这一步,要决定程序中的类层次结构了。国际象棋程序可以用一个类层次结构来表示棋子。如下图所示,ChessPiece类是抽象类,作为类的基础。ChessPieceView类也有类似的层次结构。

        另一个用于ChessBoardView类的类层次结构,使其可以用字符界面或者图形用户界面。下图显示了允许棋盘以字符控制台、2D或者3D的图形用户界面展示的层次结构示例。对于ChessPieceView层次结构类似。

6、给出每个子系统的类、数据结构、算法与模式

        在这一步,要考虑更高层次的细节,给出每个子系统的特定信息,包括要为每个子系统写的特定的类。可能会将每个子系统模型化为一个类。以下表格总结了相关信息。

子系统数据结构算法模式
玩游戏GamePlay class玩游戏对象包含一个棋盘对象和两个玩家对象每个玩家顺序下棋
棋盘ChessBoard class棋盘对象存储了一个二维8*8的网格,包含多达32个棋子每走一步检测是否得胜或平局
棋盘视图ChessBoardView抽象基础类
实体继承类
ChessBoardViewConsole,
ChessBoardViewGUI2D,
等等
保存如何画棋盘的信息画棋盘观察者
棋子ChessPiece 抽象基础类
Rook, Bishop, Knight,
King, Pawn, 以及 Queen
继承类
每个棋子保存其在棋盘上的位置通过查询棋盘上棋子的多个位置来检测是否为合法移动
棋子视图ChessPieceView 抽象基础类
继承类 RookView,
BishopView, 等等,
以及实体继承类 RookViewConsole,
RookViewGUI2D等等
保存如何画一个棋子的信息画一个棋子观察者
玩家Player 抽象基础类
实体继承类 PlayerConsole,
PlayerGUI2D等等
提示玩家下棋,检测是否为合法移动,然后移动棋子中间者
错误记录ErrorLogger class要记录的信息队列缓存信息并且将其写入Log文件使用依赖注入的策略

        上面的表格给出了软件设计中不同类的一些信息,但是没有描述它们之间的交互。UML时序图可用于模型化相关交互。下图是对上表的一些类的交互的可视化。

        上图所示仅是一个迭代,一个从GamePlay到Player的单个TakeTurn调用;因此,只是部分时序图。当TakeTurn调用结束时,GamePlay对象应该让ChessBoardView去画出自身,然后按次序让不同的ChessPieceView去画自身。还有,应该扩展时序图可视化一个棋子怎么吃掉对方的棋子,还要包含对于王车易位的支持,王车易位是指一个涉及到一个玩家的国王和任何一个玩家的车的移动。王车易位是唯一的一个玩家同时移动两个棋子的动作。

        这部分的设计文档一般都会提供每个类的真实接口,例子中省略了这个层次的细节。

        设计类与选择数据结构、算法、以及模式可能会比较麻烦。要时刻记住前面讨论过的抽象与重用的原则。对于抽象,关键的是将接口与实现分开。首先,从用户的角度给出接口。决定将部件去做什么。然后通过选择数据结构与算法决定让部件怎么做。对于重用,要对标准数据结构、算法以及模式做到烂熟于心,确保牢记C++中的标准库与可用的其他代码。

7、给出每个子系统的错误处理

        在这一步骤中,要勾画出每个子系统的错误处理。错误处理应该包含像网络访问错误这样的系统错误,以及像无效输入这样的用户错误。给出是否每个子系统都要使用异常。也可以将其列在表中。

子系统处理系统错误处理用户错误
玩游戏使用ErrorLogger记录错误,向用户显示信息,当发生不可预知的错误时优雅地将程序关闭不适用(没有直接的用户界面)
ChessBoard
ChessPiece
使用ErrorLogger记录错误,当发生不可预知的错误时抛出异常不适用(没有直接的用户界面)
ChessBoardView
ChessPieceView
使用ErrorLogger记录错误,画棋盘或棋子出错时抛出异常不适用(没有直接的用户界面)
玩家使用ErrorLogger记录错误,当发生不可预知的错误时抛出异常对用户的移动进行安全检查,确保落子在棋盘内;然后提示玩家下一步。子系统在移动棋子前检查每一步移动的合法性;如果不合法,提示用户重下。
ErrorLogger尝试记录一个错误;当未知错误发生时提示用户。不适用(没有直接的用户界面)

                错误处理的通用规则是处理所有错误。要考虑所有的错误情况。如果忘记了某种可能性,就会出现一个bug!不要把所有的错误都按“不可预知”的错误来处理。预知所有的可能性:内存申请失败,无效用户输入,磁盘失败,网络失败等等。然后就像国际象棋游戏表中显示的那样,对于不同的内部错误处理好用户错误。例如,如果用户移动无效,不应该使程序终止。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2052591.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《黑神话》主线时长约40小时 100%完成需90小时

《黑神话:悟空》即将正式发售,媒体评分也已解禁,M站均分为82分。在游戏发售之前,许多粉丝仍想了解关于该作的更多信息。游戏科学未确定《黑神话》游戏时长是多久,幸运的是有评测员透露其主线时长约为40小时&#xff0c…

亲测好用,吐血整理 ChatGPT 3.5/4.0 新手使用手册~ 【2024.08 更新】

废话不多说,直接分享正文~ 以下是小编为大家搜集到的最新的ChatGPT国内站,各有优缺点。 1、AI Plus(稳定使用) 推荐指数:⭐⭐⭐⭐⭐ yixiaai.com 该网站已经稳定运营了1年多了。2023年3月份第一批上线的网…

如何在Python中正确使用浅拷贝和深拷贝?

更多资料获取 📚 个人网站:ipengtao.com 在Python编程中,拷贝对象是一个常见的操作,尤其是在处理复杂数据结构时。Python提供了两种拷贝方式:浅拷贝(shallow copy)和深拷贝(deep co…

day35-四层负载

01.四层负载概念 02.四层实现对端口转发 1.克隆一台10.0.0.4 2.安装部署nginx服务 [rootlb:~]# scp 10.0.0.7:/etc/yum.repos.d/nginx.repo /etc/yum.repos.d/[rootlb:~]#yum -y install nginx3.配置nginx四层负载 [rootlb:~]#rm -rf /etc/nginx/conf.d/default.conf # 删除默认…

【重磅发布】2025华清远见新品发布会亮点、新品抢先看!

匠心服务 智启新程 大咖云集 • 行业分析 • 预见趋势 新品首发 • 课程升级 • 育人交流 - 2025华清远见新品发布会 将于2024年8月23日在北京隆重举行 诚邀您的到来! 大会背景 本次新品发布会以 “匠心服务 智启新程”为主题, 邀请多家业内知名…

顶刊中的水刊!中科院1区TOP和“平替”双打组合,3天初审,25天可录用!

高分“水刊” 前面小编解析了一本MDPI旗下能源电力类期刊《Energies》 (👉详情参考:MDPI旗下Energies“平替”:这本SCI又“水”又稳,不卡背景,25天录用吊打同行) 本期解析一本顶刊&#xff0c…

【Python零基础学习】字典

文章目录 前言一、简单字典示例二、使用字典三、字典遍历四、嵌套总结 前言 Python 字典 是一种非常强大且灵活的数据结构,它允许你通过键(key)来存储和检索值(value)。想象一下,字典就像一个巨大的电话簿…

免费分享一套SpringBoot+Vue员工管理(职工管理,考勤管理,奖惩管理,合同管理)管理系统【论文+源码+SQL脚本】,帅呆了~~

大家好,我是java1234_小锋老师,看到一个不错的SpringBootVue员工管理(职工管理,考勤管理,奖惩管理,合同管理)管理系统,分享下哈。 项目视频演示 【免费】SpringBootVue员工管理(职工管理,考勤…

Redis远程字典服务器(6) —— list类型详解

目录 一,基本情况 二,list常用命令 2.1 lpush,lrange 2.2 对于“下标越界”的思考 2.3 lpushx,rpush,rpushx 2.4 lpop,rpop 2.5 lindex,linsert,llen 2.6 lrem 2.7 ltrim…

基于B站的热门视频数据分析与情感分析【关联性、主题、情感分析】

目录 2 研究内容 2.1 主要研究内容 2.2 拟解决的关键问题 2.2.1热门视频特征的识别和提取 2.2.2情感分析与用户反馈 2.3技术路线 2.3.1数据收集 2.3.2数据预处理 2.3.3数据挖掘 2.3.4 数据可视化 2.4可行性分析 2.4.1技术可行性 2.4.2数据可行性 2.4.3经济可行性 2.5数据库设计…

vba发送邮件功能实现方法:如何调试测试?

vba发送邮件的配置步骤流程?vba发送邮件的安全指南? VBA是一种常用于自动化Office应用程序的编程语言。利用VBA发送邮件功能,用户可以实现自动化发送邮件的任务,无需手动操作。AokSend将详细探讨如何通过调试与测试来确保VBA发送…

ThreadLoad如何防止内存溢出

优质博文:IT-BLOG-CN 从 ThreadLocalMap看 ThreadLocal使用不当的内存泄漏问题 【1】基础概念 : 首先我们先看看ThreadLocalMap的类图,我们知道 ThreadLocal只是一个工具类,他为用户提供get、set、remove接口操作实际存放本地变…

MT7621+MT7915(MT7905)+MT7975 (W7621A6G-SDK)编译固件与升级固件方法

一、搭建开发环境,编译固件。 1、安装在Ubuntu 14.04.5 x86_64系统后,然后安装下面命令行。 $ sudo apt-get install git g make libncurses5-dev subversion libssl-dev gawk libxml-parser-perl unzip wget python xz-utils vim zlibc zlib1g zlib1g…

XSS---DOM破坏靶场复现

目录 一、OK,Boomer 一、网址: 二、源码分析: 三、 解决思路: 1.页面中的元素可以通过id和name直接取出来 2.覆盖 3.覆盖方法 四、ToString 五、setTimeout函数 六、使用框架白名单 七、成功绕过 ​编辑 二、案例分析…

才来鱼厂实习 1 个月,就转正了!

大家好,我是程序员鱼皮。昨天,我给才来我们公司 实习一个月 的前端开发同学转正了,直接发了正式 Offer!这个转正速度,放眼到所有公司中,我相信也是炸裂的。 看小伙子那么激动,让我回想到了 19 年…

认识 bufferbloat

很多人并不理解 bufferbloat 的本质,我引用《计算机网络-自顶向下方法(第 8 版)》第四章的一个解释: 很形象的比喻,buffer 就像盐,不可或缺,适量增味,过量食物就不能吃了。高血压患者更有所感受&#xff…

STM32是基于ARM架构的,那么ARM究竟是什么呢?

一、什么是ARM 首先,ARM是一家英国公司,全称Advanced RISC Machines,高级精简指令集机器,RISC意味着是精简指令集的芯片。同时也有复杂指令集CSIC的芯片,如X86,常以Intel和AMD为主。 其次,之后…

如何跳过极狐GitLab 密钥推送保护功能?

极狐GitLab 是 GitLab 在中国的发行版,专门面向中国程序员和企业提供企业级一体化 DevOps 平台,用来帮助用户实现需求管理、源代码托管、CI/CD、安全合规,而且所有的操作都是在一个平台上进行,省事省心省钱。可以一键安装极狐GitL…

vue 子组件全局自动导入,不在需要每个组件import

main.js import vue from vue import App from ./App.vue import axios from axios axios.defaults.baseURL HTTP://LOCALHOST:3000/api/ Vue.prototype.$http axios; vue config.productionTip false; //子组件全局自动导入 const requireComponents require.cont…

MK米客方德推出新一代工业级SD NAND——更长寿命、更高速度、更优功耗

博客目录 关注我,不迷路,共学习,同进步一、产品封装与兼容性二、SLC 存储颗粒的使用三、高性能 IOPS 表现四、智能健康监测功能五、内嵌 ECC 校验、坏块管理和其他保护机制六、低功耗设计总结 作为 SD NAND 领域的领跑者,MK 米客方…