7

假设有一个接口Subject。

interface Subject { void request(); }

我们有一个 RealSubject 类。假设我们想要增强 RealSubject,我们可以使用包裹 RealSubject 的代理模式:

class Proxy implements Subject { 
   private RealSubject ref;
   void request(){ ... }
}

或者我们可以扩展 RealSubject 并覆盖该方法

class EnhancedSubject extends RealSubject {
   @Override
   void request() { ... }
}

哪种方法更好?我知道 Liskov 原则;假设 EnhancedSubject 满足 Liskov 原则。你还考虑继承吗?

如果没有接口Subject(即RealSubject 没有实现任何接口),似乎“继承和覆盖”是唯一的选择,因为在代理模式中没有要实现的接口。如果没有 Subject 接口,你还能应用代理模式吗?

4

2 回答 2

6

回答您的第一个问题,“哪种方法更好”?

我个人更喜欢使用接口和代理(或装饰器)模式来实现这样的东西。(参见:第 16 条:Effective Java (2nd Edition)的优先组合优于继承)

如果超类 ( RealSubject) 不在您的控制之下,即在同一个包中,并且是专门为 Extension 设计和记录的,那么从版本到版本对其实现的任何更改都可能会破坏您对子类 ( EnhancedSubject) 的实现。本质上我要说的是:直接依赖于具体的实现会导致代码脆弱。

针对您的第二个问题,“如果EnhancedSubject满足 Liskov 原则,您还考虑继承吗?

RealSubject如果和EnhancedSubject在您的控制下并以相同的生命周期释放,则再次使用继承是安全的,但是直接依赖于具体实现会导致代码脆弱。

希望引导您使用接口实现的另一点是单元测试。

例如,使用您想要应用单元测试的情况,将模拟依赖项RealSubject注入到您的Proxy实现中会容易得多,Subject这样您就可以专门测试Proxy类,而不必完全测试整个对象层次结构,RealSubjectEnhancedSubject, 只是为了确认它的EnhancedSubject行为符合预期。

不过我想可以说,如果它都是一个非常简单的 API,并且将来几乎不会改变,那么具体的实现就更简单了。保持简单愚蠢(KISS)是最好的政策之一。

如果没有Subject接口,你还可以应用代理模式吗? ”你可以注入RealSubject另一个类并RealSubject在内部使用,但是如果使用的APIRealSubject直接依赖于具体类,你别无选择,只能使用继承。

于 2010-10-19T05:32:37.523 回答
1

代理/装饰器的优点是它可以与派生类一起重用。这可以将代理与类的实现分开。(如果我写了无效的 java.. 已经有一段时间了,你将不得不原谅我)

在这种情况下,您可能会编写如下内容:

class LoggingSubjectProxy implements Subject
{
   private Subject ref;
   void request() 
   { 
      log("Called request");
      ref.request();
   }
}

现在你可以做

LoggingSubjectProxy l;
if(dosimple)
{
    l.ref = SimpleSubject();
}
else
{
    l.ref = ComplexSubject();
}
l.request()

如果这对于您想要的扩展来说太过分了,那么您应该使用简单的继承和覆盖。

于 2010-10-19T04:49:54.450 回答