Doc Brown의 답변 은 Law of Demeter의 고전적인 교과서 구현을 보여줍니다. 그리고 수십 가지 방법을 추가하는 성가신 / 혼란스러운 코드 팽창은 아마도 자신도 포함 된 프로그래머가 종종 그렇게해도 귀찮게하지 않는 이유 일 것입니다.
객체의 계층 구조를 분리하는 다른 방법이 있습니다.
메서드와 속성을 통해 형식 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 )
. 중요 :이 작업을 수행 하는 경우 클래스가 아닌 인터페이스 로 노출하면 더 유연합니다 . 에 대해 알아야 하지만 클라이언트 만 알면 됩니다. 또는 내가 명명 한대로 - 인코더의 POV에서 볼 때 특히 프레임 임을 나타냅니다 .Encoder
class FrameHandler
interface IFrameHandler
interface IEncoderFrame
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 ); } }
...
}
비용 : 새로운 객체 인 EncoderFrameWrapper의 할당 및 GC encoder.TheFrame
가 호출 될 때마다 . 이 래퍼를 캐시 할 수 있지만 더 많은 코드가 추가됩니다. 인코더의 프레임 필드를 새 프레임으로 바꿀 수없는 경우에만 안정적으로 코딩 할 수 있습니다.
솔루션 3
더 어려운 경우, 새로운 래퍼 모두에 대해 알고 있어야 Encoder
하고 Frame
. 그 객체 자체가 LoD를 위반할 것입니다. 이것은 인코더의 책임이어야하는 인코더와 프레임 간의 관계를 조작하는 것입니다. 그 길을 시작하면 다음과 같은 일이 발생할 수 있습니다.
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
그들이 대신 한 경우에, 그것은 훨씬 더 안전했을 전반적인 아키텍처에 적합한이었다, 또는 "너무 쉽게 디테일 정도를 구현하는 것보다"이었다 처음 내가 보여 니펫을 - 제한된 일부를 노출 인터페이스로서의 프레임. 내 경험상, 그것은 종종 완전히 실행 가능한 솔루션입니다. 필요에 따라 인터페이스에 메소드를 추가하십시오. (프레임에 필요한 메소드가 이미 "알고있는"시나리오에 대해 이야기하고 있습니다. 추가하기가 쉽고 논란의 여지가 없습니다. 각 메소드에 대한 "구현"작업은 인터페이스 정의에 한 줄을 추가하는 것입니다.) 최악의 미래 시나리오에서도 해당 API를 계속 작동시킬 수 있습니다.IEncoderFrame
Frame
Encoder
.
또한주의 당신이 추가 할 수있는 권한이없는 경우 그 IEncoderFrame
대상을 Frame
, 또는 필요한 방법은 일반에 잘 맞지 않는 Frame
솔루션 # 2는 아마도 때문에 별도의 객체 생성 및 파괴, 당신을 적합하지 않는 클래스와, 솔루션 # 3은 Encoder
LoD를 달성 하기위한 방법을 구성하는 간단한 방법으로 볼 수 있습니다 . 수십 가지 방법 만 거치지 마십시오. 인터페이스로 래핑하고 "명시 적 인터페이스 구현"(C #에있는 경우)을 사용하면 해당 인터페이스를 통해 개체를 볼 때만 액세스 할 수 있습니다.
강조하고 싶은 또 다른 요점 은 기능을 인터페이스 로 노출 하기로 한 결정 은 위에서 설명한 세 가지 상황을 모두 처리 했다는 것 입니다. 첫 번째 IEncoderFrame
는 단순히 Frame
기능 의 하위 집합입니다 . 두 번째 IEncoderFrame
는 어댑터입니다. 세 번째 IEncoderFrame
는 Encoder
기능에 대한 파티션 입니다. 이 세 가지 상황 사이에서 요구 사항이 변경 되더라도 중요하지 않습니다. API는 동일하게 유지됩니다.