添加新方法的子类

时间:2019-07-25 16:12:25

标签: design-patterns software-design

我正在编写一个Tracker类,该类将向客户端公开方法以获取用户训练的当前状态,例如距离,步速,卡路里等。将这个值想象为 getters >。

class Tracker{
  float getDistance();
  float getTime();
  float getCalories();
}

现在,想一想,我可能会发现一种也可以获取标高的方法,然后可能(而不是同时)获取步数,所以我的问题是如何更好地解决此设计问题。

首先想到的是经典继承

我的第一个想法就是将这个接口作为子类,所以我最终会得到

class ElevationTracker extends BaseTracker{

   float getElevation();

}

但是随后,我可能想添加一个StepTracker,扩展ElevationTracker,以便同时获得两个统计信息。

class StepTracker extends ElevationTracker{

   float getStepsCount();

}

这对我来说有点奇怪,因为StepTracker现在隐含地提供了海拔高度统计信息,并且可能是相反的,ElevationTracker扩展了{ {1}},在这种情况下,这只是我首先发现哪个功能的问题。

此外,我不确定此继承是否与专业化哲学

一致

一门万能的课程

另一种想法,也许是最简单的想法,是只拥有一个类StepTracker,并且每当我想添加新功能时,通过向其添加新方法来检索该功能的信息来更改该类;然后客户端可以更新其代码以使用此新功能。 例如,下个月,我更改了Tracker类,看起来像这样

Tracker

我认为这个解决方案就像是我之前(第一次创建class Tracker{ float getDistance(); float getTime(); float getCalories(); //This is new float getElevation(); } 类时想到这个 Elevation 功能)一样,我从一开始就添加了最后一个方法。

我之所以这样做,只是因为现在“要求已更改”

为每个功能都添加一个类

另一种想法是不要将每个功能都视为类的方法,而是将其单独视为类,这意味着我将拥有像Tracker这样的接口

Tracker

然后为每个功能都有一个类

  • interface Tracker { float getValue(); /** Maybe some other methods */ }
  • DistanceTracker
  • TimeTracker
  • ...

因此,我只需添加一个彼此独立的新CaloriesTrackerElevationTracker

这里的问题是,有些功能依赖于其他功能,例如StepTrackerPaceTracker可能依赖于CaloriesTracker,因此它们可能需要接收{ {1}}。

此外,客户端代码可能变得有些混乱,必须为每个功能保存一个实例。 我看到的最重要的陷阱是,我通常会一起使用所有这些跟踪器,我可能不会仅使用DistanceTracker或仅使用DistanceTracker,所以单独使用每个功能可能没有任何好处

结论

我想知道哪个选项是最好的,或者是否还有另一个更好的选项。也许我可以通过一些调整重新考虑其中之一,或者添加设计模式以对其进行改进。

在我看来,单个类选项可提供更快的开发速度,考虑到,尽管其他类使用OOP功能,但它们只是将更新已经从Tracker类编写的类的问题转移到了另一个客户或中产阶级。

1 个答案:

答案 0 :(得分:1)

正如您正确指出的,在这里继承没有用,因为跟踪器之间没有IS-A关系。如果一个跟踪器需要其他跟踪器来计算其值,则可以使用合成。根据SOLID设计原则的“单一职责”原则,一种类或方法应该做一件事。在这种情况下应用此原理,为每种跟踪器创建不同的类或接口是有意义的。为每种跟踪器和相应的实现类都提供接口可能会很好。因此,例如,可以创建诸如IDistanceTracker,IElevationTracker,IStepTracker等的接口及其实现,例如DistanceTrackerImpl,ElevationTrackerImpl等。

通过针对接口进行编码,我们保持了代码的灵活性,以在将来提供同一跟踪器的不同实现。例如,在客户端代码中,我们可以使用接口,而在服务代码中,我们可以潜在地使用同一接口的多个实现,并进行诸如对一组特定的客户端使用一种实现,而对其余客户端使用另一种实现的操作。在这种情况下,客户端代码不必更改。有时,假设我们要升级某些客户端以使用较新版本的实现-假设来说-可穿戴设备1.0硬件仅能够跟踪步骤,并假设我们根据这些步骤显示消耗的卡路里,然后根据卡路里跟踪器显示可以根据这些步骤计算卡路里。假设在较新的可穿戴硬件2.0中,该硬件还能够跟踪海拔高度,因为将需要修改较新硬件的卡路里跟踪器以使用海拔数据来计算卡路里。但是旧设备仍应使用以前的卡路里跟踪器,该跟踪器仅使用步长而不使用海拔。这导致需要具有同一跟踪器的多个实现,因此最好通过具有单个接口ICalorieTracker和相同的多个实现来针对该接口进行编码。

因此,恕我直言,为每个跟踪器具有独立的接口和实现而没有任何继承关系可能是一个好主意。如果存在一个满足IS-A关系的跟踪器,则一个跟踪器接口可以扩展另一个接口。例如,IAdvancedCalorieTracker扩展了ICalorieTracker。这里的子类型满足IS-A关系,因此可以使用继承。