【问题标题】:The way to design structure of classes类结构的设计方法
【发布时间】:2019-04-29 07:15:21
【问题描述】:

我有非常简单的类结构。类 B 和 C 继承自 A。在某些函数和属性的情况下,功能相似。 B 类和 C 类对数据的处理不同,但类具有相同的输出(创建输出的函数在 A 类内部)。结构示例:

现在我需要扩展功能。我需要添加选项以在处理和数据输出(X 类)方面略有不同。此选项由配置文件管理,所以我想保留旧的下载、处理和输出数据的方式,例如:

  1. 选项 1 - 使用 旧方式处理和输出 下载没有线程的数据
  2. 选项 2 - 使用新的处理和输出方式下载没有线程的数据
  3. 选项 3 - 使用 旧方式处理和输出 使用 线程 下载数据
  4. 选项 4 - 使用新的处理和输出方式下载没有线程的数据

我不确定如何实现新处理和输出数据的组合。我需要 B 类和 X (BX) 类和 C 类和 X (CX) 类的组合。我考虑了这些选项:

  1. 最简单但最糟糕的方法 - 在 B 类和 A 类中复制一些函数。
  2. 保留 A 和 B 类并添加 BX 和 AX 类的组合。
  3. 重写类 A 和 B 仅用于下载数据。然后添加用于处理数据的类,然后添加用于输出数据的类。所有类将共享对象。看起来是最好的选择,但要做的工作最多。

有没有更好的选择(例如设计模式或类似的东西)如何以最干净的方式扩展类?

【问题讨论】:

  • 你考虑过使用接口吗?
  • 是的,想想抽象类(python 没有概念接口)。但是我仍然不确定如何为这种情况设计抽象类。还是会有非常冗余的代码,还是我想错了?

标签: python oop design-patterns uml


【解决方案1】:

在我看来你有一个Dataflow

您拥有的是获取(下载)数据 -> 处理数据 -> 输出数据。这基本上是一个包含三个阶段的管道。

如果您使用对象对此进行建模,则有两种类型的对象:执行操作的对象以及管理数据流和操作顺序的一种或多种对象。

让我们使用三个接口(你可以使用抽象类来定义它们没关系)来定义管道的步骤:IDataDownloaderIDataProcessorIDataOutputer

让我们添加另一个代表和管理管道的对象:

(注:我没有使用python的经验,所以我会用C#写对不起..)

public class Pipeline {

  private IDataDownloader mDataDownloader;
  private IDataProcessor mDataProcessor;
  private IDataOutputer mDataOutputer;

  public void Setup() {

    // use a config file to select between the two ways

    if (Config.UseOldWayDownloader) {
      mDataDownloader = new OldWayDownloader();
    }
    else {
      mDataDownloader = new NewWayDownloader();
    }
    // ....

    // or you can use service locator or dependecy injection that will get
    // a specific downloader based on configuration/setup

    mDataDownloader = Services.Get<IDataDownloader>();
    // ....
  }

  public void Execute() {

    var data = mDownloader.Download();

    var processedData = mDataProcessor.Process(data);

    mDataOutputer.Output(processedData);
  }
}

这样你就可以得到很好的separation of concerns 并得到一个可以扩展的模块化系统。该系统也是可组合的。您可以用不同的方式组合它的各个部分。

这似乎需要更多的工作,但事实可能并非如此。简洁的设计通常更容易编写和扩展。

确实,您可能会编写更多代码,但由于设计简洁,此代码可以编写得更快,并且可以节省调试时间。调试是我们编写软件的大部分时间,但它经常被认为只能通过编写和代码行来衡量的程序员所忽视。

松散类型语言就是一个例子。很多时候你写的代码更少,所以它更胖,但它很容易因为错误而出错。如果出现错误,您将获得更多(有时更难)调试,从而浪费时间。

如果您从一个简单的概念验证应用开始,它们将显着加快开发速度。一旦你达到了拥有一个经过测试的强大软件的地步,那么不同的力量就会发挥作用。

为了确保您的软件稳健,您必须编写更多测试和更多检查/断言,以确保一切顺利运行。所以最终你可能会拥有与强类型相同数量的代码为你做一些检查,你可以编写更少的测试。

那么有什么更好的呢?嗯...这取决于。

有几个因素会影响开发速度。这 用一门语言的程序员的品味和经验。应用程序也会影响这一点。一些应用程序在松散类型语言上更容易编写,而另一些应用程序在强类型语言上更容易编写。

速度可以不同,但​​并不总是这样,有时是一样的。

我认为在您的情况下,您将浪费更多时间尝试正确设置层次结构并对其进行调试,即使最终您的代码少了几行。从长远来看,如果你需要扩展一个更难理解的设计,它会大大减慢你的速度。

在管道方法中,添加新的下载、处理或输出数据的方法是添加单个类的问题。

【讨论】:

【解决方案2】:

好吧,为了使它成为 UML 答案,我添加了类图:

Pipeline 引用了这三个处理类。它们都是抽象的,因此您需要以不同的方式实现它们。而configuration 将根据配置(文件)所说的来分配它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-06
    • 2017-06-27
    • 2013-11-21
    • 2011-05-07
    • 1970-01-01
    相关资源
    最近更新 更多