接口作为功能或接口作为类型

时间:2011-06-22 06:46:18

标签: oop language-agnostic interface

假设我有这样的要求:
系统中的对象都派生自一个名为IObject的基类,它可能包含带颜色的对象,带有转换的对象,以及两者。

现在有两种设计类层次结构的方法 第一个是:

  

让混凝土类派生自   IObject,还选择“功能”   接口作为其基类   表明它支持这种行为,   喜欢界面:IHasColor,
  IHasTransformation

第二个是:

  

组织基类,然后让   从一个派生出的具体类   他们:IObject,IColorObject,   ITransfromationObject,   IColorAndTransformationObject

我更喜欢第一个(它有一个正式名称吗?),因为它更灵活,正如你可以看到第二个可能有类组合爆炸问题,当有很多属性,如颜色,转换... < / p>

我想知道你的想法和建议。

感谢。

3 个答案:

答案 0 :(得分:10)

课程抽象出对象类型的真实概念。

接口抽象出行为或对象能力的真实概念

所以问题变成了,“颜色”是对象的属性还是对象的能力?

当您设计层次结构时,您将世界限制在更狭窄的空间。如果将颜色作为对象的属性,那么您将拥有两种对象,即具有颜色的对象和不具有颜色的对象。这适合你的“世界”吗?

如果你将它建模为一种能力(界面),那么你将拥有能够为世界提供颜色的物体,比如说,投射颜色。

对于转换,同样的逻辑适用。你可以将世界分成两种对象,即可以变换的对象和不能变换的对象,或者你可以将它视为一种能力,一个对象可能有能力将自己转化为另一种东西。

对我来说,从这个角度来看,有意义的是:

  • 颜色是对象的属性。事实上,每个物体都应该有一种颜色,即使它是透明的,即使它是反射的,即使它是“无”(祝你好运找出一个有颜色的物体=无物在现实世界中的意义,仍然可能在你的程序逻辑)。
  • 转换是一种能力,即一个界面,一个对象能够做的事情,其中包括对象可能会或可能无法做到的事情。

答案 1 :(得分:1)

我认为你直接跳到接口,跳过类。是否需要你的应用程序。有一个“IObject”接口?也许您的类层次结构的“CObject”根类可能会对您有所帮助。

它认为胜利者是排名第一的解决方案,你可能有一个“MyObject”,无论是接口的实现,还是直接的类。稍后,您可以根据需要在类层次结构中添加其他类或接口。

在看到几个应用程序(一些我的,一些其他的)之后,我认为应该有一个“我的自定义应用程序类层次结构根对象”或“我的自定义应用程序类层次结构根接口”设计模式。

答案 2 :(得分:1)

我正在我的项目中处理类层次结构,基本上我有类似你在问题中描述的情况。

假设我有基类型Object,它是我工具箱中所有其他类的绝对根。所以一切都是直接或通过它的子类来源。每个Object派生类都有一个共同的功能,但在某些叶子类中,效果与其他叶子类的差别不大。例如,每个对象都有大小和位置,可以使用属性或方法更改,如Position = new Point(10,10),Width = 15等。但是有些类应该忽略属性的设置或根据自己修改它内心的状态。考虑控制停靠在父窗口的左侧。您可以设置所有喜欢的高度属性,但通常会忽略它,因为此属性实际上取决于父容器控件的高度(或者它的ClientArea高度或类似的高度)。

因此,在您到达需要“自定义”行为的位置之前,让Object抽象类实现基本的通用功能是可以的。如果Object提供了在Height属性的setter中调用的受保护的虚拟SetHeight方法,则可以在DockedControl类中覆盖它,并且仅当对接为None时才允许更改高度,在其他情况下,您可以限制它或完全忽略。

所以我们很高兴,但现在我们要求对象在Click或Hover等鼠标事件上做出反应。所以我们从抽象的Object类派生MouseAwareObject并实现事件和东西。

现在客户端需要可停靠的鼠标感知对象。所以我们从DockableObject派生出来......嗯,现在呢?如果我们可以做多重继承我们可以做到但是我们遇到了重复界面模糊的钻石问题,我们需要处理它。我们可以在新类中使用两个Dockable和MouseAware类型,并对它们进行代理外部调用以提供功能。

最后想到的是制作IDockable和IMouseAware接口,让它们定义功能,并将它们自由添加到需要提供具体行为/实现的对象。

我想我会将我的基类分成几部分,并将我的Object留给非常有限的“核心”属性和方法集以及其他功能,这些功能实际上是对象作为一种类型可选的,但在具体情况下需要转移到接口像IResizable,IDockable,IMakeAWorldABetterPlaceAble等。使用这个解决方案,可以将行为“附加”到对象派生类,而无需从根基类一直到叶类的draggin虚拟或纯虚拟(抽象)方法。 / p>

在所有受影响的类中实现接口当然不方便,但您始终可以实现一些“适配器”并只转发对它们的调用。这样你就没有重复的实现(当然在某种程度上)并且在任务的实现之间脱钩(Resize可能对不同的类意味着不同的东西)和对客户端代码的期望。

对于你的问题,这可能不是理想的答案,但也许它会暗示你自己的解决方案。