데 메터의 법에 따르면, 클래스는 멤버 중 하나를 반환 할 수 있습니까?


13

데메테르의 법칙에 관한 세 가지 질문이 있습니다.

팩토리 및 빌더 클래스와 같이 객체를 반환하도록 특별히 지정된 클래스 외에도 클래스의 속성 중 하나에 의해 유지되는 객체 또는 데 메타 법칙 을 위반하는 객체를 반환하는 방법은 괜찮습니다 (1) ? 그리고 그것이 데 미터 법칙을 위반한다면, 반환 된 객체가 데이터를 나타내는 불변의 객체인지 여부와이 데이터에 대한 게터 만 포함하는 것이 중요합니까 (2a)? 또는 해당 클래스의 데이터로 수행되는 모든 작업이 클래스 외부에서 수행되기 때문에 이러한 ValueObject 자체가 반 패턴입니까?

의사 코드에서 :

class A {}

class B {

    private A a;

    public A getA() {
        return this.a;
    }
}

class C {

    private B b;
    private X x;

    public void main() {
        // Is it okay for B.getA to exist?
        A a = this.b.getA();

        a.doSomething();

        x.doSomethingElse(a);
    }
}

Demeter의 법칙에 따라 위와 같은 패턴이 금지 된 것 같습니다. doSomethingElse()법을 위반하지 않고 부름받을 수 있도록하려면 어떻게해야합니까 (3)?


9
많은 사람들 (특히 기능 프로그래머)은 데메테르 법칙을 반 패턴으로 본다. 다른 사람들은 이것을 "때때로 유용한 데메테르 제안"으로 본다.
David Hammen

1
나는이 질문이 데메테르의 법칙 (Delaw of Demeter)의 부조리를 설명하게 만들 것이다. 예, LoD는 "가끔 유용하지만"일반적으로 어리 석습니다.
user949300

어떻게 x설정합니까?
candied_orange

@CandiedOrange x는 값이 호출 할 수있는 객체 인 다른 속성 일뿐 입니다 doSomethingElse.
user2180613 2016 년

1
귀하의 예는 LOD를 위반하지 않습니다. LOD는 개체의 클라이언트가 해당 개체의 내부 세부 정보 또는 공동 작업자와 연결되지 않도록하는 것입니다. 당신은 같은 것을 피하고 싶습니다 user.currentSession.isLoggedIn(). 고객 뿐만 아니라 공동 작업자의 고객을 연결 user하기 때문 입니다. 대신 당신은 쓸 수 있기를 원합니다 . 이것은 일반적으로 구현을 위임 하는 메소드를 추가하여 수행됩니다 . usersessionuser.isLoggedIn()isLoggedInusercurrentSession
요나

답변:


7

많은 프로그래밍 언어에서 모든 반환 값은 객체입니다. 다른 사람들이 말했듯이 반환 된 객체의 메서드를 사용할 수 없으면 아무것도 반환하지 않아야합니다. "클래스 A, B 및 C의 책임은 무엇입니까?" 그렇기 때문에 A, B, C와 같은 metasyntactic 변수 이름을 사용하면 "종속적"이라는 대답이 항상 요구됩니다. 이러한 용어에는 상속 책임이 없기 때문입니다.

기능이 어디로 가고 누가 무엇을 호출해야하는지에 대한 추론을 제공하는 Domain Driven Design을 살펴볼 수 있습니다.

불변 객체에 관한 두 번째 질문 은 엔티티 객체와 비교 한
가치 객체 의 개념에 관한 것입니다. DDD에서는 거의 제한없이 값 객체를 전달할 수 있습니다. 이것은 엔티티 객체에 대해서는 사실이 아닙니다.

단순한 LOD는 DDD에서 집계 에 액세스하기위한 규칙으로 훨씬 잘 표현됩니다 . 외부 개체는 집계 멤버에 대한 참조를 보유해서는 안됩니다. 루트 참조 만 보유해야합니다.

DDD를 제외하고는 최소한 도메인에 적합한 클래스에 대한 고유 한 고정 관념 세트를 개발하려고합니다. 시스템을 설계 할 때 이러한 고정 관념에 대한 규칙을 시행하십시오.

또한 이러한 모든 규칙은 복잡성을 관리하는 것이 아니라 자신을 망치는 것이 아니라는 점을 항상 기억하십시오.


1
DDD는 데이터와 같은 클래스 멤버가 노출 될 수있는시기 (예 : 게터가 존재해야 함)에 대한 규칙을 제공합니까? 모든 토론은 약 Law of Demeter, tell, don't ask, getters are evil, object encapsulation그리고 single responsibility principle이 질문에 졸이다 것 : 도메인 객체에 위임을 사용해야 할 때 객체의 값을 받고 그것으로 뭔가를하고 반대로? 그러한 결정을 내려야하는 상황에 미묘한 자격을 적용하는 것이 매우 유용합니다.
user2180613

많은 사람들이 클래스 인스턴스를 데이터 구조 (전달되는 데이터 백)로 취급하기 때문에 "게터가 악하다"와 같은 지침이 존재합니다. 이것은 객체 지향 프로그래밍이 아닙니다. 객체는 실제로 일부 작업을 제공하는 기능 / 동작 번들입니다 . 작업 범위는 책임에 의해 정의됩니다. 이 작업은 분명히 메소드 등에서 반환되는 객체를 생성 할 것입니다. 클래스가 실제로 "Y 및 Z 속성을 가진 데이터 객체 X 표현"을 넘어 명확하게 정의 할 수있는 의미있는 작업을 수행하는 경우 올바른 경로에 있습니다. . 나는 그것을 강조하지 않을 것입니다.
Jeremy

이 시점에서 확장하려면 많은 getter / asker를 사용할 때 일반적으로 Fowler가 트랜잭션 스크립트를 호출하는 모든 것을 조율하는 어딘가에 큰 방법이 있음을 의미합니다. 게터를 사용하기 전에 스스로에게 물어보십시오. 왜 객체에서이 데이터를 요구합니까? 왜이 기능이 객체의 메소드에 있지 않습니까? 이 기능을 구현하는 것이이 클래스의 책임이 아닌 경우, getter를 사용하십시오. Fowler 의 책 [Refactoring] ( martinfowler.com/books/refactoring.html )은 그러한 질문에 대한 휴리스틱에 관한 책입니다.
Jeremy

본 버논 (Vaughn Vernon)은 자신의 저서-도메인 주도 디자인 구현에서 10 장 pp 382의 Demeter and Tell, Do n't Ask에 대해 언급하고있다. 접근 할 수 있다면 살펴볼 가치가있다.
Jeremy

6

데 메터의 법에 따르면, 클래스는 멤버 중 하나를 반환 할 수 있습니까?

예, 가장 확실합니다.

요점을 살펴 보자 .

  • 각 장치는 다른 장치에 대한 정보가 제한적이어야합니다. 현재 장치와 "밀접하게"관련된 장치 만
  • 각 부대는 친구들과 만 대화해야합니다. 낯선 사람과 이야기하지 마십시오.
  • 가까운 친구와 만 대화하십시오.

이 세 가지 모두 하나의 질문을 남깁니다 . 친구는 누구입니까?

무엇을 반환할지 결정할 때, 데메테르의 법칙 또는 최소 지식 원칙 (LoD)은이를 위반할 것을 요구하는 코더를 방어 할 것을 요구하지 않습니다. 그것은 코더가 그것을 강요하지 않도록 지시합니다.

혼란스러워하는 것은 많은 사람들이 setter가 항상 void를 반환해야한다고 생각하는 이유입니다. 시스템 상태를 변경하지 않는 쿼리 (getter)를 작성하는 방법을 허용해야합니다. 기본 명령 쿼리 분리 입니다.

이것은 원하는대로 코드 체인을 자유롭게 탐색 할 수 있다는 것을 의미합니까? 아니요. 서로 연결되어있는 것만 함께 연결하십시오. 그렇지 않으면 체인이 바뀌고 갑자기 물건이 파손될 수 있습니다. 이것은 친구들이 의미하는 것입니다.

긴 체인을 설계 할 수 있습니다. 유창한 인터페이스, iDSL, 많은 Java8 및 오래된 Old StringBuilder는 모두 긴 체인을 구축 할 수있게합니다. 체인의 모든 것이 함께 작동하고 지속적으로 함께 일할 것이기 때문에 LoD를 위반하지 않습니다. 서로 들어 보지 못한 것들을 함께 묶을 때 데메테르를 위반하게됩니다. 친구는 체인을 계속 작동 시키겠다고 약속 한 사람들입니다. 친구의 친구는하지 않았다.

팩토리 및 빌더 클래스와 같이 객체를 반환하도록 특별히 지정된 클래스 외에도 클래스의 속성 중 하나에 의해 유지되는 객체 또는 데 메타 법칙을 위반하는 객체를 반환하는 방법은 괜찮습니다 (1) ?

이것은 데메테르를 위반할 수있는 기회를 만듭니다. 이것은 위반이 아닙니다. 이것은 반드시 나쁘지는 않습니다.

그리고 그것이 데 미터 법칙을 위반한다면, 반환 된 객체가 데이터를 나타내는 불변의 객체인지 여부와이 데이터에 대한 게터 만 포함하는 것이 중요합니까 (2)?

불변은 좋지만 여기서는 관련이 없습니다. 더 긴 체인을 통해 물건을 얻는 것이 더 나아지지는 않습니다. 더 나은 점은 사용하는 것과 분리하는 것입니다. 사용중인 경우 매개 변수로 필요한 것을 요청하십시오. 낯선 사람의 게터를 파헤쳐 서 사냥하지 마십시오.

의사 코드에서 :

데메테르의 법칙은 위와 같은 패턴을 금지한다고 생각합니다. 법을 위반하지 않고 doSomethingElse ()를 호출 할 수 있도록하려면 어떻게해야합니까 (3)?

내가 이야기하기 전에 x.doSomethingElse(a)당신이 기본적으로 쓴 것을 이해하십시오

b.getA().doSomething()

이제 LoD는 도트 카운팅 연습이 아닙니다 . 그러나 체인을 만들 때 A(를 사용하여 B) 얻는 방법을 알고 있고를 사용하는 방법을 알고 있다고 말합니다 A. 글쎄 지금 AB당신은 단지 그들을 함께 결합하기 때문에 더 가까운 친구가 될 수 있었다.

만약 당신이 무언가를 줄 것을 요청했다면 당신이 A사용할 수 A있고 그것이 어디에서 왔는지 걱정하지 않았을 것이고 B, 행복하고 삶 A에서 오는 것에 대한 집착이없는 삶을 살 수있을 것 B입니다.

x.doSomethingElse(a)어디에서 x왔는지에 대한 세부 사항 이 없으면 LoD는 그것에 대해 전혀 말할 것이 없습니다.

LoD는 건설과의 사용을 분리 할 수 있습니다 . 그러나 종교적으로 모든 객체를 친숙하지 않은 것으로 취급하면 정적 메소드로 코드를 작성해야 할 것입니다. 이런 방식으로 놀랍도록 복잡한 객체 그래프를 만들 수 있지만 결국 작업을 시작하려면 메소드를 호출해야합니다. 친구가 누구인지 결정하기 만하면됩니다. 그로부터 멀어지지 않습니다.

따라서 클래스는 LoD 하의 멤버 중 하나를 반환 할 수 있습니다. 그렇지 않은 경우, 일부 클라이언트는 사용하기 전에 클래스를 가져 와서 해당 클래스에 연결하려고 시도 할 수 있기 때문에 해당 멤버가 클래스에 친숙한 지 명확히해야합니다. 반환하는 것이 항상 해당 사용을 지원해야하기 때문에 중요합니다.

이것이 단순히 우려되지 않는 많은 경우가 있습니다. 컬렉션은이를 사용하는 모든 것과 친구가되기 때문에이를 무시할 수 있습니다. 마찬가지로 가치있는 대상은 모든 사람에게 친숙합니다. 그러나 주소를 추출 해야하는 직원 객체를 요구하는 주소 유효성 검사 유틸리티를 작성한 경우 주소를 요청하기 만하면 직원과 주소가 모두 같은 라이브러리에서 온 것이기를 희망합니다.


유창한 인터페이스는 친숙 함이라는 개념 때문에 LOD에서 예외가 아닙니다 ... 유창한 인터페이스에서 체인의 각 메소드는 모두 자기 자신을 반환하기 때문에 동일한 객체에서 호출됩니다. settings.darkTheme().monospaceFont()에 대한 구문 설탕입니다settings.darkTheme(); settings.monospaceFont();
Jonah

3
@Jonah 자아를 돌려주는 것은 최고의 친근감입니다. 모든 유창한 인터페이스가 아닙니다. 많은 iDSL은 또한 유창하며, 다른 지점에서 사용 가능한 방법을 변경하기 위해 다른 유형을 반환합니다. 고려 jOOQ , 단계 빌더 , 마법사 빌더 , 내 자신의 악몽의 괴물 빌더 . 유창한 스타일입니다. 패턴이 아닙니다.
candied_orange

2

객체를 반환하도록 특별히 지정된 클래스 외에 [...]

나는이 주장을 잘 이해하지 못한다. 모든 오브젝트는 메시지 호출의 결과로 오브젝트를 리턴 할 수 있습니다. 특히 "모든 것을 대상으로"생각한다면.

그러나 클래스는 "새로운"메시지를 보내어 자신 만의 종류의 새 개체를 인스턴스화 할 수있는 유일한 개체입니다 (또는 가장 일반적인 OOP 언어의 경우 새 키워드로 대체 됨)


어쨌든, 귀하의 질문에 관해서는 다른 곳에서 사용하기 위해 속성 중 하나를 반환하는 객체에 대해 다소 의심 스럽습니다. 이 객체는 해당 상태 또는 구현 세부 정보에 대한 정보를 유출 할 수 있습니다.

그리고 C 객체가 B의 구현 세부 사항에 대해 알기를 원하지 않을 것입니다. 이것은 C 객체 관점에서 A 객체에 대한 원하지 않는 종속성입니다.

따라서 A를 알면서 C가 LOD와 관계없이 자신이해야 할 일에 대해 너무 많이 알지 못하는지 스스로에게 물어볼 수 있습니다.

예를 들어, 항목 중 하나에 대한 수집 객체를 요청하는 것이 적절하고 Demeter 규칙을 위반하지 않는 것이 합법적 일 수있는 경우가 있습니다. 반면에 Book 객체에 SQLConnection 객체를 요청하는 것은 위험하므로 피해야합니다.

LOD는 많은 프로그래머가 작업을 수행하기 전에 객체에 정보를 요청하는 경향이 있기 때문에 존재합니다. LOD는 때때로 (항상 그런 것은 아니지만) 우리의 사물이 너무 많은 일을함으로써 너무 복잡하게 만든다는 사실을 상기시켜줍니다. 때때로 우리는 다른 대상에게 우리를 위해 일을하도록 요청해야합니다. LOD는 우리가 물체의 행동을 어디에 두는 지에 대해 두 번 생각하게합니다.


0

팩토리 및 빌더 클래스와 같이 객체를 반환하도록 특별히 지정된 클래스 외에도 메소드가 객체를 반환해도 괜찮습니까?

왜 그렇지 않습니까? 클래스가 객체를 반환하도록 의도되었거나 객체를 전혀 반환해서는 안된다는 것을 동료 프로그래머에게 알리는 것이 좋습니다. 모든 것이 객체 인 언어에서는 아무것도 반환 할 수 없으므로 옵션이 심각하게 제한됩니다.


예는 암시 b.getA().doSomething()x.doSomethingElse(b.getA())
적에

A a = b.getA(); a.DoSomething();-다시 한 점으로.
Robert Harvey

2
@ user2180613-질문 b.getA().doSomething()하고 x.doSomethingElse(b.getA())싶을 때 명시 적으로 질문해야합니다. 말 그대로 귀하의 질문은에 관한 것 같습니다 this.b.getA().
David Hammen 2016 년

1
으로 A의 구성원이 아닌 C,하지에 생성 C및 제공하지 C, 그것을 사용하는 것 같습니다 AC(어떤 방식으로하는) 디테일 정도를 위반합니다. 점을 세는 것은 LoD에 관한 것이 아니라 단지 예시적인 도구 일뿐입니다. Driver().getCar().getSteeringWheel().getFrontWheels().toRight()각각 하나의 점을 포함하는 별도의 문장 으로 변환 할 수 있지만 LoD 위반은 여전히 ​​존재합니다. 이 포럼의 많은 게시물에서 실제 행동을 구현하는 객체에 작업 수행을 위임해야한다는 내용을 읽었습니다 tell don't ask. 반환 값은 그와 충돌하는 것 같습니다.
user2180613 2016 년

1
나는 그것이 사실이라고 생각하지만 질문에 대답하지 않습니다.
user2180613

0

하고있는 일에 따라 다릅니다. 수업의 회원을 공개하는 경우,이 수업의 회원이거나 해당 회원의 유형이 비즈니스 인 사람이 아닌 경우에는 좋지 않습니다. 그런 다음 해당 멤버의 유형과 멤버를 반환하는 함수의 유형을 변경하면 모든 사람이 코드를 변경해야합니다.

반면에, 클래스의 인터페이스에 잘 알려진 클래스 A의 인스턴스를 제공한다고 명시되어 있다면 괜찮습니다. 운이 좋으면 반원을 제공하여이 일을 할 수 있다면 좋을 것이다. 해당 멤버를 A에서 B로 변경 한 경우 A를 반환하는 메서드를 다시 작성해야합니다. 이제 분명히 하나의 라이너 반환 대신 하나를 생성하고 사용 가능한 데이터로 채워야합니다. 회원. 수업의 인터페이스는 변경되지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.