Java 如何避免大型if语句和instanceof
动物Java 如何避免大型if语句和instanceof,java,casting,iteration,Java,Casting,Iteration,动物 public abstract class Animal { String name; public Animal(String name) { this.name = name; } } public abstract class Animal { String name; public Animal(String name) { this.name = name; } public abstract void exhibitNaturalBehaviou
public abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
}
public abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public abstract void exhibitNaturalBehaviour();
}
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.
}
狮子
public class Lion extends Animal {
public Lion(String name) {
super(name);
// TODO Auto-generated constructor stub
}
public void roar() {
System.out.println("Roar");
}
}
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 runAway() {
System.out.println("Running...");
}
}
public class Deer extends Animal {
public Deer(String name) {
super(name);
}
public void exhibitNaturalBehaviour() {
System.out.println("Running...");
}
}
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) {
if (a instanceof Lion) {
Lion l = (Lion) a;
l.roar();
}
if (a instanceof Deer) {
Deer l = (Deer) a;
l.runAway();
}
}
}
}
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();
}
}
}
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.
}
}
公共类测试动物{
公共静态void main(字符串[]args){
动物狮子=新狮子(“Geo”);
动物鹿1=新鹿(“D1”);
动物鹿2=新鹿(“D2”);
List li=new ArrayList();
加上(狮子);
li.添加(deer1);
li.添加(deer2);
对于(动物a:li){
如果(狮子的一个实例){
狮子l=(狮子)a;
l、 吼叫();
}
如果(鹿的实例){
鹿l=(鹿)a;
l、 失控();
}
}
}
}
有没有更好的方法可以在不强制转换的情况下遍历列表?在上述情况下,这似乎没问题,但是如果基类有很多扩展,那么我们也需要很多if块。有没有设计模式或原则来解决这个问题?是的,在抽象类中提供一个名为
action()
的方法,在两个子类中实现它,一个会咆哮,另一个会失控如果你的方法不是多态的,你不能没有强制转换。要使其多态,请在基类中声明一个方法,并在子类中重写它。Animal
public abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
}
public abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public abstract void exhibitNaturalBehaviour();
}
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.
}
狮子
public class Lion extends Animal {
public Lion(String name) {
super(name);
// TODO Auto-generated constructor stub
}
public void roar() {
System.out.println("Roar");
}
}
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 runAway() {
System.out.println("Running...");
}
}
public class Deer extends Animal {
public Deer(String name) {
super(name);
}
public void exhibitNaturalBehaviour() {
System.out.println("Running...");
}
}
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) {
if (a instanceof Lion) {
Lion l = (Lion) a;
l.roar();
}
if (a instanceof Deer) {
Deer l = (Deer) a;
l.runAway();
}
}
}
}
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();
}
}
}
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.
}
}
避免
实例
而不在基类中发明一些新的人工方法(使用非描述性名称,如performAction
或doWhatYouAreSupposedToDo
)的一种优雅方法是使用。以下是一个例子:
动物
public abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
}
public abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public abstract void exhibitNaturalBehaviour();
}
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.
}
测试动物
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();
}
}
}
}
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();
}
}
}
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.
}
}
公共类测试动物{
公共静态void main(字符串[]args){
动物狮子=新狮子(“Geo”);
动物鹿1=新鹿(“D1”);
动物鹿2=新鹿(“D2”);
List li=new ArrayList();
加上(狮子);
li.添加(deer1);
li.添加(deer2);
对于(动物a:li)
a、 accept(new ActionVisitor());//这里有一个动物的列表。通常,当你有一个对象列表时,所有这些对象都必须能够做同样的事情而不需要浇铸
因此,最好的两种解决方案是:
- 对于两个具体的类有一个通用的方法(在
Animal
中定义为abstract
)
- 从一开始就将
狮子
与鹿
分开,并有两个不同的列表
考虑在构造函数中为动物设置的动作(咆哮、逃跑等)添加一个接口。然后在动物类上有一个抽象方法,如act(),该方法的调用与Adeel类似
这将允许您随时通过字段交换操作。该语言中的模式匹配支持消除了对丑陋访问者模式的需要
例如,请参见此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 _ => ()
}
}
}
抽象类动物(名称:String)
类Lion(名称:String)扩展了Animal(名称){
def roar(){
println(“吼!”)
}
}
类鹿(名称:String)扩展了动物(名称){
def失控(){
println(“正在运行!”)
}
}
对象测试动物{
def main(参数:数组[字符串]){
val动物=名单(新狮子(“Geo”)、新鹿(“D1”)、新鹿(“D2”))
(动物l.咆哮)
案例d:鹿=>d.逃跑()
案例u=>()
}
}
}
最简单的方法是让超类实现默认行为
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();
}
}
}
事实证明instanceof比上面介绍的visitor模式快;我认为这应该让我们产生疑问,当visitor模式用更多的代码行做同样的事情时,它真的比instanceof更优雅吗
这是我的测试。我比较了3种方法:上面的访问者模式、instanceof和Animal中的显式类型字段
操作系统:Windows 7企业版SP1,64位
处理器:英特尔(R)核心(TM)i7CPU 860@2.80GHz 2.93GHz
内存:8.00GB
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();
}
}
import java.util.ArrayList;
导入java.util.List;
公共类动物测试1{
公共静态void main(字符串[]args){
动物狮子=新狮子(“Geo”);
动物鹿1=新鹿(“D1”);
动物鹿2=新鹿(“D2”);
List li=new ArrayList();
加上(狮子);
li.添加(deer1);
li.添加(deer2);
int reps=10000000;
长时间启动,已过;
start=System.nanoTime();
对于(int i=0;i a、 accept(new ActionVisitor());//方法是否必须有不同的名称(roar、runAway)?多态性的思想是在子类中使用相同的方法,JVM调用适当的方法。有没有更好的方法来做错事?在测试动物中的代码的意图是什么?是“对每只动物做动物特有的事情”还是“咆哮”allLion
s,runAway
allDeer
“没有别的了吗?是的,我倾向于同意。我忍不住投票给访问者。我喜欢它,但我认为你做的比我多。+1关于你后来添加的描述,访问者添加任何新的动物
都需要修改动物观察器
。然而,另一个选择是不做这种修改就好了关于。你说呢?@Adeel Ansari,这是一个很好的观点。这取决于我想说的情况。这取决于你是想把具体的动作实现收集在一个类中,还是想把它们分散在不同的类中。在不同的情况下,两者都是可取的。然而,在这种情况下,roar
和runAway
它们是如此的不同,以至于我不想为它们发明一个共同的名称。难道不可能在父类(动物)中实现accept方法吗?不,不幸的是,不可能。这是因为方法签名是在编译时确定的,而目标类型在本例中是确定的