设计OOP层次结构时的问题

时间:2010-06-04 21:56:01

标签: oop inheritance hierarchy

创建继承层次结构时,我感到困惑。抽象基类应该只包含继承类的常用内容。但是在设计时不可能知道哪些是常用的方法,字段和属性。因此,我可以自由地将它们与分层次继承的抽象类分组。这就是我正在做的事情,而不关心我的真实课程将在哪个层次结构中,因为我会在综合层次结构中轻松找到一个位置。而且很有可能在层次结构的中间找到一些不必要的抽象类。

但我不知道这是正确的设计方式吗?在非完美的抽象层次结构中会出现哪些缺点。我必须给出明确的例子。想象棋和棋子。如果我必须定义pawn,queen,king等等。请看下面的UML图。

http://i.stack.imgur.com/1VC22.png

我在片段抽象课后将hierarcy分为2个滑动和非滑动片段,因为我认为滑动和非滑动将需要不同的常用成员。

但是我也可以将它们分开定向,因为有些碎片可以朝向8个方向,有些可以朝向4个方向,有些可以朝向2个或3个方向。将它们定向分组会产生一些新问题。他们将在哪个层次结构?他们会在滑动课后来吗?如果有可能在滑动和非滑动组下找到四个方向分组,我们知道不可能由2个类继承。所以对于这种情况我必须选择接口?如果方向永远不会在两者之下,那么就可以使用抽象类。那没关系,如果我再次找到新的公共分组,不需要继承2个类,所以我可以在方向下定义它。

在设计的最后,我的所有作品都可以在层次结构中找到完美的叶子节点,这对于将来拥有足够的综合性建筑非常有益,我不需要在层次结构中进行更改。

但是创建过大的综合层次结构会有什么不利之处呢?

可以在IDE的自动完成中显示许多不必要的和奇怪的命名抽象基类,这会混淆其他人吗?还有什么其他缺点?

3 个答案:

答案 0 :(得分:2)

方式,对任务而言过于复杂。你的OO太过分了。这意味着您必须使用代码填充20多个类。你应该真的简化这个。

考虑在属性(颜色,isUnderAttack)和行为(移动)方面考虑它。在这种情况下,您只需要一个类class ChessPiece,它具有颜色属性和isUnderAttack。然后,您可以使用某种形式的依赖注入。考虑一下这段代码的外观:

public class ChessPiece
{
    public enum ChessPieceColor{White, Black}
    private IChessMove behavior;
    public ChessPiece(IChessMove behavior, ChessPieceColor color)
    {
        this.behavior = behavior;
    }
    public void Move()
    {
        behavior.Move();
    }
}

public interface IChessMove
{
    void Move();
}

public class KnightMove : IChessMove
{
    public void Move()
    {
        // code to perform the moving.
    }
}

public class Program
{
    static void Main()
    {
        ChessPiece knight = new ChessPiece(new KnightMove(), ChessPiece.ChessPieceColor.White);
    }
}

然后,您只需为每种类型的作品编写不同的IChessMove代码。显然你需要在这里为这些方法添加更多信息才能使它真正起作用,但它应该向你显示你应该在这里使用的模式。当许多人有共同的行为时,每个可能作品的课程都会走得太远。

答案 1 :(得分:1)

好的......问题是,继承层次结构是脆弱的 - 它们很容易无用,例如在您描述为您的问题的情况下。您可以按照您的建议以多种方式设计层次结构,但请记住Liskov替换原则,最重要的是,您不应过度使用继承。仅在必要时使用它。您不应该仅仅因为可以使用抽象类和继承。我不擅长国际象棋,但不同颜色的棋子有不同的行为吗?创建继承层次结构时,有一个着名的抽象问题示例。拿一个圆圈和椭圆,这是更抽象的?当你试图让其中任何一个成为另一个的超类时,你最终会得到灵活的设计。尝试了解有关面向对象编程的更多信息,并尝试仅在没有其他选择更好的情况下继承。

答案 2 :(得分:1)

  
    

但是创造太大的全面的hieararchy会有什么不利之处呢?

  

拥有过于复杂的模型的缺点是它使代码更难以编写,读取和维护。

虽然发生了。第一枪很难设计出来。最好不要尝试。

这就是测试驱动开发的目的。而不是假设你需要什么,你使用测试来充实你的需求,这反过来充实了实现。 TDD ftw。