【问题标题】:Good or Bad OOP? [closed]OOP 好还是坏? [关闭]
【发布时间】:2011-05-04 06:12:19
【问题描述】:

假设有一个带有构造函数的抽象类,该构造函数调用子类尚未实现的受保护抽象方法。这是个好主意还是坏主意?为什么?

【问题讨论】:

  • 是的。抽象类是个坏主意。
  • 抽象类并不是一个坏主意——只是能够从构造函数中调用虚方法。这个想法对具体的类也不好——不仅仅是抽象的;)
  • @Reed,不,抽象类是个坏主意(尽管有其他原因)。通常,委托/聚合是一种比继承更好、更不容易出错的代码重用机制。接口继承很好(即没有实现),但类继承是一个糟糕的主意。
  • 接口更好:)

标签: oop design-patterns architecture


【解决方案1】:

这是个坏主意。

您基本上是在构造函数中创建控制反转。在基类数据初始化之前调用基类中的方法(在大多数语言中),这也是危险的。它很容易导致不确定的行为。

请记住,在大多数语言中,当您构造一个类时,所有的基类构造都会首先运行。因此,如果您有类似:MyClass() : MyBaseClass() {},通常,MyBaseClass 的构造函数会完整运行,然后 MyClass 的构造函数会执行。但是,通过在基类中使用虚方法,您会在 MyClass 完全初始化之前调用实例方法 - 这可能非常危险。

【讨论】:

  • 好点。感谢您的洞察力。
【解决方案2】:

这是一个坏主意,因为在大多数 OOP 语言中,子级在父级初始化之后才被初始化。如果抽象函数在child中实现,那么它可能会在错误假设它已经初始化的情况下对child中的数据进行操作,而在parent构造中则不是这样。

例子:

 class Base {
     public:
         Base() { virtualInit(); }
         virtual ~Base() {}
     protected:
         virtual void virtualInit() {}
 };

 class Derived : public Base {
     public:
         Derived() : ptr_(new SomeObject) {}
         virtual ~Derived() {}
     protected:
         virtual void virtualInit() {
               // dereference ptr_
         }
     private:
         scoped_ptr<SomeObject> ptr_;
 };

在上面的例子中,Base::Base()Derived::Derived() 之前被执行,Derived::Derived() 负责初始化ptr_。因此,当从Base::Base() 调用virtualInit() 时,它会取消引用未初始化的指针,从而导致各种麻烦。这就是为什么 ctor 和 dtor 应该只调用非虚拟函数(在 C++ 中)、最终函数(在 Java 中)或特定于语言的等效函数。

【讨论】:

    【解决方案3】:

    我看不出你为什么要这样做的原因,更不用说限定它了。听起来你试图将一些功能注入到对象中。为什么不重载构造函数或创建一个可以设置的属性,以便您可以通过组合注入功能,甚至创建带有 IOC 对象参数的构造函数。

    正如另一个人已经发布的那样,它确实取决于您尝试解决特定问题的上下文。最自然的做法是坚持一个接口,开发一个抽象类,并在每个实现中重载构造函数。如果没有更多信息,我只能评论您问题中发布的内容。

    你的这个设计不能算是好坏。仅仅因为它可能是您实现目标的唯一途径。

    【讨论】:

    • 感谢您的意见。我实际上并没有试图解决一个特定的问题。只是针对可能的设计解决方案征求意见。
    • 接口如何更好?如果我需要一个通用类来继承所有类怎么办?普通类本身不应该直接实例化,而是包含了后代类需要的一些方法和属性?难道你不需要一个抽象类来从继承中受益吗?
    • 个人喜好,在我的程序设计中,我总是倾向于组合而不是继承。我发现解决方案更强大。我会使用 CRC 方法来查看问题,并比继承更多地解决交互问题。一个对象有责任,应该非常擅长。它也应该能够被另一个类似类型的替换。因此,我会首先关注这些行为和设计。并不是说我不会做继承,但这不是我从一开始就想到的事情,它通常在我重构时发生。
    【解决方案4】:

    这只是一个好主意,如果您可以设法避免自己的脚射击。但无论如何,OOP 总是容易出现糟糕的设计;因此,如果您的语言允许 OOP 以外的其他范例,那么在这种情况下使用 OOP 绝对是一个糟糕的选择。

    至少在 C++ 中, 子类定义了所有初始化的顺序;这是所有成员变量的初始化和所有父类的构造函数。这个必须考虑!

    或者,您可以给构造函数(作为参数)一个指向特定函数(我更喜欢静态函数)的指针,而不是调用抽象成员函数。这可能是一个更加优雅和理智的解决方案;这是(可能是所有)非 OOP 语言的常用解决方案。 (在java中:指向一个函数或函数列表的指针是接口的设计模式,反之亦然。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-05
      • 1970-01-01
      • 2011-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多