【问题标题】:Extend interface to an abstract class将接口扩展为抽象类
【发布时间】:2012-09-22 07:47:39
【问题描述】:

我有一个界面(移动)应该移动一些形状。

interface Move { move(); }
abstract class Shape : Move

class Circle : Shape
class Square : Shape
class Triangle : Shape

我的疑问是,我必须有一个可以移动 Shapes 的界面,但只能移动 Circle 和 Triangle,那么如何从 Square 中“删除”界面?我应该从 Shape 中删除接口并在 Circle 和 Triangle 上手动添加它吗?我对此有点困惑。希望有人能帮帮我。

【问题讨论】:

    标签: c# oop


    【解决方案1】:

    你应该像这样设置你的类:

    interface IMovable { move(); } 
    abstract class Shape : { } 
    
    class Circle : Shape, IMovable { } 
    class Square : Shape { } 
    class Triangle : Shape, IMovable { } 
    

    如果不是每个形状都可以移动,那么Shape 一定不能实现接口。另请注意,我将您的界面重命名为 IMovable,这不是什么大不了的事,但它更容易被接受并且命名约定更好。

    【讨论】:

    • 这个。 “我如何取消继承某些基本行为”这个问题的答案总是“一开始就不要继承它”。
    • @KeithS,这是正确的。停止继承只是查看继承的相反方式。因此,希望这将有助于 OP 以另一种方式向前发展。 :)
    • @smartcaveman,你是说将界面重命名为IMovable
    • @Mike,是的。因为它不是移动,所以它是一个可以移动的对象。它似乎更遵循惯例:例如IEnumerableIQueryableIObservableIEquatableIComparableIFormattable
    【解决方案2】:

    您不能从继承树中删除接口。

    您的模型似乎需要两个抽象类 - ShapeMovableShape

    interface IMove { move(); } 
    abstract class Shape : {} 
    abstract class MovableShape : IMove, Shape {} 
    
    class Circle : MovableShape{}
    class Square : Shape{}
    class Triangle : MovableShape{}
    

    【讨论】:

    • @NadirSampaoli,您的 意见 已得到适当注意,但要小心,因为这会变成非常主观的对话。需要注意的一点是,我也为这个答案 +1,因为它非常正确
    • @Mike 抱歉,事实上我的评论听起来很大胆;我应该附加一个“在我看来”。
    • +1 @Mike - 两个答案都是正确。哪个更适合OP?问题中没有足够的信息来确定答案。
    • @Mike - 谈到声誉,我真的不能发表太多评论,只能说它每天都与我无关。社区很重要,尝试培养它而不是将网站视为“竞争”是正确的。 (顺便说一句 - 有 Sportsmanship badge 来鼓励社区思考)。
    • @NadirSampaoli 关于 Mike 和 Oded 的讨论,我认为 Nadir 对 Mikes 的回复给出了一个合理的回答,而不是大发雷霆(我猜这经常发生)。 .
    【解决方案3】:

    您应该更熟悉接口、类和 OO 背后的一般概念。您要说的是以下内容:

    • 每个形状都可以移动。
    • 正方形是一种形状。
    • 但 Square 不能移动。

    显然这是没有意义的。所以你必须调整你的班级设计。要么每个形状都可以移动,Shape(和 Square)应该实现 Move,或者不是每个形状都可以移动,那么 Shape 不应该实现 Move。

    【讨论】:

      【解决方案4】:

      试试这个:

      interface IMove { move(); }
      abstract class Shape { }
      
      class Circle : Shape, IMove { }
      class Square : Shape { }
      class Triangle : Shape, IMove { }
      

      【讨论】:

        【解决方案5】:

        其他选项可能只是在Shape 类中实现IMove.Move 方法并默认抛出NotSupportedException

        public abstract class Shape : IMove 
        {
             public virtual void Move()
             { 
                 throw new NotSupportedException();
             }
        }
        

        因此,归根结底,“任何形状都可以是可移动的”,但“可移动的形状应该提供自己的移动实现方式”。

        最后,让我们假设有一堆形状以相同的方式移动。您将创建一个派生ShapeDefaultMovableShape 抽象类,它会覆盖Shape.Move 虚拟方法。

        public abstract class DefaultMovableShape 
        {
             public override void Move()
             {
                   // Do stuff
             }
        }
        

        【讨论】:

        • 虽然这可行,但我建议不要这样做,除非您的 Move 函数规范在接口层明确声明 Move 在不适用的情况下可能会抛出异常对于底层对象。当然,此时您应该质疑它为什么要实现该接口,并且您可能遇到了设计问题(请参阅已接受的答案)。
        • @Anthony 我同意了。我只是想给出一个选择。我相信有理由认为接受的答案是这样做的正确方法,但我的,在某些情况下,可能会完成这项工作。这取决于您对问题的看法:“形状不可移动”或“形状可能是可移动的”。
        • @Anthony 我已经仔细检查了您的评论。该声明应在Shape 类文档中,也应在IMove 中。 Shape “可以移动”,因为 Shape 可以提供“如何移动”的实现。因此,如果您尝试移动无法移动的 Shape,则此 Shape“不支持移动”。
        【解决方案6】:

        最佳答案取决于这些类的用例和环境。作为开发应用程序或框架的团队的一员,采用该团队使用的设计模式比寻求“完美”的解决方案更可取,因为这将使其他人更容易采用和维护您的代码。

        您希望如何使用和扩展这些类也很重要。您认为“Square”将来需要可移动吗? Shape 的可移动性总是静态的,还是作为动态属性更有用? Move() 对非 Shapes 的类有任何价值吗?如果可移动性作为动态属性可能有用,请考虑以下几点:

        public abstract class Shape
        {
             public bool isMovable()
             {
                 return false;
             }
        
             public virtual void Move()
             { 
                 if (!isMovable() {
                     throw new NotSupportedException();
                 } else {
                     throw new BadSubclassException();
                 }
             }
        }
        

        然后,您的子类可以覆盖 isMovable 以提供静态或动态行为,并且可以随着时间的推移进一步修改或子类化,只要您的文档明确说明 isMoveable 应始终在调用 Move 之前。默认行为应基于您希望使用您的代码的其他人的期望,基于他们如何实现相关设计模式。

        通过查看集合类的可变性在不同框架中的演变历史,可以找到做出这些决定所面临挑战的一个很好的例子。有些设计将可变类(集合、数组、字典等)作为基类,在子类中实现不变性,反之亦然。这两种方法以及动态方法都有有效的论据,但对于框架的用户来说,最重要的因素是一致性,,因为正确的东西确实是最容易使用的问题,提供安全性和性能不受影响。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-05-20
          • 2016-12-30
          • 1970-01-01
          • 1970-01-01
          • 2010-10-21
          • 1970-01-01
          • 2012-10-08
          • 1970-01-01
          相关资源
          最近更新 更多