前提

我相信有一种方法可以客观地定义“好”和“坏”的面向对象设计技术,并且作为一个社区,我们可以确定这些技术是什么。这是一项学术练习。如果认真、坚定地去做,我相信这会给整个社会带来巨大的好处。社区将受益于拥有一个我们都可以指出的地方,“这项技术是‘好’或‘坏’,我们应该或不应该使用它,除非有特殊情况。”

计划

对于这项工作,我们应该关注面向对象的原则(而不是函数式、基于集合或其他类型的语言)。

我不打算接受一个答案,相反,我希望这些答案能够为最终的集合做出贡献,或者成为对问题的理性辩论。

我意识到这可能会引起争议,但我相信我们可以解决问题。大多数规则都有例外,我相信这就是分歧的根源。我们应该做出声明,然后注意相关的例外情况和异议者的反对意见。

基础

我想尝试一下定义“好”和“坏”:

  • “好”——这项技术第一次就能发挥作用,并且是一个持久的解决方案。以后更改很容易,并且很快就能收回实施的时间投入。将来可以一致应用并容易被维护程序员识别。总体而言,它有助于在产品的整个生命周期内实现良好的功能并降低维护成本。

  • “坏”——这种技术可能在短期内有效,但很快就会成为一种负担。立即很难改变或随着时间的推移变得更加困难。初始投资可能或小或大,但它很快就会成为不断增长的成本,最终成为沉没成本,必须不断消除或解决。它是主观应用的并且不一致,并且可能是令人惊讶的或将来不易被维护程序员识别。总体而言,它最终导致维护和/或操作产品的成本增加,并抑制或阻止对产品的更改。通过抑制或阻止变革,它不仅成为直接成本,而且成为机会成本和重大责任。

起动机

作为我认为好的贡献的一个例子,我想提出一个“好”原则:

关注点分离

[简短的介绍]

例子

[代码或其他类型的示例]

目标

[解释该原则可以防止哪些问题]

适用性

[为什么、在哪里、何时使用这个原则?]

例外情况

[什么时候我不会使用这个原则,或者它实际上在哪里可能有害?]

反对意见

[请注意此处社区的任何不同意见或反对意见]

有帮助吗?

解决方案

关注点分离

更喜欢聚合而不是 Mixin 式的继承

虽然可以通过从实用程序类继承来获得功能,但在许多情况下,所有功能都可以使用所述类的成员来获得。

示例(Boost.Noncopyable):

Boost.Noncopyable 是一个缺少复制构造函数或赋值运算符的 C++ 类。它可以用作基类,以防止子类被复制或赋值(这是常见的行为)。也可以作为直接会员使用

转换这个:

class Foo : private boost::noncopyable { ... };

对此:

class Foo {
    ...
private:
    boost::noncopyable noncopyable_;
};

示例(可锁定对象):

Java 引入了 synchronized 关键字作为一种习惯用法,允许以线程安全的方式使用任何对象。这可以反映在其他语言中,以便为任意对象提供互斥体。一个常见的例子是数据结构:

class ThreadsafeVector<T> : public Vector<T>, public Mutex { ... };

相反,这两个类可以聚合在一起。

struct ThreadsafeVector<T> {
    Vector<T> vector;
    Mutex mutex;
}

目标

继承作为一种代码重用机制经常被滥用。如果继承用于除 Is-A 关系之外的任何内容,则整体代码的清晰度会降低。

通过更深的链,mixin 基类大大增加了“死亡钻石”场景的可能性,其中子类最终继承了 mixin 类的多个副本。

适用性

任何支持多重继承的语言。

例外情况

mixin 类提供或需要重载成员的任何情况。在这种情况下,继承通常意味着 Is-Implemented-In-Terms-Of 关系,并且聚合是不够的。

反对意见

这种转变的结果可能会导致公共成员(例如 MyThreadSafeDataStructure 可能有一个可公开访问的 Mutex 作为一个组件)。

其他提示

有一些易于理解的原则可以作为一个很好的起点:

研究现有的设计模式以找到其背后的原则也是一个好主意,最重要的是(通常)更喜欢组合而不是继承。

我认为简短的答案是“好的”OO 设计在变化时是稳健的,任何需求变化时代码破坏最少。如果你考虑所有通常的规则,它们都会得出相同的结论。

困难在于,你无法在没有上下文的情况下评估设计的“优点”;我相信,一个定理是,对于任何模块化,都存在需求的变化,这将最大限度地破坏,导致每个方法中的每个类都受到影响。

如果您想做到严格,您可以开发一组“变更案例”并按概率顺序对它们进行排序,以便最大限度地减少最高概率变更的破坏。

但在大多数情况下,一些成熟的直觉会有很大帮助:特定于设备或特定于平台的事物往往会发生变化,业务规则和业务流程往往会发生变化,而算术等的实现则很少发生变化。(不像你想象的那样,永远不会。例如,考虑一个可能能够也可能不能利用平台支持的 BCD 算术的业务系统。)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top