이와 같은 코드가 "기차 사고"입니까 (Demeter of Law)


23

내가 작성한 일부 코드를 탐색하면서 다음과 같은 구성을 보았습니다. 언뜻보기에 충분히 깨끗해 보입니다. 그렇습니다. 실제 코드 getLocation()에서이 메서드는 좀 더 구체적인 이름을 가지고 있는데,이 이름은 정확히 어떤 위치에 도달하는지 더 잘 설명합니다.

service.setLocation(this.configuration.getLocation().toString());

이 경우 service알려진 형식의 인스턴스 변수이며 메서드 내에서 선언됩니다. this.configuration클래스 생성자에 전달되어 제공되며 특정 인터페이스를 구현하는 클래스의 인스턴스입니다 (공개 getLocation()메서드 를 요구함 ). 따라서 표현식의 리턴 유형 this.configuration.getLocation()이 알려져 있습니다. 특히이 경우, A는 java.net.URL반면 service.setLocation()원한다 String. 두 가지 유형의 문자열과 URL 직접 호환되지 않기 때문에, 어떤 변환의 종류는 둥근 구멍에 스퀘어 페그에 맞게 필요합니다.

그러나 , 인용으로 데메테르의 법칙에 따른 면도 코드 하는 방법 F 급에서 C는 단지 메소드를 호출한다 C 에 의해 생성되거나 인수로서 전달 된 객체 F , 그리고 인스턴스 변수 유지 오브젝트 C . 그 이상은 ( toString()메소드 호출의 결과로 생성 된 임시 객체를 고려하지 않는 한 위의 특정 경우 의 최종 결과입니다.이 경우 전체 법률이 무례한 것으로 보입니다) 허용되지 않습니다.

나열된 제약 조건을 감안할 때 위와 같은 호출을 권장하지 않거나 허용하지 않는 이유가 있습니까? 아니면 너무 지나치게 질긴 것입니까?

객체 에 의해 URLToString()호출 toString()된 메소드 URL(예 :에 의해 반환 된 메소드 getLocation())를 매개 변수로 전달하고 결과를 반환 하는 메소드를 구현하려는 경우 호출 을 래핑하여 getLocation()정확히 동일한 결과를 얻을 수 있습니다. 효과적으로, 나는 전환을 한 단계 바깥쪽으로 옮기려고합니다. 어떻게 든 받아 들일 수 있습니까? (이 보인다 않는 모든 조금 주위에 이동 것들이기 때문에 그것이 어떤 차이 어느 쪽이든을하지 않도록, 직관적, 나에게. 그러나, 데메테르의 법칙의 문자로가는 인용, 그것은 내가 때문에 허용 될 수 그런 다음 매개 변수에서 직접 함수로 작동합니다.)

이것이 toString()표준 유형을 호출 하는 것보다 약간 더 이국적인 것에 관한 것이면 어떤 차이가 있습니까?

대답 할 때 service변수가 있는 유형의 동작 또는 API를 변경하는 것은 실용적이지 않습니다. 또한 논쟁의 여지가 있기 때문에 반환 유형을 변경하는 getLocation()것도 비현실적 이라고 가정 해 봅시다 .

답변:


34

여기서 문제 의 서명입니다 setLocation. 그것은 것 stringly 입력 .

정교하게 : 왜 기대 String할까요? A String모든 종류의 텍스트 데이터를 나타냅니다 . 유효한 위치 이외의 다른 것일 수 있습니다.

실제로 이것은 질문이됩니다. 위치는 무엇입니까? 어떻게 내가 코드로 보지 않고 알아? 그것은라면 URL내가 더 많은 것을이 방법이 기대에 대해 알고있는 것보다.
아마 커스텀 클래스가되는 것이 더 합리적 일 것 Location입니다. 좋아, 나는 처음에 그것이 무엇인지 알지 못하지만 어느 시점에 (아마도 작성하기 전에이 this.configuration.getLocation()메소드가 무엇을 반환하는지 알아내는 데 잠시 시간이 걸릴 것입니다).
물론 두 경우 모두 예상 할 수있는 다른 곳을 찾아야합니다. 그러나 후자의 경우 이해하는 Location것이 무엇인지 , 나는 API를 사용할 수 있으며, 전자의 경우 이해하는 String것이 무엇인지 (예상 가능한) API가 무엇을 기대하는지 여전히 알 수 없습니다.

가능성이 낮은 시나리오에서 위치는 모든 종류의 텍스트 데이터 입니다. 텍스트 표현이있는 모든 종류의 데이터로 해석합니다 . 사실, 주어 ObjecttoString이 코드의 클라이언트에서 믿음의 꽤 도약을 요구하지만, 방법을, 당신은 그와 함께 갈 수 있습니다.

또한 이것은 당신이 말하는 Java이며, 디자인에 의해 기능이 거의 없다는 것을 고려해야합니다. 그것이 실제로 toString끝에 전화하도록 강요하는 것 입니다.
예를 들어 정적으로 유형이 지정된 C # 을 사용하면 암시 적 캐스트에 대한 동작을 정의 하여 실제로 해당 호출을 생략 할 수 있습니다 .
Objective-C와 같이 동적으로 유형이 지정된 언어에서는 값이 문자열처럼 동작하기 만하면 모든 사람이 행복하기 때문에 변환도 필요하지 않습니다.

toString자바의 명시 성 요구에 의해 실제로 생성되는 잡음보다 마지막 호출이 호출 이 적 다고 주장 할 수있다 . 당신은 어떤 Java 객체가 가지고 있는 메소드를 호출하고 있으므로, 실제로 "먼 거리의 유닛"에 대한 지식을 인코딩하지 않으므로 최소한의 지식 원칙을 위반하지 않습니다. getLocation반환 값에 관계없이 toString메소드 가없는 방법 은 없습니다 .

그러나 실제로 가장 자연스러운 선택이 아니면 문자열을 사용하지 마십시오 (또는 언어를 사용하지 않는 한 열거 형이없는 경우도 있습니다).


나는 당신에게 동의하는 경향이 있지만, 그는 이미 그가 서비스 API를 수정할 수 없다고 말했다.
jhocking

1
@jhocking : 그는 할 수 없다고 말하지 않았습니다. 그는 그것이 비현실적이라고 말했다. 동의하지 않습니다. API 디자인의 결함을 해결하는 코드에 모범 사례를 적용하는 것은 그러한 해결 방법이 유일한 옵션 일 경우에만 의미가 있습니다. 그러나 여기서는 API를 바로 설정하는 것이 가장 좋습니다. 결함을 제거하는 것이 항상 해결하는 것이 좋습니다.
back2dos

어쨌든 +1은 "명시
성에

1
"문자열 형식"인 경우에도이 +1을 제공합니다. 내 코드에서 가능한 한 의미 / 의도를 나타내는 유형을 전달하려고 시도하지만 타사 라이브러리 및 API로 작업 할 때 때로는 해당 API 작성자가 결정한 내용을 사용하는 경우가 있습니다. 그리고 IIRC, 위치 실제로 "모든 종류의 텍스트 데이터"가 될 수 있지만, 내가 작업하고있는 구현에서 URL이 아닌 위치는 전혀 의미가 없습니다.
CVn

1
API 변경에 관해서는; 이 때문에 거의 항상, 컴퓨터 소프트웨어입니다 가능한 상황을 타개 할 수 있습니다. (아무것도 없다면 항상 추상화 계층을 작성할 수 없습니다.) 그러나 때로는 그렇게하려면 정당하고 장단하기에는 너무 많은 노력이 필요합니다. 그런 다음 그러한 변경을 완벽하게 수행 할 수는 있지만 여전히 비현실적입니다.
CVn

21

데 메터의 법칙은 디자인 지침이며 종교적으로 따라야 할 법이 아닙니다.

클래스가 충분히 분리되어 있다고 생각 this.configuration.getLocation()되면, 특히 말했듯이 API의 다른 부분을 변경하는 것이 비현실적 인 경우 라인에 아무런 문제가 없습니다 .

나는 그가 원하는 것을 제 시간에 제공하는 한 Demeter의 법칙을 조각으로 만들더라도 고객이 완벽하게 행복 할 것이라고 확신합니다. 이는 나쁜 소프트웨어를 만드는 변명이 아니라 소프트웨어를 개발할 때 실용적임을 상기시키는 것입니다.


1
this.configuration알려진 인터페이스 유형의 인스턴스 변수 이므로 엄격한 해석에 따라 해당 인터페이스에 의해 정의 된 메소드를 호출하는 것이 좋습니다. 예, KISS, SOLID, YAGNI 등과 같은 지침이라는 것을 알고 있습니다. 일반적인 소프트웨어 개발에서 실제로 "법칙"(법적인 의미로)이 있다면 그 수가 거의 없습니다.
CVn

4
실용주의 +1 ;-)
Treb

1
Dementer의 법칙은 이와 같은 경우에도 적용되어야한다고 생각하지 않습니다. 구성은 기본적으로 컨테이너입니다. 모든 API에서 컨테이너 내부를 살펴 본 적이 정상적이고 예상되는 동작입니다.
Loren Pechtel

2

이와 같이 코드를 작성하지 않는다고 생각할 수있는 유일한 것은 this.configuration.getLocation()null을 반환 하면 어떻게됩니까? 이 코드를 사용하는 주변 코드 및 대상 고객에 따라 다릅니다. 그러나 마르코가 말했듯이-데메테르의 법칙은 경험의 법칙입니다. 따르는 것이 좋지만 불필요하게 등을 꺾지 마십시오.


경우 this.configuration.getLocation()는 null을 반환, 우리는 하나 (A)는 가장 가능성까지, 또는 (b) 뭔가 치명적인 내가 할 경우에, 중간에 일어난 것을 가지고 못했네 원하는 코드가 실패 할 수 있습니다. 따라서 이것은 일반적으로 유효한 지점이지만,이 경우에는 적용되지 않는다고 말하는 것이 안전합니다. 또한이 모든 것을 둘러싼 것은 예외 처리기가 그러한 종류의 예기치 않은 실패를 처리하도록 특별히 설계되었습니다.
CVn

2

Demeter of Law를 엄격히 따르면 구성 객체에 다음과 같은 메소드를 구현해야합니다.

function getLocationAsString() {
  return getLocation().toString();
}

그러나 개인적으로 나는 이것이 작은 상황이기 때문에 귀찮게하지 않을 것이며, 어쨌든 API로 인해 손을 묶을 수는 없습니다. 프로그래밍 규칙은 선택시해야 할 일에 대한 것이지만 때로는 선택이 없을 수도 있습니다.


1
c2.com/cgi/wiki?TrainWreck "원하는 행동을 나타내는 방법을 만들고 고객에게 무엇을해야하는지 알려주는 방법을 제시하십시오 . 이것은"말하지 말아라 "원칙을
따릅니다
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.