2

所以,假设你有这个:

if ([foo respondsToSelector:@selector(bar:)]) {
   [foo bar:abc];
} else {
   [qux mux:abc];
}

而且两者bar:都有mux:副作用。

如果协议(接口)中没有 @optional 成员之类的东西,您将如何将其移植到 Java?

我可以想到三种方法:

  1. C方式:向接口添加一个方法,该方法返回一个位域,表示哪些方法已实现且有效。
  2. COM方式:修改接口,使所有方法都返回一个结果码并检查E_NOTIMPL。使用 out 参数作为返回值。
  3. (我想象的是)Java 方式:将每个接口方法标记为抛出 UnsupportedOperationException 并捕获它们以检查未实现的方法。

我是否错过了任何其他令人信服的选择?假设这段代码不经常被调用,所以我们不需要优化性能,我认为 3 是最好的方法,因为它是可执行的。有替代方案的论据吗?

4

3 回答 3

2

MonoTouch 为您的问题提供了完善的解决方案:使为 Objective-C 设计的代码在 OO VM 语言中感觉相当舒适。

他们通过有一个基类来解决这个问题,该基类将所有必需的方法标记为abstract,并且不标记可选方法(或者更准确地说,virtual在 C# 中标记为,但 Java 不需要)。它是围绕必需方法的编译时强制执行而明确设计的,同时仍允许可选方法。

更多详细信息:http ://docs.xamarin.com/index.php?title=ios/tutorials/Events%2C_Protocols_and_Delegates#Protocols_Deep_Dive

于 2012-07-18T23:45:01.313 回答
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)。如果将它与空对象模式结合使用(其中您有一个无状态的 MyProtocolWithOptions 实例,它什么都不做,并且由每个不具有 MyProtocolWithOptions 功能而不是 null 的 ThingWithCapabilities 返回),您可以获得更好的代码:

public static void test(ThingWithCapabilities mp) {
    mp.interfaceFor(MyProtocolWithOptions.class).optional1();
}

除非你正在写一些大的东西,否则这种方法可能很复杂。我只是提到它,因为它很有趣(并且我在一些宠物项目上成功使用过它)。

于 2012-07-18T16:53:07.313 回答
1

不,您所说的“Java 方式”实际上是一种反方式:RuntimeException在程序的常规流程中不应该捕获子类,因为它们表示编程错误。

更好的方法是将协议拆分成更小的部分:所有必需的方法最终都在一个接口中,而可选方法将在它们自己的小接口中。现在您可以测试您的对象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();
}
于 2012-07-18T16:25:34.233 回答