面向对象设计不良的迹象有哪些?

时间:2008-12-06 01:27:48

标签: oop

在设计新系统或了解其他人的代码时,有哪些迹象表明在设计阶段出现了问题?是否有寻找类图和继承层次结构的线索,甚至是代码本身,只是为设计检修而尖叫,特别是在项目的早期?

16 个答案:

答案 0 :(得分:48)

对我来说最重要的事情是“code smells”。

大多数情况下,我对违反“良好做法”的事情很敏感。

类似的事情:

  • 除了您从名称中想到的内容之外的其他方法(例如:默认删除零字节文件的FileExists())

  • 一些非常长的方法(程序周围的对象包装符号)

  • 在同一个枚举成员上重复使用switch / case语句(需要提取的子类的符号)

  • 许多用于处理的成员变量,而不是捕获状态(可能表示需要提取方法对象)

  • 一个有很多责任的课程(违反单一可怜原则)

  • 成员访问的长链(这个很好,这个。其他很好,但my.very.long.chain.of.member.accesses.for.a.result很脆弱)< / p>

  • 课程命名不佳

  • 在狭小空间内使用过多设计模式

  • 工作太辛苦(重写框架中已存在的功能,或同一项目中的其他地方)

  • 拼写错误(任何地方)和语法(在评论中)或评论只是误导

答案 1 :(得分:18)

我会说OO设计糟糕的头号规则(是的,我已经犯过太多次了!)是:

  • 打破单一的类 责任原则(SRP )和 执行太多动作

其次是:

  • 过多的继承而不是 组成,即那些类 纯粹来自子类型 他们免费获得功能。 赞成组合而不是继承

答案 2 :(得分:16)

无法正确进行单元测试。

答案 3 :(得分:12)

Anti-patterns

软件设计反模式

  • 抽象反转:不暴露用户所需的实现功能,以便他们使用更高级别的功能重新实现它
  • 模棱两可的观点:在不指定其观点的情况下呈现模型(通常是OOAD)
  • 大泥球:一个没有可识别结构的系统
  • Blob:从面向对象的设计中推广上帝对象
  • 燃气工厂:一种不必要的复杂设计
  • 输入kludge:未能指定和实施可能无效输入的处理
  • 界面臃肿:使界面如此强大以至于实施起来非常困难
  • 魔术按钮:直接在接口代码中编码实现逻辑,而不使用抽象。
  • 种族危害:未能看到不同事件顺序的后果
  • 铁路解决方案:一种建议的解决方案虽然很差,但由于在设计的其他方面缺乏远见和不灵活性而是唯一可用的解决方案
  • 重新耦合:引入不必要的对象依赖
  • Stovepipe系统:几乎无法维护的不良相关组件组合
  • Staralised架构:包含用于规范化和使用数据集合的双用途表的数据库架构

面向对象设计反模式

  • 贫血领域模型:使用没有任何非OOP的业务逻辑的域模型,因为每个对象都应具有属性和行为
  • BaseBean:从实用程序类继承功能而不是委托给它
  • 调用super:要求子类调用超类的重写方法
  • 圆椭圆问题:基于value-subtypes
  • 对变量类型进行子类型化
  • 空子类失败:创建一个未通过“空子类测试”的类,其行为与从中派生的类不同而没有修改
  • 上帝对象:在设计的单个部分集中了太多的功能(类)
  • 对象污水池:重用状态不符合(可能是隐式)合同的对象重新使用
  • 对象狂欢:未能正确封装允许不受限制地访问其内部的对象
  • 恶作剧者:唯一目的是将信息传递给另一个物体的物品
  • 顺序耦合:需要按特定顺序调用其方法的类
  • 单胎炎:过度使用单身人士模式
  • 又一个无用的层:向程序,库或框架添加不必要的层。在第一本关于编程模式的书之后,这变得很流行。
  • 溜溜球问题:由于碎片过多而难以理解的结构(例如继承)

答案 4 :(得分:7)

这个问题假设面向对象意味着良好的设计。有些情况下,另一种方法更为合适。

答案 5 :(得分:5)

一种气味是具有硬依赖性/对其他对象的引用的对象,这些对象不是其自然对象层次结构或域相关组合的一部分。

示例:假设您有城市模拟。如果Person对象具有NearestPostOffice属性,则可能遇到麻烦。

答案 6 :(得分:3)

我讨厌看到的一件事是基类将自身向下转换为派生类。当你看到这个时,你知道你有问题。

其他例子可能是:

  • 过度使用switch语句
  • 覆盖所有内容的派生类

答案 7 :(得分:3)

在我看来,所有OOP代码都会在足够长的时间内退化为程序代码。

当然,如果你读了我最近的问题,你可能会理解为什么我有点厌倦了。

OOP的关键问题在于,您的对象构造图应该与您的调用图无关。

一旦解决了这个问题,OOP实际上就开始有意义了。问题是很少有团队知道这种设计模式。

答案 8 :(得分:2)

以下是一些:

  • 循环依赖
  • 您的基类属性XYZ未受保护/私有
  • 您希望您的语言支持多重继承

答案 9 :(得分:2)

在一个很长的方法中,用#region / #endregion包围的部分 - 在我看过的几乎每种情况下,代码都可以很容易地被提取到一个新方法中,或者需要以某种方式重构。

过于复杂的继承树,其中子类执行非常不同的事情,并且彼此之间只是切线相关。

违反DRY - 每个子类都以几乎完全相同的方式覆盖基本方法,只有很小的变化。一个例子:我最近研究过一些代码,其中每个子类都覆盖了一个基本方法,唯一的区别是类型测试(“x是ThisType”vs“x是ThatType”)。我在基础中实现了一个采用泛型类型T的方法,然后在测试中使用它。然后,每个孩子都可以调用基本实现,传递它想要测试的类型。这从8个不同的子类中删除了大约30行代码。

答案 10 :(得分:1)

重复代码=做同样事情的代码......我认为根据我的经验,这是OO设计中可能出现的最大错误。

答案 11 :(得分:1)

对象很好创造了大量的OO设计。

答案 12 :(得分:0)

让所有对象继承一些基本实用程序类,这样就可以调用实用程序方法而无需输入这么多代码。

答案 13 :(得分:0)

找一位有代码基础经验的程序员。让他们解释一些有效的方法。

如果他们说“此函数调用该函数”,则他们的代码是程序性的。

如果他们说“这个班级与该班级互动”,他们的代码就是OO。

答案 14 :(得分:0)

以下是糟糕设计的最突出特点:

  1. 刚性
  2. 脆弱性
  3. 不动
  4. 查看The Dependency Inversion Principle

答案 15 :(得分:0)

当你不只是一个Money \ Amount类而是一个TrainerPrice类,TablePrice类,AddTablePriceAction类等等。

IDE驱动开发或自动完成开发。结合极端严格的打字是一场完美的风暴。

在这里你可以看到很多可能是变量值成为类名和方法名以及一般类的无偿使用。你还会看到所有原语成为对象的东西。所有文字作为类。函数参数为类。然后到处转换方法。你还会看到类似包装另一个类的方法,将一个方法子集提供给另一个类,只包括目前所需的类。

这样就可以生成近乎无限量的代码,如果您有可计费小时数,那么这些代码非常棒。当变量,上下文,属性和状态被展开为超显式和过度特定的类时,这会产生指数大灾难,因为这些事物迟早会成倍增加。把它想象成[a,b] x [x,y]。尝试创建一个完整的流畅界面以及尽可能多地遵循设计模式,可以进一步加剧这一点。

OOP语言不像某些松散类型的语言那样具有多态性。松散类型的语言通常以浅层语法提供运行时多态性,静态分析无法处理。

在OOP中,您可能会看到难以自动检测的重复形式,可以使用地图将其转换为更动态的代码。虽然这些语言的动态性较差,但您可以通过一些额外的工作来实现动态功能。

这里的交易是您可以节省数千(或数百万)行代码,同时可能会丢失IDE功能和静态分析。性能可以是两种方式。运行时多态通常可以转换为生成的代码。但是在某些情况下,空间非常大,除了运行时多态之外的任何东西都是不可能的。

对于缺乏泛型的OOP语言以及OOP程序员尝试严格键入动态松散类型语言时,问题更为常见。

如果没有泛型,你应该有一个A代表X = [Q,W,E]和Y = [R,T,Y]你会看到[AQR,AQT,AQY,AWR,AWT,AWY,AER ,AET,AEY]。这通常是由于担心或使用无类型或将类型作为变量传递而失去IDE支持。

传统的松散类型语言是使用文本编辑器而不是IDE创建的,并且通过IDE支持而失去的优势通常以其他方式获得,例如组织和构造代码以使其可导航。

通常可以将IDE配置为理解您的动态代码(并链接到它),但很少能够方便地正确支持它。

提示:这里的上下文是OOP在PHP中出现了可怕的错误,人们使用简单的OOP Java编程传统上已经尝试将其应用于PHP,即使有一些OOP支持也是一种根本不同的语言类型。

针对您的平台进行设计,尝试将其转换为您习惯的平台,设计以满足IDE或其他工具的需求,设计以满足支持单元测试等需要响起警钟,因为它与设计相差很大工作软件来解决给定类别的问题或给定的功能集。