每班可以容纳多少人?


29

我是一位长期的开发人员(我49岁),但是是面向对象开发的新手。自从Bertrand Meyer的Eiffel以来,我一直在阅读有关OO的文章,但是几乎没有做过OO编程。

关键是每本有关OO设计的书都以船,汽车或我们经常使用的任何常见对象为例开始,并且它们开始添加属性和方法,并说明如何建模对象状态以及如何使用对象它。

因此,它们通常会采用“模型越好,模型在应用程序中代表对象的能力越强,结果就越好”的想法。

到目前为止,到目前为止还不错,但是,我发现有几位作者给出了一些食谱,例如“一个类应该放在一个页面上”(我会添加“显示器尺寸是多少?”,因为我们尝试不这样做)打印代码!)。

以一个PurchaseOrder类为例,该类具有控制其行为的有限状态机和的集合PurchaseOrderItem,此处工作的一个论据是,我们应该使用一个PurchaseOrder简单的类,并带有一些方法(比数据类多一点),并且具有PurchaseOrderFSM处理的有限状态机的“专家类” PurchaseOrder

我想说的是Jeff Atwood 在《编码恐怖》上的Code Smells帖子的“特征嫉妒”或“不适当的亲密关系”分类。我将其称为常识。如果我可以签发,核准或取消我的真正的采购订单,那么PurchaseOrder类应该有issuePOapprovePO而且cancelPO方法。

这不是我理解为面向对象的基础的“最大化内聚力”和“最小化耦合”的古老原则吗?

此外,这是否有助于提高类的可维护性?


17
没有理由将类型名称编码为方法名称。也就是说,如果你有PurchaseOrder,你可能只是命名方法issueapprovecancel。该PO后缀仅增加了负值。
dash-tom-bang 2010年

2
只要您远离黑洞方法,就可以了。:)
Mark C

1
以当前的屏幕分辨率,我会说145个字符大。
DavRob60 2010年

感谢大家的评论,他们真的帮助了我解决这个问题。如果该站点允许,我也将选择TMN和lazarus的答案,但它只允许一个,因此转到了Craig。最好的祝福。
米格尔·韦洛索

Answers:


27

班级应使用“单一责任原则”。我见过的大多数非常大的类都对很多事情都做了,这就是为什么它们太大的原因。查看每个方法并确定代码是在此类中还是单独的重复代码是一个提示。您可能有一个issuePO方法,但是它包含10行数据访问代码吗?该代码可能不应该存在。


1
克雷格(Craig),我不了解“单一责任原则”,只是阅读了一下。我想到的第一件事是,责任的范围是什么?对于一个采购订单,我会说正在管理其状态,这就是issuePO,approvePO和cancelPO方法的原因,而且还包括与处理PO物料状态的PurchaseOrderItem类进行交互。因此,我不清楚这种方法是否与SRP一致。你打算说什么?
米格尔·韦洛索

1
+1是一个非常简洁的答案。实际上,对于一个班级应该有多大没有确切的衡量标准。运用SRP确保类是只大,因为它需要是。
罗宾斯

1
@MiguelVeloso-批准PO可能与发出PO有不同的逻辑和规则。如果是这样,将职责分为POApprover和POIssuer是有意义的。这个想法是,如果规则改变了一个,那么为什么要弄乱包含另一个规则的类。
马修·弗林

12

我认为,只要变量,函数和方法与所讨论的类相关,则类的大小并不重要。


1
另一方面,大尺寸表示这些方法可能与所讨论的类无关。
Billy ONeal,2010年

5
@Billy:我想说大类是代码的味道,但是它们并不会自动变坏。
David Thornley,2010年

@David:这就是为什么里面有“可能”一词的原因-这很重要:)
Billy ONeal 2010年

我不得不不同意...我正在一个具有相当一致的命名约定并且事物布局合理的项目中工作...但是我仍然有一个类,该类包含5万行代码。它太大了:)
Jay

2
这个答案准确地显示了“通向地狱的道路”的发展方式-如果不限制类的职责,那么将有许多变量,函数和方法与该类相关-这很容易导致它们太多
布朗

7

大类的最大问题是测试这些类或它们与其他类的交互。大班教学本身不是问题,但是像所有气味一样,它表明一个潜在的问题。一般而言,当类很大时,这表明该类正在做的事情比单个类要多。

对于您的汽车类比,如果class car太大,则可能表明它需要分为class engine,class door和class windshield,等等。


8
并且不要忘了Car应该实现Vehicle接口。:-)
克里斯

7

我认为没有一个太大的类的特定定义。但是,我将使用以下准则来确定是重构类还是重新定义类的范围:

  1. 是否有许多彼此独立的变量?(在大多数情况下,我的个人承受能力约为10〜20)
  2. 有很多针对极端情况的方法吗?(我的个人容忍度是……嗯,这在很大程度上取决于我的心情)

关于1,假设您在班级中定义了挡风玻璃尺寸,车轮尺寸,尾灯灯泡型号以及其他100个细节car。尽管它们都与汽车“相关”,但是很难追踪此类行为car。而且有一天,当您的同事意外地(或在某些情况下,有意地)根据尾灯灯泡模型更改了挡风玻璃的尺寸时,您将永远需要找出挡风玻璃为何不再适合您的汽车的原因。

关于2,假设您尝试定义汽车在类中可能具有的所有行为car,但car::open_roof()仅适用于敞篷车,car::open_rear_door()不适用于那些2门汽车。尽管敞篷车和2门汽车在定义上是“汽车”,但在类中实施它们会使类car变得复杂。该类变得更难使用和维护。

除了这两个准则,我建议对类范围进行明确定义。定义范围后,请严格根据定义实施类。大多数情况下,由于范围的定义不明确,或者由于添加到该类的任意功能,人们获得了“太大”的类


7

在300行中说出您的需求

注意:此经验法则假设您正在遵循“每个文件一个类”的方法,这本身就是一个好的可维护性构想

这不是绝对的规则,但是如果我在一类中看到200行以上的代码,我会感到怀疑。300条警铃响起。当我打开别人的代码并找到2000行时,我知道这是重构时间,甚至没有阅读第一页。

通常,这归结为可维护性。如果您的对象是如此复杂,以至于您无法在300行中表达其行为,那么其他人将很难理解。

我发现我在Model-> ViewModel-> View world中弯曲了这个规则,在其中我为UI页面创建“行为对象”,因为它通常需要300多行来描述行为的完整系列页面。但是,我仍然对这个编程模型还是陌生的,并且找到了在页面/视图模型之间重构/重用行为的好方法,这逐渐减小了我的ViewModels的大小。


我的个人指导原则也是大约300条线。+1
Marcie

我认为OP正在寻找一种启发式方法,这是一个很好的方法。
neontapir

谢谢,使用精确数字作为指导很有价值。
Will Sheppard 2015年

6

让我们回头看看:

这不是我理解为面向对象的基础的“最大化内聚力”和“最小化耦合”的古老原则吗?

实际上,它是编程的基石,而OO只是其中的一个范例。

我会让Antoine deSaint-Exupéry回答您的问题(因为我很懒;)):

达到完美,不是在没有其他可添加的东西时,而是在没有其他东西可取的时候。

班级越简单,您就对的信心就越大,对其进行全面测试就越容易。


3

我在Feature Envy分类中使用OP。拥有一堆带有在其他类的内部上运行的方法的类的类,没有什么比让我感到烦恼的了。OO建议您对数据以及对该数据的操作进行建模。这并不意味着您将操作与数据放在了不同的位置!它试图让您将数据和这些方法放在一起

carperson模型如此流行的情况下,这就好比将用于建立电气连接甚至启动器的代码放到person类中。我希望这对任何有经验的程序员都是明显错误的

我实际上并没有理想的类或方法大小,但是我更喜欢将我的方法和函数放在一个屏幕上,这样我就可以在一个地方看到它们的全部内容。(我将Vim配置为打开一个60行的窗口,该窗口恰好在打印页面上的行数附近。)在某些地方这没有多大意义,但对我有用。我喜欢遵循相同的类定义指南,并尝试将文件的长度保持在几个“页面”以下。超过300、400条线的任何内容都开始感到笨拙(主要是因为该班级开始承担太多直接责任)。


+1-良好的经验法则,但不能独断专行。但是,我要说的是,仅仅因为一个班级的破裂并不意味着不同的班级应该接触彼此的私人成员。
Billy ONeal 2010年

我不认为不同的阶层应该接触彼此的私处。虽然“朋友”访问权限对于启动和运行某些东西很方便,但是(对我而言)这是更好设计的垫脚石。通常,我也避免访问器,因为依赖于另一个类的内部是另一个Code Smell
dash-tom-bang 2010年


1

我完全同意在类中使用单一责任原则,但是如果遵循该原则并导致大类出现,那就这样吧。真正的重点应该是各个方法和属性不要太大,圈复杂度应作为此处的指南,随之而来的结果可能是许多私有方法将增加整体类的大小,但使其可读性和可测试性达到细粒度的水平。如果代码仍然可读,则大小无关紧要。


1

发出采购订单通常是一个复杂的过程,不仅涉及采购订单。在这种情况下,将业务逻辑放在单独的类中并简化采购订单可能是正确的选择。但是,总的来说,您是对的-您不想要“数据结构”类。例如,您的“ POManager”业务类的issue()方法可能应该调用PO的issue()方法。然后,PO可以将其状态设置为“已签发”并记下签发日期,然后POManager可以向应付帐款发送通知以使他们知道期望有发票,并向库存发送另一条通知以使他们知道有进货和预计日期。

任何为方法/类大小推送“规​​则”的人显然都没有在现实世界中花费足够的时间。正如其他人提到的那样,它通常是一个重构指标,但是有时(尤其是在“数据处理”类型的工作中),您确实确实需要用一个跨越500多行的方法编写一个类。不要挂在上面。


我猜想POManager将是MVC模式中的“控制器”,而PurchaseOrder将是“模型”,对吗?
米格尔·韦洛索

0

我发现有几位作者给出了一些食谱,例如“一个类应该放在一个页面中”

就一个类的实际大小而言,看到一个大类表示代码有气味,但不一定意味着总是有气味。(尽管通常如此)正如加勒比海盗所言,与实际规则相比,这更多是您所谓的“准则”。我曾经严格遵循“一堂课程中不超过100个LOC”的尝试,结果导致大量不必要的过度设计。

因此,它们通常会采用“模型越好,模型在应用程序中代表对象的能力越强,结果就越好”的想法。

我通常以此开始设计。该课程在现实世界中做什么?我的班级应如何按照要求说明该对象?我们在这里添加一些状态,在此处添加一个字段,在这些字段上使用的方法,再添加一些,瞧!我们有工人阶级。现在,使用适当的实现方式来填充签名,我们很高兴继续前进,或者您认为呢。现在,我们拥有了所需的所有功能的优秀大类。但这是一个问题,它没有考虑到现实世界的工作方式。我的意思是,它没有考虑“更改”。

最好将类拆分成较小的部分的原因是,以后很难更改大类而不影响现有逻辑或无意中引入一两个新的bug或使所有内容难以重用。这是重构,设计模式,SOLID等发挥作用的地方,最终结果通常是一个小类在其他更小,更细化的子类上工作。

同样,在您的示例中,将IssuePO,ApprovePO和Cancel PO添加到PurchaseOrder类中似乎不合逻辑。采购订单不会发出,批准或取消。如果PO应该具有方法,则这些方法应该在其状态下工作,而不是整个类。它们实际上只是其他类正在处理的数据/记录类。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.