【问题标题】:Is it a good design to have a class which is derived from a base class and also contains an instance of it?拥有一个从基类派生并包含它的实例的类是一个好的设计吗?
【发布时间】:2013-01-13 06:27:54
【问题描述】:

我正在设计一个基于生产者/消费者的应用程序,并且我坚持在类表示中表示生产者生成的任务。

实际问题是这样的:生产者可以产生一个StandaloneTask,它可以被消费者直接消费,或者它可以产生一个CompressTask,它必须先通过TaskDecompressor,然后先将其提取到一个消费者可以消费的StandaloneTask的数量。

由于StandaloneTaskCompressTask 之间有很多共同点,所以我创建了一个名为TaskBase 的基类,其中包含所有这些通用信息。

class abstract TaskBase 
{

}

class StandloneTaskType1: TaskBase
{

}

class StandloneTaskType2: TaskBase
{

}

.
.
.

class StandloneTaskTypeN: TaskBase
{

}

解压任务的工作原理是什么? 一个任务可以有一个或多个需要在运行时填写的参数。压缩任务由一个带有参数的任务和有关如何获取需要在这些参数中填充的值的其他信息组成。获取值后,TaskDecompressor 假设将所有这些值填充到参数化任务中以生成一个或多个独立任务。

我创建了一个CompressTask 类,如下所示。

class CompressTask: TaskBase
{
    TaskBase task;

    //runtime parameters
}

现在,CompressTask 派生自 TaskBase 并且它还包含 TaskBase 的实例,这对我来说看起来很奇怪。有这样的课是正确的吗?或者有没有更好的给定案例的类表示。

【问题讨论】:

  • 听起来像是装饰器模式。
  • 没关系。谷歌“复合模式”是一种众所周知的设计模式,它是派生类具有其基类型的集合的示例。

标签: c# oop class class-diagram


【解决方案1】:

总体而言,这种结构并不少见,您有点开始走Composite pattern 的道路。你的 CompressTask 有点像 Composite 而你的 StandaloneTask 有点像 Leaf。

我建议阅读该设计模式,并可能考虑让消费者更容易使用 TaskBase 的任何子类,无论它是 CompressTaskStandaloneTask。这将加强您的设计并简化使用。

【讨论】:

  • 实际上,我的应用程序是一个云应用程序,其中我有多个相同消费者的实例在不同的机器上运行。这就是为什么我想首先扩展任务,以便子任务平均分配给所有消费者。在这种情况下你有什么建议?您仍然希望有一个消耗整个 CompressTask 的消费者。
  • 这本身就是一个完整的讨论。分布式计算可能是一个复杂的系统,并且肯定会影响您的设计。你所描述的听起来像是Hadoop 或类似的东西可以帮助你。 Hadoop 确实以 Java 为重点,但它们有一个 C# 可能能够挂钩的流式 API。
  • 此外,当消费者运行时,您的 CompressTask(或任何复合任务)可以简单地要求您的云应用程序分发其包含的任务。如果您的消费者一次只能处理一项任务,您将在系统中为每个复合任务运行失去一个消费者节点,因此您需要为此做好计划。
【解决方案2】:

想象一下……

class Student: Person
{
    Person father;
    Person mother;
    Date dateOfEnrollment;
}

这很合理,不是吗?原则上,您的 CompressTask 类没有任何问题。

【讨论】:

    【解决方案3】:

    一个重要的 OO 设计规则:favor composition over implementation inheritance。仅仅因为StandaloneTaskCompressTask 有很多共同点,让它们共享同一个基类并不是一个好的选择。如果两个类共享一些接口,建议通过使用接口继承来分解接口。如果两个类共享一些实现,你最好将实现分解到某个类中,并将其嵌入到上述两个类中(即composition)。

    回到你的例子,CompressTask 派生自 TaskBase,它还包含一个 TaskBase 实例。这意味着您同时使用实现继承和组合,这听起来不太好。以下骨架仅供参考:

    interface Task
    {
        // some common Task interface here...
    }
    
    class TaskImpl
    // Or: class TaskImpl : Task // depends on your needs
    {    
        // some common Task-related implementation here...
    }
    
    class CompressTask: Task // interface inheritance, NOT implementation inheritance
    {
        TaskImpl taskImpl; // contains *reusable* task-related implementation
        Task task;  // contains the target task(s) to be compressed
        // other code...
    }
    
    class StandloneTaskType1: Task
    {
        TaskImpl taskImpl;
        // other code...
    }
    
    .
    .
    .
    
    class StandloneTaskTypeN: Task
    {
        TaskImpl taskImpl;
        // other code...
    }
    

    【讨论】:

      【解决方案4】:

      更好……想象一下……

      class Foo // implicitly extends Object
      {
          String name; // also a direct subclass of Object (at least in Java anyway)
          Integer age; // extends Number, which extends Object (at least in Javaland)
          Object theRootOfAllEvil; // a raw instance of the superclass
          int i; // the only member that is not an Object
      }
      

      请原谅我的爪哇语,但我来自哪里并没有错;)

      这一切都取决于你的课程的细节。

      【讨论】:

        猜你喜欢
        • 2021-11-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-25
        • 2023-03-19
        • 1970-01-01
        • 2011-01-26
        • 1970-01-01
        相关资源
        最近更新 更多