布朗博士的答案显示了经典的《德米特律法》教科书的实现-以及添加数十种方法的烦恼/杂乱无章的代码膨胀,这可能就是为什么程序员(包括我自己)即使这样做应该也不愿意这么做的原因。
还有一种分离对象层次结构的替代方法:
通过您的方法和属性公开interface
类型,而不是class
类型。
在原始海报(OP)的情况下,encoder->WaitEncoderFrame()
将返回IEncoderFrame
而不是Frame
,并定义允许的操作。
解决方案1
在最简单的情况下,Frame
和Encoder
类都是你的控制之下,IEncoderFrame
是方法框架的一个子集已经公开暴露,而Encoder
类并不真正关心你做的是什么样的对象。然后,实现很简单(c#中的代码):
interface IEncoderFrame {
void DoOrGetSomething();
}
class Frame : IEncoderFrame {
// A method that already exists in Frame.
public void DoOrGetSomething() { ... }
}
class Encoder {
private Frame _frame;
public IEncoderFrame TheFrame { get { return _frame; } }
...
}
解决方案2
在中间情况下,如果Frame
定义不受您的控制,或者将IEncoderFrame
的方法添加到不合适Frame
,则Adapter是一个很好的解决方案。这就是CandiedOrange的答案所讨论的new FrameHandler( frame )
。重要信息:如果执行此操作,则将其公开为接口而不是类,则更加灵活。Encoder
我必须了解class FrameHandler
,但是客户只需要了解interface IFrameHandler
。或以我的名字命名,interface IEncoderFrame
-表示从编码器的POV看,它具体是Frame:
interface IEncoderFrame {
void DoOrGetSomething();
}
// Adapter pattern. Appropriate if no access needed to Encoder.
class EncoderFrameWrapper : IEncoderFrame {
Frame _frame;
public EncoderFrameWrapper( Frame frame ) {
_frame = frame;
}
public void DoOrGetSomething() {
_frame....;
}
}
class Encoder {
private Frame _frame;
// Adapter pattern. Appropriate if no access needed to Encoder.
public IEncoderFrame TheFrame { get { return new EncoderFrameWrapper( _frame ); } }
...
}
成本:每次encoder.TheFrame
调用新对象EncoderFrameWrapper的分配和GC 。(您可以缓存该包装程序,但会添加更多代码。只有在无法用新框架替换编码器的框架字段时,才能轻松可靠地进行编码。)
解决方案3
在更困难的情况下,新包装器需要同时了解Encoder
和Frame
。该对象本身会违反LoD-它正在操纵Encoder和Frame之间的关系,这应该是Encoder的责任-可能很难解决。如果您沿着这条路走,可能会发生以下情况:
interface IEncoderFrame {
void DoOrGetSomething();
}
// *** You will end up regretting this. See next code snippet instead ***
class EncoderFrameWrapper : IEncoderFrame {
Encoder _owner;
Frame _frame;
public EncoderFrameWrapper( Encoder owner, Frame frame ) {
_owner = owner; _frame = frame;
}
public void DoOrGetSomething() {
_frame.DoOrGetSomething();
// Hmm, maybe this wrapper class should be nested inside Encoder...
_owner... some work inside owner; maybe should be owner-internal details ...
}
}
class Encoder {
private Frame _frame;
...
}
那太丑了。当包装器需要触摸其创建者/所有者(编码器)的详细信息时,有一个复杂的实现:
interface IEncoderFrame {
void DoOrGetSomething();
}
class Encoder : IEncoderFrame {
private Frame _frame;
// HA! Client gets to think of this as "the frame object",
// but its really me, intercepting it.
public IEncoderFrame TheFrame { get { return this; } }
// This is the method that the LoD approach suggests writing,
// except that we are exposing it only when the instance is accessed as an IEncoderFrame,
// to avoid extending Encoder's already large API surface.
public void IEncoderFrame.DoOrGetSomething() {
_frame.DoOrGetSomething();
... make some change within current Encoder instance ...
}
...
}
当然,如果我知道自己会在这里结束,我可能不会这样做。只需编写LoD方法即可完成。无需定义接口。另一方面,我喜欢接口将相关方法包装在一起。我喜欢对“框架”进行“类似框架的操作”的感觉。
最后评论
考虑一下:如果实施者Encoder
觉得公开Frame frame
适合他们的整体体系结构,或者“比实现LoD容易得多”,那么如果他们改为执行我所展示的第一个代码片段,则安全得多-公开有限的框架,作为接口。 以我的经验,这通常是一个完全可行的解决方案。只需根据需要向接口添加方法。(我说的是一个场景,我们“知道” Frame已经有了所需的方法,或者添加起来很容易且没有争议。每种方法的“实现”工作是在接口定义中添加一行。)知道即使在最坏的未来情况下,也可以保持该API正常工作-在这里,IEncoderFrame
Frame
Encoder
。
另请注意,如果您没有权限添加IEncoderFrame
到Frame
,或者所需的方法不太适合一般Frame
类,并且解决方案2不适合您,可能是由于额外的对象创建和销毁,解决方案3可以看作是一种组织Encoder
LoD 方法的简单方法。不要只是通过许多方法。将它们包装在Interface中,并使用“显式接口实现”(如果您使用的是c#),以便仅当通过该接口查看对象时才可以访问它们。
我要强调的另一点是,决定将功能公开为接口,从而解决了上述所有3种情况。首先,IEncoderFrame
只是Frame
的功能的子集。第二个IEncoderFrame
是适配器。第三部分IEncoderFrame
是Encoder
s功能的一个分区。在这三种情况之间您的需求是否变化都没关系:API保持不变。