想要改进这个问题吗?通过编辑这篇帖子,更新问题以便能够用事实和引用回答。
Closed 4 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
原关闭原因未解决。
当设计一个新的系统或理解别人的代码时,有哪些明显的迹象表明在设计阶段出现问题?在类图和继承层次图中有何线索,甚至是在代码中,会强烈表明需要进行设计大修,特别是在项目早期?
想要改进这个问题吗?通过编辑这篇帖子,更新问题以便能够用事实和引用回答。
Closed 4 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
原关闭原因未解决。
当设计一个新的系统或理解别人的代码时,有哪些明显的迹象表明在设计阶段出现问题?在类图和继承层次图中有何线索,甚至是在代码中,会强烈表明需要进行设计大修,特别是在项目早期?
对我来说主要的问题是“代码异味”。
我大多数情况下对违反“良好实践”的事情很敏感。
事情像:
名称与功能不符的方法(例如,FileExists()会悄悄地删除零字节文件)。 Translated: 名称与功能不符的方法(例如,FileExists()会悄悄地删除零字节文件)。
一些极长的方法(对象包装过程的标志)
对同一个枚举成员重复使用switch / case语句(表明需要提取子类)。
许多成员变量被用于处理,而不是来捕获状态(这可能表明需要提取方法对象)。
一个有很多职责的类(违反单一职责原则)
长长的成员访问链(this.that还好,this.that.theOther还好,但是我.非常.长的.成员访问链.为了一个结果是脆弱的)
类名不恰当
在小空间中使用过多的设计模式
工作太努力(重新编写已经存在于框架中或同一项目中其他位置的函数)
拼写错误(任何地方)和语法错误(在评论中),或者仅仅是误导性的评论
我想说,糟糕的面向对象设计的头号规则(是的,我也犯了太多次!)就是:
跟随着:
无法进行正确的单元测试。
反模式
软件设计反模式
面向对象设计反模式
这个问题假设面向对象意味着良好的设计。但有些情况下,另一种方法更加合适。
一种气味是指具有强依赖性或参照其他不属于其自然对象层次结构或领域相关构成的对象。
例如:假设你有一个城市模拟器。如果一个人对象具有最近的邮局属性,那么你可能会有麻烦。
我最痛恨的一件事情是基类将自己向下转换为派生类。当你看到这种情况时,你就知道出了问题。
其他例子可能包括:
在我的看法中,所有的面向对象编程代码在足够长的时间跨度上会退化为过程化代码。
如果您读了我的最新问题,您可能会理解为什么我有点厌烦。
面向对象编程的主要问题在于它并没有明确表示你的对象构建图应该独立于你的调用图。
一旦您解决了那个问题,面向对象编程实际上就开始变得有意义了。问题在于很少有团队意识到这种设计模式。
这里有几个:
在一个长方法中,用 #region / #endregion 包围的部分 - 在我看来几乎每种情况下,那段代码都可以轻松地提取到一个新方法中,或者需要以某种方式进行重构。
过于复杂的继承树,其中子类做的事情非常不同,而且彼此关系也只是间接相关的。
DRY(不要重复自己原则)的违反 - 通过子类在几乎完全相同的方式下覆盖基本方法,仅有一个小差异。例如:我最近处理的某些代码中,子类每个都覆盖了一个基本方法,而唯一的区别是类型测试(“x为ThisType”与“x为ThatType”)。我在基类中实现了一个方法,该方法接受一个通用类型T,然后在测试中使用它。然后,每个子对象都可以调用基本实现,传递它想要测试的类型。这将从8个不同的子类中每个减少约30行代码。
重复的代码 = 做同样事情的代码...在我的经验中,这是面向对象设计中可能发生的最大错误。
物件是好的,但創造數不勝數的物件是一個糟糕的OO設計。
为了调用您的实用程序方法而不必键入太多代码,使所有对象继承某些基本实用程序类。
找一个有经验的程序员,向他们询问某个功能的工作原理。
如果他们说“这个函数调用那个函数”,他们的代码是过程化的。
如果他们说“这个类与那个类互动”,他们的代码是OO。
当你不仅拥有一个MoneyAmount类,而且还有一个TrainerPrice类、TablePrice类、AddTablePriceAction类等。
IDE驱动开发或自动完成开发。结合极为严格的类型检查,形成了完美的风暴。
这就是你看到的可能是很多什么可能是可变值变成类名和方法名以及一般类的过度使用。你也会看到所有原始类型变成对象的事情。所有的字面量都是类。函数参数作为类。然后到处都有转换方法。你还会看到类包装另一个类,向另一个类提供一个子集的方法,包括它目前只需要的方法。
这创造了产生近无限量的代码的可能性,如果您计费时数,那么这非常棒。当变量、上下文、属性和状态展开成超级明确和过于具体的类时,这将创造出指数级的灾难,因为那些东西迟早会成倍增加。就像[a,b]x[x,y]一样。这可以进一步加剧通过尝试创建完整的流畅界面以及恪守尽可能多的设计模式。
面向对象语言并不像一些松散类型的语言那样多态。松散类型的语言通常在浅层语法中提供运行时多态,这种多态是静态分析无法处理的。
在面向对象编程中,您可能会看到一些难以自动检测的重复形式,可以使用映射将其转换为更动态的代码。虽然这些语言不太动态,但您可以通过一些额外的工作实现动态功能。
这里的交易是你可以节省数千(甚至数百万)行代码,但可能会失去IDE特性和静态分析。性能可以两者皆有。运行时多态性通常可以转换为生成的代码。然而,在某些情况下,空间如此巨大,除了运行时多态性外任何方法都不可能。
当面向对象编程语言缺乏泛型时,问题变得更加普遍;当面向对象程序员试图在动态松散类型的语言中严格化类型时,问题也会更加明显。
如果没有泛型,当你应该有A for X = [Q, W, E] 和 Y = [R, T, Y] 的时候,你看到的是 [AQR, AQT, AQY, AWR, AWT, AWY, AER, AET, AEY]。这往往是由于害怕或使用无类型或将类型作为变量进行丢失 IDE 支持。
传统上,松散类型的语言通常是使用文本编辑器而非集成开发环境(IDE)制作的,而丧失的IDE支持优势通常会通过其他方式获得,如组织和结构化代码以便于导航。
经常情况下,集成开发环境可以被配置为理解你的动态代码(并链接至动态代码),但很少有IDE能够以方便的方式支持它。
提示:这里的背景是在PHP中遇到糟糕的OOP错误,使用简单的OOP Java编程的人一般试图将其应用到PHP中,即使PHP有一些OOP支持,但它仍然是一种基本上不同的语言。
针对你的平台设计以尝试将其转化为你所熟悉的平台,为IDE或其他工具提供设计,为支持单元测试提供设计等,所有这些都应引起警惕,因为这是一个显著的偏离,远离设计解决一类问题或一组特定功能的工作软件。