2

前提

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

计划

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

我不打算接受一个答案,而是希望这些答案有助于最终收集或成为对问题的理性辩论。

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

基础

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

  • “好” - 这种技术将第一次起作用,并且是一个持久的解决方案。以后很容易更改,并且会很快支付其实施的时间投资。它可以在未来得到维护程序员的一致应用和轻松识别。总体而言,它有助于实现良好的功能并降低产品生命周期内的维护成本。

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

起动机

作为一个我认为好的贡献应该是什么样子的例子,我想提出一个“好的”原则:

关注点分离

[简短的介绍]

例子

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

目标

[解释这个原则可以防止什么问题]

适用性

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

例外

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

异议

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

4

3 回答 3

3

有一些很好理解的原则可能会形成一个很好的起点:

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

于 2008-12-14T18:33:21.547 回答
1

关注点分离

更喜欢聚合而不是 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的组件)。

于 2008-12-14T17:59:11.790 回答
1

我认为简短的回答是“好的”面向对象设计在变化下是健壮的,对于任何需求变化的代码破坏最少。如果您考虑所有通常的规则,它们都会得出相同的结论。

困难在于你无法在没有上下文的情况下评估设计的“好”;我相信,对于任何模块化来说,都会存在一个定理,要求的变化将最大限度地破坏,导致每个类在每个方法中都被触及。

如果你想严谨一点,你可以开发一个“变更案例”的集合,并按照概率顺序对它们进行排序,这样就可以最大限度地减少最高概率变更的破坏。

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

于 2008-12-14T18:32:51.393 回答