instanceof与多态

5

我在使用instanceof运算符时遇到了问题。我试图避免使用它。基本上,我有以下结构:

class Shape {}
class Triangle extends Shape {}
class Rectangle extends Shape {}

ShapeParser s;
while (s.hasNext())
     parseShape(s.next()); // returns a Shape object

void parseShape(Triangle t) { // do stuff } // KEY POINT HERE
void parseShape(Rectangle t) { // etc }

我的重点是:我想对函数进行参数重载,但它并没有按照我的意图工作(编译错误)。我试图避免以下情况:

void parseShape(Shape s)
{
     if (s instanceof Triangle) ...
}

更新:似乎共识是创建一个基类方法:parseShape()来进行提取。我想澄清我的问题:这个问题的动机与观察者模式有关。假设我有以下Observer对象负载方法:
    public void update(Observable obj, Shape objectPayload){} 
// note: the objectPayload is usually of type Object

不要执行:

public void update(Observable obj, Shape objectPayload)
{
       if (objectPayload instanceof Triangle)
          // do stuff
       else if (objectPayload instanceof Rectangle)
          // etc
}

我想要做的事情:

public void update(Observable obj, Shape objectPayload)
{
       parseShape(objectPayload);
}

    void parseShape(Triangle t) {  } // do stuff
    void parseShape(Rectangle t) { }

1
请发布错误信息... - phooji
你的矩形为什么叫t呢? :-) - paxdiablo
不,不,一百次不!别逼我过去给你一个耳光 :-) 在面向对象编程中,对象/类 是至高无上的。如果你发现自己又回到了以 代码 为主的时代,那么你正在倒退到面向对象之前的时代。即使是观察者模式也应该遵循这个规则:...通过调用它们的方法之一来自动通知它们任何状态的变化。 - paxdiablo
看起来您越来越想使用访问者模式。例如,可以查看此线程。将其放入您的形状层次结构中一次,您就不需要使用解析代码或任何其他类似操作的代码来污染该层次结构了。 - Ted Hopp
4个回答

4

如果您的parseShape()方法是在Shape中声明的,那么它可以在TriangleRectangle中被覆盖。

也就是说:

ShapeParser s;
while (s.hasNext())
     // Calls the proper implementation of parseShape()
     s.next().parseShape();

+1. 这是正确的面向对象编程方式。在代码审查中使用 instanceof 应该通常被视为一种不良行为 :-) - paxdiablo
你这种排序方式把世界颠倒过来,然后一切就会变得井然有序。在“涂热沥青、撒羽毛”的问题上给一个+1的投票。 - Chris

4
您可以将parseShape移到每个Shape类中。或者,您可以使用访问者模式。在此线程的解决方案中展示了一个反射的巧妙技巧,避免了Java中完整访问者模式的复杂性。
更新:
以下是访问者模式的步骤:
  1. 声明一个接口:
  2. public interface ShapeVisitor {  
        visit(Triangle);  
        visit(Rectangle);  
        // ...  
    }
    
  3. Shape中,声明一个抽象方法acceptVisitor
  4. class Shape {
        public abstract void acceptVisitor(ShapeVisitor visitor);
    }
    
  5. 在每个具体类中,实现acceptVisitor
  6. class Triangle extends Shape {
        public void acceptVisitor(ShapeVisitor visitor) {
            visitor.visit(this);
        }
    }
    
  7. 声明您的ParseVisitor类以实现ShapeVisitor并实现所有必需的方法(只需将每个parseShape方法重命名为visit)。
这样做的好处是,首先,它将解析代码从您的Shape层次结构中分离出来,并将其集中在一个单独的解析类中;其次,如果您稍后决定需要执行其他操作(比如,渲染),您可以应用相同的模式而无需更改任何Shape类。这种方法的主要缺点是,如果您决定添加另一个Shape子类,则必须更改实现ShapeVisitor的所有类。

1

嗯,向基类添加一个解析方法,然后只需循环迭代形状列表并调用s.parse()即可?


0
我猜错误是因为s.next()返回了Shape。逻辑上,最好的办法是给shape添加一个parse()方法,然后调用s.parse()。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接