![]() |
1
7
全部归功于阿特玛武器公司 http://www.xtremevbtalk.com 回答 in this thread 这两种情况的核心是我认为面向对象设计的基本规则:单一责任原则。有两种表达方式:
SRP是一个不可能总是被满足的理想,遵循这一原则是 坚硬的 .我倾向于追求“一个班级应该尽可能少的承担责任”,我们的大脑非常善于说服我们,一个非常复杂的单一班级比几个非常简单的班级更不复杂。最近,我已经开始尽我最大的努力编写较小的类,而且我的代码中的错误数量也大大减少了。在解雇它之前,先试试几个项目。 我首先建议,不要通过创建一个映射基类和三个子类来开始设计,而是从一个将每个映射的独特行为分隔为表示通用“映射行为”的二级类的设计开始。这个职位致力于证明这种方法的优越性。如果没有对您的代码相当熟悉的知识,我很难具体说明,但我将使用一个非常简单的地图概念:
地图类型
指示地图表示的三种地图类型中的哪一种。当您想更改映射类型时,可以调用
这就是你现在拥有的,你明智地认为这是个坏主意。既然我提到了SRP,我们来计算一下 地图 .
**从技术上讲,每个变量都是一种责任,但我已经将其简化了。*
对于最后的总计,如果添加第四个映射类型会发生什么?你必须增加
更多
状态变量(1+职责),更新
数量
新映射所需的更改数: 4 +
还有其他问题。很可能,一些映射类型将具有类似的状态信息,因此您将在状态之间共享变量。这使得
测试这个也不容易。假设你想为这个场景编写一个测试“当我创建一个‘繁殖怪物’地图时,地图应该每5秒生成一个新的怪物。”很容易讨论你如何测试这个:创建地图,设置它的类型,启动它,等待超过5秒的时间,然后检查敌人的数量。但是,我们的界面目前没有“敌人计数”属性。我们可以添加它,但是如果这是唯一一个有敌人计数的地图呢?如果我们添加属性,我们将拥有一个在2/3的情况下无效的属性。还不太清楚我们在测试“产卵怪物”地图时没有读取测试代码,因为所有测试都将测试
你当然可以
现在,我们的映射层次结构可能看起来像这样(为了简单起见,只定义了一个派生映射):
地图基类 0职责 映射派生类 -必须管理状态(1) -必须执行某些特定于类型的工作(1) 总计:2项职责 添加新地图 (同上)2职责 每类责任总数: 二 添加新映射类的成本: 二
这样好多了。我们的测试场景呢?我们的状态比较好,但仍然不是很好。我们可以在派生类上放置“许多敌人”属性,因为每个类是独立的,如果需要特定的信息,我们可以强制转换到特定的映射类型。不过,如果你有
当我们创建派生类时,我们做了什么?我们确定了类中每次都在变化的部分,并将其下推到子类中。如果我们创建了一个单独的
现在使用地图需要给它一个特定的
就是这样,一行新类的代码。想要一个硬性玩家生成地图吗?
那么,这与在派生类上具有属性有什么不同呢?从行为的角度来看,没有什么不同。从测试的角度来看,这是一个重大的突破。
在“一个带有类型参数的类”的情况下,如果3个行为有3个难度级别,并且每个行为有10个测试,那么您将编写90个测试(不包括测试以查看是否从每个行为转到另一个行为)。在“派生类”场景中,您将拥有9个需要10个测试的类:90个测试。在“行为类”场景中,您将为每个行为编写10个测试:30个测试。 责任统计如下: MAP有1个职责:跟踪一个行为。 行为有两个责任:维持状态和执行行动。 每类责任总数: 三 添加新映射类的成本: 0(重用行为)或2(新行为) 因此,我的观点是,“行为类”场景并不比“派生类”场景更难编写,但是它可以显著降低测试的负担。我读过类似的技术,多年来一直把它们当作“太麻烦”而不予理睬,直到最近才意识到它们的价值。这就是为什么我写了近10000个字符来解释和证明它。 |
![]() |
2
3
如果您的子类型是父类型的某种专门化,则应该对其进行子类化。换句话说,如果您只需要功能性,那么应该避免继承。作为 Liskov Substitution Principle 状态:“如果s是t的子类型,则程序中t类型的对象可以替换为s类型的对象,而不会更改该程序的任何期望属性。” |
![]() |
3
1
在您的例子中,我将使用混合方法(这可能称为组合,我不知道),其中映射模式变量实际上是一个单独的对象,它将所有相关数据/行为存储到映射模式中。这样,您可以拥有任意多的模式,而不必对map类做太多的实际操作。 Gutofb7把它钉在了头上,当你想把某个东西子类化的时候。举一个更具体的例子:在你的汽车类中,在你的程序中的任何地方,你所处理的是哪种类型的汽车,这有关系吗?现在,如果您对map进行子类化,您需要编写多少处理特定子类的代码? |
![]() |
4
1
在你与地图和产卵讨论的特定问题中,我认为这是一个你想要支持的案例。 composition over inheritance . 当你想到它时,它们并不是三种不同类型的地图。相反,它们是相同的地图,有三种不同的产卵策略。因此,如果可能的话,您应该使生成函数成为一个单独的类,并将生成类的实例作为映射的成员。如果您的地图“模式”中的所有其他差异在性质上都相似,那么您可能根本不必对地图进行子类划分,尽管可能需要对不同的组件进行子类划分(即,具有一个“生成策略”基类,并对由此生成的三种类型进行子类划分),或者至少为它们提供一个公共接口。 如果您认为每种类型的映射在概念上是不同的,那么我建议使用子类化,因为这似乎满足了Liskov的替换原则。然而,这并不是说你应该完全放弃写作。对于每种类型的映射都具有但可能具有不同行为/实现的那些属性,您应该考虑让基类将它们作为组件。这样,如果需要,您仍然可以混合和匹配功能,同时使用继承来保持关注点的分离。 |
![]() |
5
1
我不在C中编程,但在RubyonRails、Xcode和MooTools(javascript oop框架)中,同样的问题可能会被问到。 我不喜欢有一个方法,当某个特定的、永久的、属性是错误的时候,它永远不会被使用。就像如果它是大众的bug,某些齿轮将永远不会转动。那太愚蠢了。 如果我找到类似的方法,我会尝试将所有可以在我的不同“cars”之间共享的东西抽象成父类,并将方法和属性用于每种car,然后用它们的特定方法定义子类。 |
![]() |
Schadre · C-plus编码错误 2 年前 |
![]() |
Nithin K · 即使类属性的类型正确,也会获取异常 2 年前 |
![]() |
amirreza870 · Python OOP-更改类文本 2 年前 |
![]() |
A_K · 使用cat或打印方法打印部分内容的子集闭包 2 年前 |
![]() |
Mo Fatah · 如何使用Python类打印数独板? 3 年前 |