객체 지향 디자인의 느슨한 결합


16

나는 GRASP를 배우려고 노력 중이며 로우 커플 링에 대한 설명 (이 페이지 3 )을 발견했으며 이것을 발견 했을 때 매우 놀랐습니다.

클래스 의 메소드 addTrack를 고려하십시오. Album가능한 두 가지 메소드는 다음과 같습니다.

addTrack( Track t )

addTrack( int no, String title, double duration )

커플 링을 줄이는 방법은 무엇입니까? 앨범 클래스를 사용하는 클래스는 트랙 클래스를 알 필요가 없기 때문에 두 번째는 않습니다. 일반적으로 메소드에 대한 매개 변수는 기본 유형 (int, char ...) 및 java. * 패키지의 클래스를 사용해야합니다.

나는 이것에 반대하는 경향이있다. 나는 여러 가지 이유로 인해 addTrack(Track t)더 낫다고 믿는다 addTrack(int no, String title, double duration).

  1. 가능한 한 적은 수의 매개 변수를 사용하는 것이 좋습니다 (Bob Uncle Bob의 Clean Code에 따르면 바람직하지 않거나 경우에 따라 2, 특별한 경우에는 3, 리팩터링이 필요한 경우-홀리 규칙이 아닌 권장 사항 임) .

  2. 경우 addTrack인터페이스하는 방법이며, 요구 사항이이 것을 필요 Track자세한 내용은 (예를 들어 연도 또는 장르)를해야하고 인터페이스를 변경해야하는 등 방법이 다른 매개 변수 지원을해야한다고.

  3. 캡슐화가 깨졌습니다. addTrack인터페이스에 있으면 의 내부를 알 수 없습니다 Track.

  4. 실제로 많은 매개 변수와 함께 두 번째 방식으로 더 연결됩니다. 가정 no매개 변수를 요구하는 것은에서 변경할 수 intlong이상이 있기 때문에 MAX_INT트랙 (또는 어떤 이유) 다음 두 Track방법이 될 경우 상태 및 방법의 필요성이 변경되는 addTrack(Track track)경우에만이 Track변경됩니다.

모든 4 가지 주장은 실제로 서로 연결되어 있으며, 그 중 일부는 다른 것의 결과입니다.

어떤 접근법이 더 낫습니까?


2
교수 또는 트레이너가 작성한 문서입니까? 제공 한 링크의 URL을 기준으로 클래스에 대한 링크 인 것처럼 보이지만 문서를 만든 사람에 대한 크레딧은 없습니다. 이것이 수업의 일부인 경우 문서를 제공 한 사람에게 이러한 질문을하는 것이 좋습니다. 그건 그렇고 당신의 추론에 동의합니다-앨범 클래스가 본질적으로 트랙 클래스에 대해 알고 싶어한다는 것은 분명합니다.
Derek

솔직히, "Best Practices"에 대해 읽을 때마다 나는 소금 한 알을 가져갑니다!
AraK

@Derek Google에서 "grasp patterns example"을 검색하여 문서를 찾았습니다. 나는 그것을 쓴 사람은 없지만 대학 출신이기 때문에 믿을 만하다고 믿는다. 주어진 정보를 기반으로 소스를 무시하는 예를 찾고 있습니다.
m3th0dman 2016 년

4
@ m3th0dman "하지만 그것이 대학에서 왔기 때문에 그것이 믿을 만하다고 생각합니다." 대학 출신이기 때문에 신뢰할 수 없다고 생각합니다. 나는 소프트웨어 개발의 모범 사례에 대해 이야기하는 다년간 프로젝트에서 일하지 않은 사람을 믿지 않습니다.
AraK

1
@AraK 신뢰성은 의심 할 여지가 없습니다. 그것이 제가 여기서하고있는 일입니다.
m3th0dman 2016 년

답변:


15

글쎄, 처음 세 가지는 실제로 커플 링 이외의 다른 원리에 관한 것입니다. 종종 충돌하는 디자인 원칙 간의 균형을 유지해야합니다.

네 번째 요점 커플 링에 관한 것이며, 나는 당신에게 강력하게 동의합니다. 커플 링은 모듈 간의 데이터 흐름에 관한 것 입니다. 데이터가 유입되는 컨테이너의 유형은 중요하지 않습니다. 필드 대신 필드를 더블 Track로 전달한다고해서 전달할 필요는 없습니다. 모듈은 여전히 ​​동일한 양의 데이터를 공유해야하며 여전히 동일한 양의 커플 링을 가지고 있습니다.

또한 시스템의 모든 커플 링을 집계로 간주하지 못했습니다. Track클래스를 도입하면 두 개의 개별 모듈 사이에 또 ​​다른 종속성이 추가 되지만 시스템 의 커플 링을 크게 줄일 수 있습니다.

예를 들어 "재생 목록에 추가"버튼과 Playlist개체 를 고려하십시오 . Track두 객체 만 고려하면 객체를 도입하여 커플 링을 증가시킬 수 있습니다. 이제 두 개의 클래스 대신 세 개의 상호 의존 클래스가 있습니다. 그러나 이것이 시스템 전체가 아닙니다. 또한 트랙을 가져오고, 트랙을 재생하고, 트랙을 표시하는 등의 작업을 수행해야합니다. 해당 믹스에 하나 이상의 클래스를 추가하는 것은 무시할 수 있습니다.

이제 로컬이 아닌 네트워크를 통해 트랙을 재생하기위한 지원을 추가해야합니다. NetworkTrack동일한 인터페이스를 따르는 객체 를 만들면 됩니다. Track객체가 없으면 다음 과 같은 곳 어디에서나 함수를 만들어야합니다.

addNetworkTrack(int no, string title, double duration, URL location)

따라서 커플 링을 효과적으로 두 배로 늘릴 수 있기 때문에 네트워크 관련 사항을 신경 쓰지 않는 모듈조차도 계속 추적해야합니다.

리플 효과 테스트는 실제 커플 링 양을 결정하는 좋은 방법입니다. 우리가 우려하는 것은 변화가 영향을 미치는 장소를 제한하는 것입니다.


1
+ 프리미티브에 대한 연결은 슬라이스 방식에 관계없이 여전히 연결됩니다.
JustinC

URL 추가 옵션 / 리플 효과를 언급하면 ​​+1입니다.
user949300

4
+1 이것에 대한 흥미로운 독서는 또한 원시적 유형의 사용이 실제로 가치 객체 를 해결책으로 삼는 원시적 강박 관념 "냄새"로 여겨지는 DIP 의 의존성 역전 원리에 대한 논의 일 것이다 . 나에게 들리는 것처럼 들리는 기본 유형의 전체 조글이있는 Track 객체를 전달하는 것이 더 낫습니다 ... 특정 클래스와의 종속성 / 커플 링을 피하려면 인터페이스를 사용하십시오.
Marjan Venema 2016 년

전체 시스템 커플 링과 모듈 커플 링의 차이점에 대해 잘 설명했기 때문에 허용 된 답변입니다.
m3th0dman 2016 년

10

내 추천은 :

사용하다

addTrack( ITrack t )

그러나 그것이 ITrack구체적인 클래스가 아닌 인터페이스 인지 확인하십시오 .

앨범은 ITrack구현 자의 내부를 모릅니다 . 에 의해 정의 된 계약에만 연결됩니다 ITrack.

이것이 가장 적은 양의 커플 링을 생성하는 솔루션이라고 생각합니다.


1
Track은 단순한 빈 / 데이터 전송 객체라고 생각합니다. 여기에는 필드와 getter / setter 만 있습니다. 이 경우 인터페이스가 필요합니까?
m3th0dman 2016 년

6
필수? 아마 아닙니다. 암시합니다. 트랙의 구체적인 의미는 발전 할 수 있고 발전 할 것이지만 소비층이 필요로하는 것은 아닐 것입니다.
JustinC

2
@ m3th0dman 항상 concretion이 아닌 추상화에 의존합니다. 그것은 Track멍청하거나 똑똑한 것에 관계없이 적용됩니다 . Trackconcretion입니다. ITrack인터페이스는 추상화입니다. 그렇게하면 앞으로을 준수하는 한 다른 종류의 트랙을 가질 수있게됩니다 ITrack.
Tulains Córdova 2016

4
아이디어에 동의하지만 'I'접두사를 잃습니다. 24 페이지, Robert Martin의 Clean Code에서 : "오늘날의 레거시 뭉치에서 흔히 볼 수있는 앞의 I는 최악의 정보를 너무 많이 산만하게하는 것입니다. 상호 작용."
Benjamin Brumfield

1
@BenjaminBrumfield 당신이 맞아요. 나는 명확성을 위해 대답을 남길 것이지만 접두사도 좋아하지 않습니다.
Tulains Córdova 2016

4

두 번째 예제 방법 은 트랙 객체를 인스턴스화하고 현재 앨범 객체에 저장하기 때문에 커플 링을 증가시킬 가능성이 높다고 주장 합니다. (위의 의견에서 제안했듯이 앨범 클래스는 트랙 클래스 개념을 가지고 있다고 가정합니다.)

첫 번째 예제 방법에서는 Track이 Album 클래스 외부에서 인스턴스화되고 있다고 가정하므로 최소한 Track 클래스 의 인스턴스화 가 Album 클래스에 연결되어 있지 않다고 가정 할 수 있습니다 .

모범 사례에 따라 두 번째 클래스를 참조하는 클래스가 하나도 없다고 제안하면 객체 지향 프로그래밍 전체가 창 밖으로 나옵니다.


다른 클래스에 대한 암시 적 참조가 명시 적 참조를 갖는 것보다 어떻게 결합되어 있는지 알 수 없습니다. 어느 쪽이든, 두 클래스는 연결됩니다. 나는 커플 링을 명시 적으로하는 것이 낫다고 생각하지만, 그것이 "더 많은"커플 링이라고 생각하지는 않는다.
TMN

1
@TMN, 추가 커플 링은 두 번째 예제가 내부적으로 새 Track 객체를 만드는 것을 의미합니다. 객체의 인스턴스화는 트랙 객체를 앨범 객체의 일종의 목록에 추가 해야하는 방법에 결합됩니다 (단일 책임 원칙 위반). 트랙이 생성되는 방식을 변경해야하는 경우 addTrack () 메서드도 변경해야합니다. 첫 번째 예제의 경우에는 그렇지 않습니다.
Derek

3

커플 링은 코드에서 얻는 여러 측면 중 하나 일뿐입니다. 커플 링을 줄임으로써 반드시 프로그램을 개선 할 필요는 없습니다. 일반적으로 이것이 가장 좋은 방법이지만이 특별한 경우 왜 Track알 수 없습니까?

Track전달 되는 클래스를 사용하면 Album코드를 쉽게 읽을 수 있지만 더 중요한 것은 정적 매개 변수 목록을 동적 객체로 바꾸는 것입니다. 이는 궁극적으로 인터페이스를 훨씬 더 역동적으로 만듭니다.

캡슐화가 고장 났다고 언급하지만 그렇지 않습니다. Album의 내부를 알아야하며 Track, 객체를 사용하지 않은 경우 객체를 모두 동일 Album하게 사용하려면 객체에 전달 된 모든 정보를 알아야합니다. 호출자는 객체 Track를 구성해야하기 때문에 내부의 Track내용도 알아야하지만 호출자가이 정보를 메소드에 직접 전달한 경우이 정보를 모두 동일하게 알고 있어야합니다. 다시 말해서, 캡슐화의 장점이 객체의 내용을 알지 못한다면,이 정보를 그대로 Album사용해야하기 때문에이 경우에는 사용될 수 없었습니다 Track.

사용하고 싶지 않은 곳 은 발신자가 액세스하기를 원하지 않는 내부 논리가 포함되어있는 Track경우 Track입니다. 다시 말해서, Album라이브러리를 사용하는 프로그래머가 사용해야하는 클래스라면,이를 사용 Track하여 데이터베이스에 유지하기 위해 메소드를 호출하기를 원치 않을 것 입니다. 이것의 진정한 문제는 인터페이스가 모델과 얽혀 있다는 사실에 있습니다.

문제를 해결하려면 Track인터페이스 구성 요소와 논리 구성 요소로 분리하여 두 개의 개별 클래스를 작성해야합니다. 발신자 Track에게는 정보를 보유하고 사소한 최적화 (계산 된 데이터 및 / 또는 기본값)를 제공하는 가벼운 클래스가됩니다. 내부 Album에서는 TrackDAO정보를 Track데이터베이스에 저장하는 것과 관련된 무거운 작업을 수행하기 위해 명명 된 클래스를 사용 합니다.

물론 이것은 단지 예일뿐입니다. 나는 이것이 당신의 경우가 아니라고 확신하므로 무죄를 자유롭게 사용하십시오 Track. 클래스를 만들 때 호출자를 명심하고 필요한 경우 인터페이스를 작성해야합니다.


3

둘 다 맞다

addTrack( Track t ) 

(이미 argumented로) 동안

addTrack( int no, String title, double duration ) 

사용하는 코드가 클래스 가 있다는 것을 알 필요가 없기 때문에 결합적습니다 . 예를 들어, 호출 코드를 업데이트하지 않고도 트랙 이름을 바꿀 수 있습니다.addTrackTrack

더 읽기 쉽고 유지 가능한 코드에 대해 이야기하는 동안 기사는 커플 링 에 대해 이야기하고 있습니다. 덜 결합 된 코드는 구현하고 이해하기가 반드시 쉽지는 않습니다.


논증 4 참조; 나는 두 번째 것이 어떻게 덜 결합되어 있는지 알지 못합니다.
m3th0dman 2016 년

3

낮은 커플 링은 커플 링 없음을 의미하지 않습니다 . 어딘가에서 코드베이스의 다른 곳에있는 객체에 대해 알아야 할 것이 있으며, "커스텀"객체에 대한 의존도를 줄일수록 코드를 변경해야하는 이유가 더 많아집니다. 저자가 두 번째 기능으로 홍보하는 것은 결합이 적지 만 객체 지향이 적습니다. 이는 객체 지향 설계 방법론 이라는 GRASP의 전체 아이디어와 상반됩니다 . 요점은 시스템을 객체 모음과 상호 작용으로 디자인하는 방법입니다. 피하는 것은 대신 자전거를 타야한다는 말로 차를 운전하는 법을 가르치는 것과 같습니다.

대신, 적절한 방법은 "느슨한 커플 링"이론 인 콘크리트 물체 에 대한 의존성을 줄이는 것 입니다. 방법이 알아야 할 명확한 콘크리트 유형이 적을수록 좋습니다. 그 문장에 의해서만, 첫 번째 옵션은 실제로 덜 결합되어 있습니다. 더 간단한 유형을 취하는 두 번째 방법은 모든 더 간단한 유형에 대해 알아야하기 때문입니다. 물론 내장되어 있고 메소드 내부의 코드가 신경 써야 할 수도 있지만 메소드의 서명과 메소드의 호출자는 가장 확실하지 않습니다 . 개념적 오디오 트랙과 관련된 이러한 매개 변수 중 하나를 변경하려면 트랙 객체 (객체의 지점 인 캡슐화)에 포함 된 경우와 별도로 별도의 경우 더 많은 변경이 필요합니다.

한 걸음 더 나아가서 Track이 동일한 작업을 더 잘 수행 한 것으로 대체 될 것으로 예상되면, 필수 기능을 정의하는 인터페이스는 ITrack 순서 일 것입니다. 이를 통해 "AnalogTrack", "CdTrack"및 "Mp3Track"과 같은 다른 구현을 통해 해당 형식에보다 구체적인 추가 정보를 제공하면서도 개념적으로 "트랙"을 나타내는 ITrack의 기본 데이터 노출을 제공 할 수 있습니다. 유한 한 오디오 부분. Track도 마찬가지로 추상 기본 클래스가 될 수 있지만 항상 Track 고유의 구현을 사용해야합니다. BetterTrack으로 다시 구현하면 이제 예상 매개 변수를 변경해야합니다.

따라서 황금률; 프로그램과 코드 구성 요소는 항상 변경해야 할 이유가 있습니다. 새로운 것을 추가하거나 동작을 수정하기 위해 이미 작성한 코드를 편집 할 필요 가 없는 프로그램은 작성할 수 없습니다 . 어떤 방법에서 당신의 목표, (GRASP, SOLID, 다른 약어 나 전문 용어 당신이 생각할 수있는) 것을 확인하는 것입니다 것입니다 시간이 지남에 따라 변경해야 할, 그리고 그 변화가 쉽게 가능한하려면되도록 시스템을 설계 (번역; 적은 수의 코드 줄을 만지고 가능한 한 의도 한 변경 범위를 벗어난 시스템의 다른 영역에는 영향을 미치지 않습니다). 변화에 가장 가능성이 무엇이 적절한 예는 트랙 addTrack은 () 또는 약, 신경 쓰지 않을 수 있다는 더 많은 데이터 구성원을 얻을 것입니다 없습니다 해당 트랙이 BetterTrack으로 대체됩니다.

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