我有一个抽象类Animal
,其中有两个扩展类Dog
和Cat
。
public abstract class Animal {
...
}
public class Dog extends Animal {
...
}
public class Cat extends Animal {
...
}
在另一堂课中,我有一个ArrayList<Animal> animals
,其中同时包含Cat
和Dog
的实例。
在具有ArrayList<Animal>
的类中,我希望能够重载带有doSomething()
或Dog d
作为参数的方法Cat c
,但仍然能够从动物的ArrayList调用它们。如下:
public void aMethod(){
ArrayList<Animals> animals = getTheAnimals();
for (Animal a : animals){
doSomething(a);
}
}
public void doSomething(Dog a){
//a is an object of type dog
}
public void doSomething(Cat a){
//a is an object of type cat
}
从本质上讲,每种方法都充当“选择器”,该方法正在为其接收动物type
。
按上述方式尝试时,出现以下编译错误:
错误:找不到适合doSomething(Animal)的方法
我知道我可以使用instanceOf
或a.getClass().equals(type)
之类的东西,但是我已经读到这是不好的做法。
这个问题与this other question略有不同,因为我想要两个单独的方法,每个方法具有不同的参数。
编辑:我将避免使用“访客模式”,因为我认为它不完全适合我的实际实现。将doSomething()方法移入每个类,并进行重构以确保这在逻辑上有意义(每个类执行应负责的操作)。谢谢。
答案 0 :(得分:1)
与其在客户端方法中进行迭代并调用doSomething(),还不如在抽象类中保留doSomething()作为方法,那样您就可以更改在Cat或Dog中的使用方式。
disk = ${var.disks}
答案 1 :(得分:1)
将方法移动到df = pd.read_csv(filename.csv)
corpus = df.corpus
类中,因此每个实现都必须制作自己的方法版本。
Abstract
然后在上面提到的另一个类中,您可以执行以下操作:
public abstract class Animal {
public abstract void doSomething();
}
public class Dog extends Animal {
public void doSomething(){
System.out.println("Bark");
}
}
public class Cat extends Animal {
public void doSomething(){
System.out.println("Meow");
}
}
答案 2 :(得分:1)
您需要双重调度:java提供的运行时类型+必须实现的运行时参数类型。访问者模式是实现此目的的一种方法。
引入一个Visitable
接口,并在Animal
中实现它以使其接受访问者。
然后介绍一个访客。您可以根据需要在有或没有接口的情况下实现它。
简单版本(通过将访问者耦合到当前类),保留了代码的实际逻辑:
public interface Visitable{
void accept(MyClassWithSomeAnimals myClassWithSomeAnimals);
}
public abstract class Animal implements Visitable{
...
}
public class Dog extends Animal {
...
void accept(MyClassWithSomeAnimals myClassWithSomeAnimals){
myClassWithSomeAnimals.doSomething(this);
}
}
public class Cat extends Animal {
...
void accept(MyClassWithSomeAnimals myClassWithSomeAnimals){
myClassWithSomeAnimals.doSomething(this);
}
}
并以这种方式更新MyClassWithSomeAnimals
:
public void aMethod(){
ArrayList<Animals> animals = getTheAnimals();
for (Animal a : animals){
a.accept(this);
}
}
此解决方案有效,但有一个缺点。它向MyClassWithSomeAnimals
子类公开Animal
,而这些子类仅需要一种方法来调用visitor方法,而不是任何公共方法。
因此,一种改进方法是引入一个Visitor
接口,以限制doSomething()
Animal对访问者的了解:
public interface Visitor{
void doSomething(Dog dog);
void doSomething(Cat cat);
}
因此,使MyClassWithSomeAnimals
实现它并更改Visitable类/具体类以使用它:
public interface Visitable{
void accept(Visitor visitor);
}
public class Dog extends Animal {
...
void accept(Visitor visitor){
visitor.doSomething(this);
}
}
public class Cat extends Animal {
...
void accept(Visitor visitor){
visitor.doSomething(this);
}
}
答案 3 :(得分:0)
是的,instanceof和getclass会使您的类依赖于特定的类型,并且使用这种做法不太好。
但是,通过让方法接受Dog
或Cat
,您再次取决于具体的具体实现。
更好的方法是在Animal
接口performAction()
中定义方法。在Dog
和Cat
中提供实现。现在,迭代您的动物列表,然后调用performAction()
并根据实际实例多态地调用Dog或Cat实现的方法。以后,即使在代码中添加了Animal的另一种实现,也无需修改代码。
这样,您将在Dog类中具有与Dog相关的逻辑,而在Cat类中具有与Cat相关的逻辑,而不会在任何其他外部类中具有Cat相关的逻辑。这将有助于遵守OCP和SRP(开放关闭和单一责任原则)。
另外,如果您希望稍后执行行为,并且这种情况使您知道实现是固定的,并且希望稍后使用行为代码注入行为,那么我建议使用Visitor
模式,但我仍然认为应该不被滥用,(有时不使用设计模式而是滥用设计模式)有时我们会在不需要时强制使用模式。访客模式使用多态来将第一个调用分派到特定实例,第二个分派从该实例调用特定实例来实现实现Visitor接口的类中的重载方法。 (名称可以不同,我提到了访问者暗示实现访问者概念的类)。仅在需要时才选择访问者模式(例如实现),并且基于多态的常规方法不适合这种情况。