假设有一个接口主题。
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没有实现任何接口),似乎“inherit and override”是唯一的选项,因为在Proxy模式中没有要实现的接口。如果没有主题界面,您还可以应用代理模式吗?
答案 0 :(得分:6)
在回答您的第一个问题时,“哪种方法更好”?
我个人更喜欢使用Interface and Proxy(或Decorator)模式来实现这样的东西。 (参见:第16项:赞成组合而不是Effective Java (2nd Edition)的继承)
如果超类(RealSubject
)不在您的控制之下,即在同一个包中,并且为Extension专门设计和记录,那么从实现到版本的任何更改都可能会破坏子类的实现( EnhancedSubject
)。基本上我所说的是:直接根据具体实现导致代码脆弱。
在回答你的第二个问题时,“如果EnhancedSubject
满足Liskov原则,你还会考虑继承吗?”
如果RealSubject
和EnhancedSubject
在您的控制之下并以相同的生命周期发布,则再次使用继承是安全的,但直接根据具体实现导致脆弱的代码强>
另一个有希望使您使用Interface实现的观点是Unit 测试
例如,使用您希望应用单元测试的情况,将RealSubject
的模拟依赖项注入Proxy
Subject
的实现中会更容易您可以专门测试Proxy
类,而不必完全测试整个对象层次结构RealSubject
和EnhancedSubject
,只是为了确认EnhancedSubject
的行为符合预期。< / p>
我想可以这么说,如果它是一个非常简单的API并且将来几乎不会改变,那么具体实现只是 simpler 。并保持简单愚蠢(K.I.S.S。)是最好的政策之一。
“如果没有主题界面,您仍然可以应用代理模式吗?”
您可以将RealSubject
注入另一个类并在内部使用RealSubject
,但如果使用RealSubject
的API直接依赖于具体类,则您没有其他选择,只能使用继承。
答案 1 :(得分: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()
如果这对您想要的扩展名来说太过分了,那么您应该使用简单的加法和覆盖。