所以,让我们说你有这个:
if ([foo respondsToSelector:@selector(bar:)]) {
[foo bar:abc];
} else {
[qux mux:abc];
}
bar:
和mux:
都有副作用。
你如何将它移植到Java,在协议(接口)中没有@optional成员这样的东西?
我可以想到三种方式:
我是否错过了其他令人信服的选择?假设这个代码不经常调用,所以我们不需要优化性能,我认为3是最好的方法,因为它是可执行的。是否存在备选方案的论据?
答案 0 :(得分:2)
MonoTouch为您的问题提供了完善的解决方案:使用Objective-C设计的代码在OO VM语言中感觉相当舒适。
他们通过将基类标记为abstract
并将所有必需方法标记为virtual
来解决它,并在C#中保留未标记的可选方法(或者更确切地说,标记为{{1}},但Java不需要那)。它明确地围绕所需方法的编译时强制执行,同时仍然允许可选方法。
答案 1 :(得分:1)
不,你描述为“Java方式”的东西实际上是一种反方式:在程序的常规流程中不应该捕获RuntimeException
子类,因为它们表示编程错误。 / p>
更好的方法是将协议拆分为更小的部分:所有必需的方法最终都在一个接口中,可选的方法将在它们自己的微小接口中。现在,您可以使用instanceof
测试对象,以确定是否实现了可选接口及其隐含方法。
以下是一个例子:
@protocol MyProtocol
@optional
-(void)method1;
-(void)method2;
@required
-(void)method3;
-(void)method4;
-(void)method5;
@end
interface IMyProtocol {
void method3();
void method4();
void method5();
}
interface IMyProtocolMethod1 {
void method1();
}
interface IMyProtocolMethod2 {
void method2();
}
class MyProtocolImplWithMethod2 implements IMyProtocol, IMyProtocolMethod2 {
public void method2() {
}
public void method3() {
}
public void method4() {
}
public void method5() {
}
}
然后你可以写这个支票:
if (obj instanceof IMyProtocolMethod2) {
((IMyProtocolMethod2)obj).method2();
}
答案 2 :(得分:1)
坦率地说,Java的方法是避免这种情况。通常,Java程序员会更加努力(并使用许多已建立的模式)而不是打破多态性 - 因此切换类型将被视为一种不好的做法。
最简单的解决方案和@optional的直接翻译将是使用instanceof并在接口内部使用可选方法来扩展所需的方法:
interface MyProtocol {
void required1();
void required2();
}
interface MyProtocolWithOptional extends MyProtocol {
void optional1();
void optional2();
}
然后:
public static void test(MyProtocol mp) {
mp.required1();
if (mp instanceof MyProtocolWithOptional) {
((MyProtocolWithOptional)mp).optional1();
}
}
如果您有很多接口,并且您的应用程序在运行时是可扩展的,那么您可以使用类似功能模式(http://java.dzone.com/print/4771)的解决方案,其中可以查询每个对象的可用接口(这有点像COM ,有点不像COM)。你可以从以下内容开始:
interface ThingWithCapabilities<T> {
T interfaceFor(Class<T> inteface)
}
ThingWithCapabilities是所有其他人扩展的基本接口。然后在运行时使用它:
public static void test(ThingWithCapabilities mp) {
if (mp.interfaceFor(MyProtocolWithOptions.class) != null) {
mp.interfaceFor(MyProtocolWithOptions.class).optional1();
}
}
好消息是铸件消失了。更好的是你没有将MyProtocolWithOptions实例的生命周期与ThingWithCapabilities的生命周期联系起来(例如,你可以拥有一个MyProtocolWithOptions实例来满足许多ThingWithCapabilities实例;另一方面,你可以返回这个实例,如果你碰巧实现MyProtocolWithOptions)。如果你将它与null对象模式结合在一起(你有一个MyProtocolWithOptions的无状态实例,什么都不做,并且每个没有MyProtocolWithOptions功能而不是null的ThingWithCapabilities返回),你可以获得更好的代码:
public static void test(ThingWithCapabilities mp) {
mp.interfaceFor(MyProtocolWithOptions.class).optional1();
}
除非你写的东西很大,否则这种方法可能很复杂。我刚刚提到它,因为它很有趣(我在一些宠物项目上成功使用过它)。