3

我正在做一个简单的项目来管理桌面游戏的数据,但我主要使用它来获得有关正确编码的经验。

我刚刚达到了一个点,即我有五个紧密耦合的类,我不确定是保持原样还是重构。

我所拥有的基本上是这样的:

  • class ShipTemplate:这个类(与 c++ 模板无关)具有所有常量成员,并包含有关 Ships 类别的基本信息。
  • class TemplateSet:此类包含当前可构建的所有 ShipTemplates,并且它有一个名称。它应该是独立的,因为它代表了每个玩家在任何时候都可以使用的技术,因此可以在不同的时间保存/加载不同的集合。
  • class Ship:这个类代表一艘完整的船,带有装载、名称和其他东西。它包含对 ShipTemplate 的 const 引用,该类不允许更改,以引用其基本功能。它可以扩展 ShipTemplate,但我想跟踪哪些 Ships 具有特定的底层 ShipTemplate,这样做似乎更容易。
  • class Fleet:这个类包含一个船舶列表,它有一个名称并包含其他信息。它应该包含一个成本变量,等于其中所有船舶成本的总和。
  • class Deployment:此类包含指向玩家可用的所有 Ships、Fleets 和 TemplateSet 的指针。它还需要跟踪不再可用的 ShipTemplates,但仍然由已建造的 Ships 使用。它应该包含一个成本变量,该变量等于玩家可用的所有船舶成本的总和。它必须管理船舶从一个舰队到另一个舰队的转移。它必须找出给定舰队中的哪些船舶,或者哪些船舶具有给定的 ShipTemplate。

不幸的是,每个班级都与其他班级紧密相连。我想过不同的方法,但我不确定其中一种是否正确。

  • 到处使用friend语句,这样如果一个类修改了某些内容,它就可以正确更新所有其他类。
  • 使用非常长的名称,例如Deployment::modifyShipThrustInFleet,并允许仅通过 Deployment 类进行任何修改,这将处理所有事情。
  • 删除 TemplateSets 和 Fleet 并在 Deployment 中表示它们,以便它可以正确更新成本值/指针,而不会破坏任何“正确性”规则。这也意味着对系统的每一次修改都必须通过部署。
  • 将指向上层类的指针插入到下层类中,例如,当更改 Ship 中的某些内容时,它可以自动更新其 Deployment 和 Fleet 的成本。

是否有其他一些我没有看到的解决方案,或者重构为更多类来帮助我实现可读、可维护的代码?

4

2 回答 2

2

只是一些想法。

当以面向对象的方式思考时,思考现实生活中的对象会布置视图,您应该在程序中描述什么。现实生活中的对象是什么?它是一种“事物”,具有某些特性并暴露了某些功能。

例如 ShipTemplate 是船的蓝图。它应该定义尺寸、布局、零件类型和数量(DieselEngine、SteamEngine、AntiAircraftGun、UnderwaterMines 等),以及它们如何相互连接。

另一艘船是根据蓝图建造的——它应该有所有的零件实例。例如,它可能有两个 DieselEngines 和三个 AntiAircraftGuns。没错,那艘船并没有继承蓝图。蓝图只是对船的描述,而不是它的父级。

现在,每种类型的对象(蓝图、零件、船)都有自己的属性和功能。例如,每台发动机都会消耗一定量的燃料,并且可以将船速提高到某个值。为什么没有引擎的基类,它具有这些特性?(遗产)。枪支也是如此(我们称之为 ShipWeapon)。地雷枪和高射炮当然有很大的区别,但它们都是枪,都可以装在船上,都有重量,弹药类型,装填时间,弹药容量,火炮是否在工作。

所以这些是对象的一些属性。功能呢?OO 设计的其他重要概念是每个对象都有(封装)一些可以用它完成的功能(它可能会或可能不会改变对象状态)。例如 ShipWeapon 应该有方法 Fire(),它可能应该规定其中的弹药量。或者首先尝试使用 Aim(sometarget) 来瞄准。另一方面,引擎将具有 Start()、Stop()、SetSpeed(speed)。请注意,这些将在对象内部及其状态上起作用。船可能有 SetCourse(direction, speed),它将以所需的功率启动它的引擎并定向它的方向舵。船也可能有 Colide(船)。还有 Hit(typeofattackinggun),它将遍历船的所有部分并随机损坏一些(并为枪设置 IsOperating,或关闭其中一个引擎等)

如您所见,在设计 OO 方法时,您可以深入了解很多细节。知道什么时候停止也很好——你的程序真正需要多少细节(或准确性)才能工作。

此外,可能有一个全球性的世界,可以容纳所有船只。等等..

还有程序的其他部分,基础设施。您如何管理数据对象(船舶、世界、玩家),它们如何相互了解和交互。例如,可以通过全局地图观察每艘船作为对象,并且每艘船都会通知它有关运动(观察者模式)。或者全球世界会根据全球时钟在一定的时间间隔内查询每艘船的状态。或者...

我想我想说的是坚持主要的 OO 原则——封装、继承、多态。并且有很多关于面向对象设计、设计模式等的文献,它们很有用。Wiki 条目有点“学术”,但有主要定义,这让您思考 :) 另请查看SOLID

PS。在一个类中做所有事情通常是糟糕设计的标志。

于 2012-12-03T20:31:50.343 回答
1

现在您已经描述了如何表示各种数据,在定义完整的关系之前,尝试通过定义“协议”来完成描述:

每个班级可以对其他班级做什么?实现你的目标需要哪些方法和方法之间的规则?

一旦你定义了类如何相互影响,你很可能会发现什么是私有的候选者,什么是公共的,以及各方之间必须存在什么级别的友谊。

可能不是你的情况,但是 - 通常 - 当存在复杂的关系时,一种可能的模式可以是使用“通信总线类”,它将可以“发送”到各种对象的操作公开,每个对象都有一个私有的接口并成为...总线本身的朋友(只有总线)。

编辑

在 Svalorzen 评论之后:

这取决于你在看它的那一面。实际上,这将引入多层次的“隐私”,允许在比类本身更广泛的单元上实现封装。这是好是坏是上下文的问题,而不是成语。

你有一个“胶囊”,它是一个“俱乐部”(“有‘公共汽车’作为朋友的班级俱乐部”)而“俱乐部经理”是真正的“公众过滤器”(因此也是真正的 OOP 对象),允许需要同时与更多类私有部分交互的某些方法仅在俱乐部内部进行。

否认“友谊”只不过是一种误解,将技术与工具混为一谈,使 OOP 对象与 C++ 类相同。那是-一般来说-一个FASLE成语。C++ 类可以是比 OOP 对象更小的单元(想想pimpl成语:那里的“对象”是什么?)。一个类可以成为另一个类的朋友这一事实并不能使它成为任何人的朋友,因此私有部分不会公开。您只是在定义另一个级别的隐私,其中封装与“私人”相同。它只适用于更广泛的群体。就 OOP 而言,“更广泛的群体”所扮演的角色与非朋友类所扮演的角色相同。

“朋友破坏封装”的误解与OOP的概念无关。它与在 Java 中实现 OOP 的方式有关,这是一种与 C++ 完全不同的语言。在 C++ 中,friendsip 只是一个“将 thimgs 组合在一起”的构造,就像类、结构、模板、继承、成员资格等一样。

什么 OOP 关系(组合、继承、链接......)必须映射到什么 C++ 构造,不像在 java 中,当语言哲学被定义为单向时,不是由语言本身和它的标准定义的图书馆。

映射“OOP 对象 = C++ 类”只是从过去继承下来的一种常见文化误解,当 C++ 没有模板、没有 lambda、没有友谊、无非是类(实际上被称为“C with classes”)时实现 OOP 层次结构的唯一方法是通过类,因为这是使用当时 c++ 构造创建层次关系的唯一方法。

现在,我什至可以使用 C++ 成员和“OOP 继承”的隐式转换和“OOP 成员资格”的 C++ 私有继承来实现 OOP 系统。或者我可以使用“类集群(或 mat be labdas)”实现一个 OOP 对象,定义它的运行时行为(想想 std::locale 和相关方面)。

使用 OOP 对象 == C++ 类习语开始设计实际上是减少了 C++ 为程序设计增加的两个自由度,将您的思维限制在十多年前的 C++ 上。

于 2012-12-03T19:44:50.953 回答