这是一个糟糕的oop设计吗?

时间:2009-02-12 18:17:50

标签: oop

想象一下,我有一个名为IVehicle的界面。

从这个界面,我推导出几种具体的类型,如公共汽车和汽车(都可以移动,减速,关闭发动机等)。我的界面没有字段。

有一个具有字段的类(例如车辆的最高速度)并使用每种具体类型的设计是不是很糟糕?这会是糟糕的设计吗?另外,如果我在这个类中存储状态(例如使用字段),那么它必须是静态的吗?

由于

12 个答案:

答案 0 :(得分:7)

你在谈论一个抽象类,不,它是不错的设计。这就是他们的目标(基本上是接口,但有一些基本的实现)。

国家不必静态存储。每次创建Car,Bus等时,都会有一个完整的类(以其中一个派生类的形式)实现。

答案 1 :(得分:7)

我会将TopSpeed作为属性添加到接口:

interface IVehicle
{
    void Move();
    void SlowDown();
    void SwitchOffEngine();

    Int32 TopSpeed { get; }
}

然后在具体实体中实现这样的接口:

class Car : IVehicle
{
    public void Move() { }
    public void SlowDown() { }
    public void SwitchOffEngine() { }

    public int TopSpeed
    {
        get { return 120; }
    }
}

class Bus : IVehicle
{
    public void Move() { }
    public void SlowDown() { }
    public void SwitchOffEngine() { }

    public int TopSpeed
    {
        get { return 98; }
    }
}

我认为这是最好的方法,不需要基类。

我认为这是最好的方法,因为TopSpeed是一个特定于实现的属性,在基类中没有任何意义。由于基类需要任何实现设置此值,我认为最好更改接口以包含它。

答案 2 :(得分:6)

在你的情况下,你没有打破OO -

IVehicle 定义“如果你是一辆车你应该做什么”

AbstractVehicle 定义“如果你是一辆车,这是你应该做的最低限度,这就是你要做的事情”

ConcreteVehicle 定义“我是一个车辆,这是我做事的方式,因为我是车辆,我应该做的事情”

您不需要将所有属性都设置为静态。最高速度或最高速度之类的东西可以保持静止,但你可以将它们保持为常量,即你的通话。

答案 3 :(得分:2)

只是为了澄清一些困惑......

界面不能包含字段。

但是,它可以具有属性。必须由实现接口的对象通过代码实现的属性。

除非您计划通过接口将对象的引用转换为具体类型,否则您应该将一些有意义的属性放入接口,否则您将无法访问它们。

换句话说,如果你有这个:

IVehicle vehicle = GetVehicle();
// want to get speed of vehicle?
Car car = (Car)vehicle;
Double speed = car.Speed;

应将公共属性放入公共接口。由于车辆是一个移动的物体,速度在那里是有意义的。

然而,引擎的数量对于飞机和某些船只是有意义的,但对于汽车则没有,并且这类信息可能必须位于某些更具体的界面中。

静态意味着实例之间共享,我怀疑这是个好主意。

然而,使用接口而不仅仅是类层次结构来从实际类型和身份中分离对象的访问和使用的整体计划是一个好主意,并且可以带来良好的设计。

最好的计划是选择对您的模型有意义的抽象级别并使用它。您可以非常轻松地过度设计此类解决方案。例如,您可以意识到某些车辆根本没有发动机,有些车辆可以在多个维度上移动(即,不仅是向前/向后,而是向上/向下,侧向等),并且选择水平适合你的模型是一个良好的开端。

例如,如果您要模拟一个县必须处理的运输问题,飞机是否可以向前/向后移动或者是否有垂直可旋转引擎是没有实际意义的,所以您可能不应该尝试设计成解决方案。

答案 4 :(得分:2)

我是第二个DannySmurf,抽象类非常有用。我经常在类库中使用hide-the-abstract-class-behind-an-interface模式,这样我就可以将所有实现类,甚至是抽象的父类作为内部类,也就是说,我不公开完全根据客户端实现。

答案 5 :(得分:2)

我认为你应该使用超类而不是接口。所以很可能IVehicle接口是冗余的,你应该只有一个名为Vehicle的抽象超类。 确实,每辆车都可以移动,因此可以测量其速度,您可以将速度作为一个场地添加到超类中。

此速度字段绝不是静态的,因为它将针对Vehicle的每个具体实例进行实例化;如果你有两辆公共汽车,它们可能有不同的速度,所以该字段不能是静态的,但必须特定于实例。

答案 6 :(得分:1)

  

有一个具有字段的类(例如车辆的最高速度)并使用每种具体类型的设计是不是很糟糕?这会是糟糕的设计吗?

不,设计不错。另一种方法是为每个字段提供一个抽象/纯虚拟属性(例如最高速度)...如果你有很多这些属性,并且属性很简单(例如只返回一个数字,实际上不是任何复杂的事情)然后你对包含专门属性值的类的想法似乎比拥有更多抽象/纯虚拟属性更简单。

  

另外,如果我在这个类中存储状态(例如使用字段),那么它必须是静态的吗?

如果它是静态的,那就没关系,if-and-only-如果它适用于该类的每个实例。例如,可以将“最高速度”定义为静态(所有总线具有相同的最高速度),但不是“当前速度”(在任何给定时刻实际以不同速度行驶的不同总线)。


为了清楚起见,我认为你提出的设计是这样的:

interface IVehicle
{
  void accelerate();
}

sealed class VehicleProperties
{
  const int topSpeed;
}

class VehicleImplementation : IVehicle
{
  //const data
  const VehicleProperties vehicleProperties;
  //non-const instance/state data
  int currentSpeed;
  //ctor
  VehicleImplementation(VehicleProperties vehicleProperties)
  {
    this.vehicleProperties = vehicleProperties;
  }
  //implement IVehicle methods
  void accelerate()
  {
    if (currentSpeed => vehicleProperties.topSpeed)
      return;
    ... else todo: accelerate ...
  }
}

class Car : VehicleImplementation
{
  //static properties associated with the Car type
  static VehicleProperties carProperties = new VehicleProperties(120);
  //ctor
  Car()
    : VehicleImplementation(carProperties)
  {}
  //IVehicle methods already implemented by
  //the VehicleImplementation base class
}

答案 7 :(得分:1)

如果“向接口添加字段”,它将停止作为接口并成为基类。这可能是坏事,也可能不是,取决于您的偏好。

使用接口和避免使用成员变量的基类的模式非常好。在将接口传递给系统的另一部分的情况下,它尤其有用。

但是没有特定的上下文,就没有绝对规则说“你必须使用一个接口作为一个基类”。在基类中使用私有成员变量可能是您正在寻找的功能的第一个近似值。使用派生类中定义的私有函数的另一个解决方案更加冗长,单凭可能使您的代码更难理解(但是,如果组织中的每个人都这样做,那就不会是一个问题)。

这取决于你想要完成的事情。它还取决于您的组织,应用程序或您使用的语言使用的标准(例如,如果您使用带有getter和setter的语言,您可以在以后更轻松地替换成员变量)。

成员变量是否为静态是一个单独的问题。你应该使一个成员静态,只有它在类的生命周期内不会改变。 “最高速度”听起来像是符合这个标准。有些语言,比如c ++,有“静态函数”的概念,其结果不会改变。我想你也可以使用它,但我发现这个增加的概念太令人困惑,不值得。

答案 8 :(得分:1)

  

有一个具有字段的类(例如最高速度)是不是很糟糕的设计   车辆)并使用每个   具体类型?这会不好   设计?

没有

恕我直言,C ++中继承的困难(我不太了解Java和其他语言的情况)有助于建立“非叶类应该是抽象的”传统(它实际上是“更有效”的项目C ++“作者Scott Meyers,如果我记得很清楚的话。但是,如果有的话,将字段和具体功能放在基类中也不错OO设计。

在某些语言中,遵循此策略可能会有所回报。在经典的OO及其旗帜语言(Eiffel)中,如果您的设计引导您选择具有具体,有用的基类,那么这就是最佳选择。有一些语言机制旨在解决因使用代码或多重继承而导致的任何潜在问题。

所以,OO设计也不错;这是肯定的。但是你应该考虑到,在某些语言中,你的自然观念可能传统上被避免,而不是更自然,但更实用的替代方案。

“你的设计引导你选择”我的意思是在你的程序中创建“车辆”的实例是有意义的。如果这是真的(例如,因为您正在设计一个可以使用'车辆'的视频游戏),那么您的设计将是正确的。如果你的软件试图解决这个问题就没有任何意义来创建一个车辆(例如,因为你正在管理一个停车位而没有任何被允许“只是一辆车” - 而是你需要有摩托车,汽车,面包车等等,当然你的设计应该是汽车的抽象。但对此没有普遍的答案。

  

另外,如果我在这里存储状态   类(例如使用字段),然后   它必须是静态的吗?

没有

答案 9 :(得分:0)

你是特定于语言的。例如,在C ++中,没有接口,但您可以使用抽象类,如接口。

答案 10 :(得分:0)

另一种方法,以及我如何区分常见场景的抽象类和接口,是:

- 界面中的所有自定义行为 -Common在基类中使用的字段,可以根据需要使用和设置。

  

我经常在类库中使用hide-the-abstract-class-behind-an-interface模式,这样我就可以把所有的实现类,甚至是抽象的父类作为内部类,也就是我做的不要将实现暴露给客户端。

有这方面的例子吗?

答案 11 :(得分:0)

是的,这是一个坏主意。你对IVehicle的定义是如此笼统,但你仍然做了一些假设。首先,max-speed属性可能没有相同的度量单位。 Car类型的孩子可以在法定里程中测量最大速度,而船只或飞机中的一个将使用海里。有关此类问题的更多信息,请参阅此Codewrights Tale post on object identity。我们的文化接受但从未充分研究过继承的微妙问题。如果你想了解这些细节,研究美国联邦航空局如何处理“飞机”的定义,以便能够以一定的理智来定义规则。