如何避免大型 if 语句和 instanceof

发布于 2024-09-27 13:40:27 字数 1214 浏览 5 评论 0原文

Animal

public abstract class Animal {
 String name;

 public Animal(String name) {
  this.name = name;
 }

}

Lion

public class Lion extends Animal {

 public Lion(String name) {
  super(name);
  // TODO Auto-generated constructor stub
 }

 public void roar() {
  System.out.println("Roar");
 }
}

Deer

public class Deer extends Animal {

 public Deer(String name) {
  super(name);
 }

 public void runAway() {
  System.out.println("Running...");
 }

}

TestAnimals

public class TestAnimals {
 public static void main(String[] args) {
  Animal lion = new Lion("Geo");
  Animal deer1 = new Deer("D1");
  Animal deer2 = new Deer("D2");

  List<Animal> li = new ArrayList<Animal>();
  li.add(lion);
  li.add(deer1);
  li.add(deer2);
  for (Animal a : li) {
   if (a instanceof Lion) {
    Lion l = (Lion) a;
    l.roar();
   }
   if (a instanceof Deer) {
    Deer l = (Deer) a;
    l.runAway();
   }

  }
 }
}

有没有更好的方法无需强制转换即可迭代列表?在上面这种情况看起来没问题,但是如果你有很多基类的扩展,那么我们也需要那么多的 if 块。是否有设计模式或原则来解决这个问题?

Animal

public abstract class Animal {
 String name;

 public Animal(String name) {
  this.name = name;
 }

}

Lion

public class Lion extends Animal {

 public Lion(String name) {
  super(name);
  // TODO Auto-generated constructor stub
 }

 public void roar() {
  System.out.println("Roar");
 }
}

Deer

public class Deer extends Animal {

 public Deer(String name) {
  super(name);
 }

 public void runAway() {
  System.out.println("Running...");
 }

}

TestAnimals

public class TestAnimals {
 public static void main(String[] args) {
  Animal lion = new Lion("Geo");
  Animal deer1 = new Deer("D1");
  Animal deer2 = new Deer("D2");

  List<Animal> li = new ArrayList<Animal>();
  li.add(lion);
  li.add(deer1);
  li.add(deer2);
  for (Animal a : li) {
   if (a instanceof Lion) {
    Lion l = (Lion) a;
    l.roar();
   }
   if (a instanceof Deer) {
    Deer l = (Deer) a;
    l.runAway();
   }

  }
 }
}

Is there a better way to iterate through the list without having to cast ?In the above case it seem's ok but if you have many extensions of the base class then we'll need that many if block too.Is there a design pattern or principle to address this problem ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

没有伤那来痛 2024-10-04 13:40:28

一种避免 instanceof 的优雅方法,无需在基类中发明一些新的人工方法(使用非描述性名称,例如 performActiondoWhatYouAreSupposedToDo)是使用访问者模式。这是一个示例:

动物

import java.util.*;

abstract class Animal {
    String name;

    public Animal(String name) {
        this.name = name;
    }

    public abstract void accept(AnimalVisitor av);  // <-- Open up for visitors.

}

狮子鹿

class Lion extends Animal {
    public Lion(String name) {
        super(name);
    }
    public void roar() {
        System.out.println("Roar");
    }

    public void accept(AnimalVisitor av) {
        av.visit(this);                            // <-- Accept and call visit.
    }
}


class Deer extends Animal {

    public Deer(String name) {
        super(name);
    }

    public void runAway() {
        System.out.println("Running...");
    }

    public void accept(AnimalVisitor av) {
        av.visit(this);                            // <-- Accept and call visit.
    }

}

访客

interface AnimalVisitor {
    void visit(Lion l);
    void visit(Deer d);
}

class ActionVisitor implements AnimalVisitor {

    public void visit(Deer d) {
        d.runAway();
    }

    public void visit(Lion l) {
        l.roar();
    }
}

测试动物

public class TestAnimals {
    public static void main(String[] args) {
        Animal lion = new Lion("Geo");
        Animal deer1 = new Deer("D1");
        Animal deer2 = new Deer("D2");

        List<Animal> li = new ArrayList<Animal>();
        li.add(lion);
        li.add(deer1);
        li.add(deer2);
        for (Animal a : li)
            a.accept(new ActionVisitor());         // <-- Accept / visit.
    }
}

An elegant way of avoiding instanceof without inventing some new artificial method in the base class (with a non-descriptive name such as performAction or doWhatYouAreSupposedToDo) is to use the visitor pattern. Here is an example:

Animal

import java.util.*;

abstract class Animal {
    String name;

    public Animal(String name) {
        this.name = name;
    }

    public abstract void accept(AnimalVisitor av);  // <-- Open up for visitors.

}

Lion and Deer

class Lion extends Animal {
    public Lion(String name) {
        super(name);
    }
    public void roar() {
        System.out.println("Roar");
    }

    public void accept(AnimalVisitor av) {
        av.visit(this);                            // <-- Accept and call visit.
    }
}


class Deer extends Animal {

    public Deer(String name) {
        super(name);
    }

    public void runAway() {
        System.out.println("Running...");
    }

    public void accept(AnimalVisitor av) {
        av.visit(this);                            // <-- Accept and call visit.
    }

}

Visitor

interface AnimalVisitor {
    void visit(Lion l);
    void visit(Deer d);
}

class ActionVisitor implements AnimalVisitor {

    public void visit(Deer d) {
        d.runAway();
    }

    public void visit(Lion l) {
        l.roar();
    }
}

TestAnimals

public class TestAnimals {
    public static void main(String[] args) {
        Animal lion = new Lion("Geo");
        Animal deer1 = new Deer("D1");
        Animal deer2 = new Deer("D2");

        List<Animal> li = new ArrayList<Animal>();
        li.add(lion);
        li.add(deer1);
        li.add(deer2);
        for (Animal a : li)
            a.accept(new ActionVisitor());         // <-- Accept / visit.
    }
}
深者入戏 2024-10-04 13:40:28

动物

public abstract class Animal {
 String name;

 public Animal(String name) {
  this.name = name;
 }

 public abstract void exhibitNaturalBehaviour();

}

狮子

public class Lion extends Animal {

 public Lion(String name) {
  super(name);
 }

 public void exhibitNaturalBehaviour() {
  System.out.println("Roar");
 }
}

鹿

public class Deer extends Animal {

 public Deer(String name) {
  super(name);
 }

 public void exhibitNaturalBehaviour() {
  System.out.println("Running...");
 }

}

测试动物

public class TestAnimals {
 public static void main(String[] args) {

  Animal[] animalArr = {new Lion("Geo"), new Deer("D1"), new Deer("D2")};
  for (Animal a : animalArr) {
     a.exhibitNaturalBehaviour();    
  }

 }
}

Animal

public abstract class Animal {
 String name;

 public Animal(String name) {
  this.name = name;
 }

 public abstract void exhibitNaturalBehaviour();

}

Lion

public class Lion extends Animal {

 public Lion(String name) {
  super(name);
 }

 public void exhibitNaturalBehaviour() {
  System.out.println("Roar");
 }
}

Deer

public class Deer extends Animal {

 public Deer(String name) {
  super(name);
 }

 public void exhibitNaturalBehaviour() {
  System.out.println("Running...");
 }

}

TestAnimals

public class TestAnimals {
 public static void main(String[] args) {

  Animal[] animalArr = {new Lion("Geo"), new Deer("D1"), new Deer("D2")};
  for (Animal a : animalArr) {
     a.exhibitNaturalBehaviour();    
  }

 }
}
香草可樂 2024-10-04 13:40:28

是的,在抽象类中提供一个名为 action() 的方法,在两个子类中实现它,一个会咆哮另一个会逃跑

Yes provide a method called action() in abstract class , implement it in both of the child class, one will roar other will runaway

柳若烟 2024-10-04 13:40:28

语言中的模式匹配支持消除了对丑陋的访问者模式的需要。

例如,请参阅此 Scala 代码:

abstract class Animal(name: String)

class Lion(name: String) extends Animal(name) {
  def roar() {
    println("Roar!")
  }
}

class Deer(name: String) extends Animal(name) {
  def runAway() {
    println("Running!")
  }
}

object TestAnimals {
  def main(args: Array[String]) {
    val animals = List(new Lion("Geo"), new Deer("D1"), new Deer("D2"))
    for(animal <- animals) animal match {
      case l: Lion => l.roar()
      case d: Deer => d.runAway()
      case _       => ()
    }
  }
}

Pattern matching support in the language eliminates the need for the ugly visitor pattern.

See this Scala code for example:

abstract class Animal(name: String)

class Lion(name: String) extends Animal(name) {
  def roar() {
    println("Roar!")
  }
}

class Deer(name: String) extends Animal(name) {
  def runAway() {
    println("Running!")
  }
}

object TestAnimals {
  def main(args: Array[String]) {
    val animals = List(new Lion("Geo"), new Deer("D1"), new Deer("D2"))
    for(animal <- animals) animal match {
      case l: Lion => l.roar()
      case d: Deer => d.runAway()
      case _       => ()
    }
  }
}
渡你暖光 2024-10-04 13:40:28

如果你的方法不是多态的,你就不能没有强制转换。要使其具有多态性,请在基类中声明一个方法并在后代类中重写它。

If your method is not polymorphic you can't do without the cast. To make it polymorphic, declare a method in the base class and override it in the descendant classes.

柠檬心 2024-10-04 13:40:28

这里有一个动物List。通常,当您有一个对象列表时,所有这些对象必须能够在不进行强制转换的情况下执行相同的操作。

因此,最好的两个解决方案是:

  • 为两个具体类提供通用方法(在 Animal 中定义为 abstract
  • Lion分开>鹿 从一开始,就有两个不同的列表。

Here you have a List of animals. Usually when you have a list of Objects, all these objects must be able to do the same thing without being casted.

So the best two solutions are :

  • Having a common method for the two concrete classes (so defined as abstract in Animal)
  • Separate Lion from Deer from the start, and have two different lists.
我为君王 2024-10-04 13:40:28

事实证明,instanceof 比上面介绍的访问者模式更快;我认为这应该让我们产生疑问,当访问者模式用更多行代码更慢地执行相同的操作时,它真的比 instanceof 更优雅吗?

这是我的测试。我比较了 3 种方法:上面的访问者模式、instanceof 和 A​​nimal 中的显式类型字段。

操作系统:Windows 7 Enterprise SP1,64 位
处理器:Intel(R) Core(TM) i7 CPU 860 @ 2.80 GHz 2.93 GHz
内存:8.00 GB
JRE:1.7.0_21-b11,32位

import java.util.ArrayList;
import java.util.List;

public class AnimalTest1 {
    public static void main(String[] args) {
        Animal lion = new Lion("Geo");
        Animal deer1 = new Deer("D1");
        Animal deer2 = new Deer("D2");

        List<Animal> li = new ArrayList<Animal>();
        li.add(lion);
        li.add(deer1);
        li.add(deer2);

        int reps = 10000000;

        long start, elapsed;

        start = System.nanoTime();
        for (int i = 0; i < reps; i++) {
            for (Animal a : li)
                a.accept(new ActionVisitor()); // <-- Accept / visit.
        }
        elapsed = System.nanoTime() - start;

        System.out.println("Visitor took " + elapsed + " ns");

        start = System.nanoTime();
        for (int i = 0; i < reps; i++) {
            for (Animal a : li) {
                if (a instanceof Lion) {
                    ((Lion) a).roar();
                } else if (a instanceof Deer) {
                    ((Deer) a).runAway();
                }
            }
        }
        elapsed = System.nanoTime() - start;

        System.out.println("instanceof took " + elapsed + " ns");

        start = System.nanoTime();
        for (int i = 0; i < reps; i++) {
            for (Animal a : li) {
                switch (a.type) {
                case Animal.LION_TYPE:
                    ((Lion) a).roar();
                    break;
                case Animal.DEER_TYPE:
                    ((Deer) a).runAway();
                    break;
                }
            }
        }
        elapsed = System.nanoTime() - start;

        System.out.println("type constant took " + elapsed + " ns");
    }
}

abstract class Animal {
    public static final int LION_TYPE = 0;
    public static final int DEER_TYPE = 1;

    String name;
    public final int type;

    public Animal(String name, int type) {
        this.name = name;
        this.type = type;
    }

    public abstract void accept(AnimalVisitor av); // <-- Open up for visitors.
}

class Lion extends Animal {
    public Lion(String name) {
        super(name, LION_TYPE);
    }

    public void roar() {
        // System.out.println("Roar");
    }

    public void accept(AnimalVisitor av) {
        av.visit(this); // <-- Accept and call visit.
    }
}

class Deer extends Animal {

    public Deer(String name) {
        super(name, DEER_TYPE);
    }

    public void runAway() {
        // System.out.println("Running...");
    }

    public void accept(AnimalVisitor av) {
        av.visit(this); // <-- Accept and call visit.
    }

}

interface AnimalVisitor {
    void visit(Lion l);

    void visit(Deer d);
}

class ActionVisitor implements AnimalVisitor {

    public void visit(Deer d) {
        d.runAway();
    }

    public void visit(Lion l) {
        l.roar();
    }
}

测试结果:

访客花费了920842192 ns
instanceof 花费了 511837398 ns
typeconstant take 535296640 ns

此访问者模式引入了 2 个额外的方法调用,这对于 instanceof 来说是不必要的。这可能就是它速度较慢的原因。

并不是说性能是唯一的考虑因素,但请注意 2 个 instanceof 甚至比 2 个 case switch 语句还要快。很多人都担心instanceof的性能,但这应该可以打消担忧。

作为一名Java开发人员,当人们对避免使用instanceof抱有教条态度时,我感到沮丧,因为在我的工作中,我多次想使用instanceof来清理或编写新的干净代码,但同事/上级没有这样做不赞成这种做法,因为他们或多或少盲目地接受了永远不应该使用instanceof的想法。我感到沮丧,因为这一点常常是通过玩具示例来解释的,而这些示例并不能反映真正的业务问题。

每当您追求模块化软件设计时,总会有一些时候需要将依赖于类型的决策与相关类型隔离开来,以便类型具有尽可能少的依赖性。

这种访问者模式不会破坏模块化,但它并不是 instanceof 的更好替代方案。

It turns out that instanceof is faster than the visitor pattern presented above; I think this should make us question, is the visitor pattern really more elegant than instanceof when it's doing the same thing more slowly with more lines of code?

Here's my test. I compared 3 methods: the visitor pattern above, instanceof, and an explicit type field in Animal.

OS: Windows 7 Enterprise SP1, 64-bit
Processor: Intel(R) Core(TM) i7 CPU 860 @ 2.80 GHz 2.93 GHz
RAM: 8.00 GB
JRE: 1.7.0_21-b11, 32-bit

import java.util.ArrayList;
import java.util.List;

public class AnimalTest1 {
    public static void main(String[] args) {
        Animal lion = new Lion("Geo");
        Animal deer1 = new Deer("D1");
        Animal deer2 = new Deer("D2");

        List<Animal> li = new ArrayList<Animal>();
        li.add(lion);
        li.add(deer1);
        li.add(deer2);

        int reps = 10000000;

        long start, elapsed;

        start = System.nanoTime();
        for (int i = 0; i < reps; i++) {
            for (Animal a : li)
                a.accept(new ActionVisitor()); // <-- Accept / visit.
        }
        elapsed = System.nanoTime() - start;

        System.out.println("Visitor took " + elapsed + " ns");

        start = System.nanoTime();
        for (int i = 0; i < reps; i++) {
            for (Animal a : li) {
                if (a instanceof Lion) {
                    ((Lion) a).roar();
                } else if (a instanceof Deer) {
                    ((Deer) a).runAway();
                }
            }
        }
        elapsed = System.nanoTime() - start;

        System.out.println("instanceof took " + elapsed + " ns");

        start = System.nanoTime();
        for (int i = 0; i < reps; i++) {
            for (Animal a : li) {
                switch (a.type) {
                case Animal.LION_TYPE:
                    ((Lion) a).roar();
                    break;
                case Animal.DEER_TYPE:
                    ((Deer) a).runAway();
                    break;
                }
            }
        }
        elapsed = System.nanoTime() - start;

        System.out.println("type constant took " + elapsed + " ns");
    }
}

abstract class Animal {
    public static final int LION_TYPE = 0;
    public static final int DEER_TYPE = 1;

    String name;
    public final int type;

    public Animal(String name, int type) {
        this.name = name;
        this.type = type;
    }

    public abstract void accept(AnimalVisitor av); // <-- Open up for visitors.
}

class Lion extends Animal {
    public Lion(String name) {
        super(name, LION_TYPE);
    }

    public void roar() {
        // System.out.println("Roar");
    }

    public void accept(AnimalVisitor av) {
        av.visit(this); // <-- Accept and call visit.
    }
}

class Deer extends Animal {

    public Deer(String name) {
        super(name, DEER_TYPE);
    }

    public void runAway() {
        // System.out.println("Running...");
    }

    public void accept(AnimalVisitor av) {
        av.visit(this); // <-- Accept and call visit.
    }

}

interface AnimalVisitor {
    void visit(Lion l);

    void visit(Deer d);
}

class ActionVisitor implements AnimalVisitor {

    public void visit(Deer d) {
        d.runAway();
    }

    public void visit(Lion l) {
        l.roar();
    }
}

Test results:

Visitor took 920842192 ns
instanceof took 511837398 ns
type constant took 535296640 ns

This visitor pattern introduces 2 extra method calls that are unnecessary with instanceof. This is probably why it's slower.

Not that performance is the only consideration, but notice how 2 instanceofs are faster than even a 2-case switch statement. Plenty of people have worried about the performance of instanceof, but this should put the worry to rest.

As a Java Developer, I feel frustrated when people have a dogmatic attitude about avoiding the use of instanceof, because there have been several times in my work I wanted to clean up or write new clean code by using instanceof, but coworkers/superiors didn't approve of this approach , because they have more or less blindly accepted the idea that instanceof should never be used. I feel frustrated because this point is often driven home with toy examples that don't reflect real business concerns.

Whenever you pursue modular software design, there will always be times when type-dependent decisions need to be isolated from the types in question, so that the types have as few dependencies as possible.

This visitor pattern doesn't break modularity, but it's not a superior alternative to instanceof.

够运 2024-10-04 13:40:28

考虑为构造函数中的动物设置的操作(咆哮、逃跑等)添加一个接口。然后在 Animal 类上有一个抽象方法,例如 act(),它的调用类似于 Adeel 的方法。

这将使您可以随时交换动作以通过字段执行。

Consider adding an interface for the action (Roar, Run away, etc) which is set on the animal in the constructor. Then have an abstract method such as act() on the Animal class which gets called similar to what Adeel has.

This will let you swap in actions to act out via a field at any time.

彡翼 2024-10-04 13:40:28

最简单的方法是让超类实现默认行为。

public enum AnimalBehaviour { 
     Deer { public void runAway() { System.out.println("Running..."); } },
     Lion { public void roar() { System.out.println("Roar"); } }
     public void runAway() { } 
     public void roar() { }
 } 

 public class Animal {
     private final String name;
     private final AnimalBehaviour behaviour;
     public Animal(String name, AnimalBehaviour behaviour) {
         this.name = name;
         this.behaviour = behaviour;
     }
     public void runAway() { behaviour.runAway(); } 
     public void roar() { behaviour.roar(); }
  }

 public class TestAnimals { 
   public static void main(String... args) { 
     Animal[] animals = { 
       new Animal("Geo", AnimalBehaviour.Lion), 
       new Animal("Bambi", AnimalBehaviour.Deer), 
       new Animal("D2", AnimalBehaviour.Deer) 
     }; 

     for (Animal a : animals) {
       a.roar(); 
       a.runAway(); 
     } 
   }
 }

The simplest approach is to have the super class implement a default behaviour.

public enum AnimalBehaviour { 
     Deer { public void runAway() { System.out.println("Running..."); } },
     Lion { public void roar() { System.out.println("Roar"); } }
     public void runAway() { } 
     public void roar() { }
 } 

 public class Animal {
     private final String name;
     private final AnimalBehaviour behaviour;
     public Animal(String name, AnimalBehaviour behaviour) {
         this.name = name;
         this.behaviour = behaviour;
     }
     public void runAway() { behaviour.runAway(); } 
     public void roar() { behaviour.roar(); }
  }

 public class TestAnimals { 
   public static void main(String... args) { 
     Animal[] animals = { 
       new Animal("Geo", AnimalBehaviour.Lion), 
       new Animal("Bambi", AnimalBehaviour.Deer), 
       new Animal("D2", AnimalBehaviour.Deer) 
     }; 

     for (Animal a : animals) {
       a.roar(); 
       a.runAway(); 
     } 
   }
 }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文