安排这门课的最佳方法是什么?

时间:2012-12-03 18:47:12

标签: c++ oop class refactoring

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

我刚刚达到了这样一个程度,即我有五个紧密耦合的类,我不确定是否将整个事物保留原样或重构。

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

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

不幸的是,每个班级都与其他班级交织在一起。我想到了不同的方法,但我不确定其中哪一个是正确的。

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

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

2 个答案:

答案 0 :(得分:2)

只是一些想法。

当以面向对象的方式思考时,考虑现实生活中的对象会产生视图,你应该在程序中描述什么。现实生活中的对象是什么?它是一个“东西”,具有某些功能并暴露了一些功能。

例如,ShipTemplate是船舶的蓝图。它应该定义大小,布局,部分类型和数量(DieselEngine,SteamEngine,AntiAircraftGun,UnderwaterMines等),以及它们如何相互连接。

另一方的船舶是根据蓝图建造的 - 它应该包含所有实例。例如,它可能有两个DieselEngines和三个AntiAircraftGuns。这是正确的,船舶不继承蓝图。蓝图只是对船舶的描述,而不是它的父母。

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

所以这些是对象的一些属性。功能怎么样? OO设计的另一个重要概念是每个对象都具有(封装)一些可以用它完成的功能(并且它可能会或可能不会改变对象状态)。例如,ShipWeapon应该有方法Fire(),这可能会减少其中的弹药量。或者首先尝试使用Aim(sometarget)进行定位。另一方面,引擎会有Start(),Stop(),SetSpeed(速度)。请注意,这些将在对象及其状态的内部工作。船舶可能有SetCourse(方向,速度),它将以所需的动力启动它的发动机并定向其方向舵。船也可能有Colide(船)。并且命中(typeofattackinggun),它将遍历船的所有部分并随机损坏一些(并设置IsOperating用于枪,或关闭其中一个引擎等)。

正如您所看到的,您可以在设计OO方法时详细介绍。了解何时停止也非常好 - 您需要多少细节(或准确性)才能使您的计划工作。

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

计划的其他部分,即基础设施。如何管理数据对象(船舶,世界,玩家),他们如何相互了解和互动。例如,可以通过全局地图观察作为对象的每艘船,并且每艘船将通知它关于运动(观察者模式)。或者全球世界将基于全球时钟以某些时间间隔查询每艘船的状态。还是......

我想我想说的是坚持主要的OO原则 - 封装,继承,多态。并且有很多关于面向对象设计,设计模式等的文献,这是有用的。 Wiki entry有点“学术”,但有主要定义,让你思考:)同时看看SOLID

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

答案 1 :(得分:1)

现在您已经描述了想要表示各种数据的haw,在定义完整关系之前,请尝试通过定义“ protocols ”来完成描述:

每个班级能够为其他人做些什么?为实现目标,需要在方法之间采用哪些方法和规则?

一旦你确定了课程如何相互作用,你很可能会发现什么是私人候选人,什么是公共的,以及双方之间必须存在什么级别的友谊。

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

修改

跟随Svalorzen发表评论:

这取决于你正在观看的那一方。 事实上,这将引入多层次的“隐私”,允许在类本身的更广泛的单元上实现封装。这是好还是坏是一个背景问题,而不是成语。

不是只拥有私有(对于其他人)或公共(对任何人)的所有类,而是拥有一个“ capsule ”,即“俱乐部”(“俱乐部”)将“公共汽车”作为朋友“”和“俱乐部经理”的班级是真正的“向公众过滤”(因此是真正的OOP对象),允许某些方法需要与更多班级的私人部分进行交互时间,只在俱乐部里面做。

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

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

什么OOP关系(组合,继承,链接......)必须映射到C ++构造,与java不同,当语言哲学被定义为单向时,不是由语言本身定义的通过它的标准库。

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

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

使用OOP对象启动设计== C ++类习惯用法实际上削减了C ++增加程序设计的两个自由度,限制了你对十多年前C ++的思考。