将抽象超类型的不同子类型作为参数处理的最佳方法是什么,例如处理事件时。
情况如下:
超类型:
public interface MySuperInterface {
}
子类型
public class SubclassA implements MySuperInterface {
}
另一个子类型
public class SubclassB implements MySuperInterface {
}
某些类应该能够处理MySuperInterface的任何子类型
public class MySuperHandler {
public void handle(MySuperInterface mysuper) {
//do it
}
}
我的不同方法是
处理程序方法中的switch / case语句。 (我不喜欢)
接口中的方法接收(MySuperHandler)以及对此方法的调度 在handle方法中: mysuper.receive(this)(这意味着接口知道处理程序类)
但出于上述原因,我不满足于这些解决方案。
有没有办法处理这种情况?
感谢
答案 0 :(得分:3)
一种方法是使用Visitor Pattern。它看起来像这样:
public interface MySuperInterface {
<T> T acceptVisitor(MySuperInterfaceVisitor<T>);
}
public interface MySuperInterfaceVisitor<T> {
T visitA(SubclassA a);
T visitB(SubclassB a);
}
public class SubclassA implements MySuperInterface {
<T> T acceptVisitor(MySuperInterfaceVisitor<T> visitor) {
return visitor.visitA(this);
}
}
public class SubclassB implements MySuperInterface {
<T> T acceptVisitor(MySuperInterfaceVisitor<T> visitor) {
return visitor.visitB(this);
}
}
public class MySuperHandler implements MySuperInterfaceVisitor<Foo>{
Foo visitA(SubclassA a) {
// construct Foo from SubclassA instance
}
Foo visitB(SubclassB a) {
// construct Foo from SubclassB instance
}
}
这有点像你的#2,除了接口(和子类)不需要知道处理程序。他们只需要了解访客界面。如果您不希望MySuperInterface
及其实现了解您的特定处理程序,那么这很好。
BTW,而不是打电话:
myHandler.handle(myImpl);
你打电话给:
myImpl.acceptVisior(myHandler);
如果你想确保每个处理程序都可以处理你的接口的每个实现,但仍然保持实现不知道存在的所有“处理程序”,这种方法很好。
如果添加接口的另一个实现(MySuperInterface
),编译器将强制您添加acceptVisitor
方法。此方法可以使用现有的visit*
方法之一,也可以在访问者界面中添加新方法。如果您执行后者,则必须更新所有访问者(也称为“处理程序”)实现。这确保了可以处理每个子类型。
这种方法比assylias的答案更复杂,只有你想要打破MySuperInterface
的实现与你的处理程序代码之间的耦合,或者你有强烈的组织愿望才真正有意义处理程序代码,以便特定类型处理的所有代码都是“在一起”。
访客模式的一个常见用途是以不同方式呈现对象。假设您希望能够将对象转换为PDF或HTML。您可以在界面中使用toHTML和toPDF方法。这种方法的缺点是,现在您的类依赖于您的库来生成HTML和PDF。此外,如果某人后来想要添加新类型的输出,则需要修改这些核心类,这可能是不合需要的。使用访问者模式,只有访问者类需要了解PDF或HTMl库,并且可以添加新访问者而无需修改核心类。 (但同样,添加新的核心类意味着您需要重用现有的visit*
方法,或者您必须修改访客实现的所有。)
答案 1 :(得分:2)
你的描述有点模糊但如果你有几个子类,其中一些共享一个共同的“句柄”行为,这可能有用 - 如果你只有2个子类,并且不打算将来有更多,抽象步骤可能是不必要的:
public interface MySuperInterface {
void handle();
}
public abstract AbstractMySuperInterface {
public void handle() {
//implement default behavior
}
}
public class SubclassA implements MySuperInterface {
//nothing here, just use default behavior
}
public class SubclassB implements MySuperInterface {
public void handle() {
//implement another behavior
}
}
public class MySuperHandler {
public void handle(MySuperInterface mysuper) {
mysuper.handle();
}
}