여러 계층을 통해 인스턴스를 전달하는 것은 나쁜 습관입니까?


60

프로그램 디자인에서 종종 여러 클래스를 통해 객체 인스턴스를 전달해야하는 시점에 도달합니다. 예를 들어, 오디오 파일을로드 한 다음 플레이어로 전달하는 컨트롤러가 있고 플레이어가 playerRunnable에 전달하면 다른 곳으로 다시 전달됩니다. 그것을 피하는 방법을 알아라. 아니면 괜찮습니까?

편집 : 아마도 파일을 나중에로드 할 수 있기 때문에 플레이어 예제가 가장 좋지 않을 수도 있지만 다른 경우에는 작동하지 않습니다.

답변:


54

다른 사람들이 언급했듯이, 이것은 반드시 나쁜 습관은 아니지만 레이어의 우려를 분리하지 않고 레이어간에 레이어 별 인스턴스를 전달하지는 않습니다. 예를 들어 :

  • 데이터베이스 개체는 상위 계층으로 전달해서는 안됩니다. DAL에서 DataAdapter를 사용하고 DTO 또는 데이터 집합을 만들어 전달하는 대신 .NET의 DataAdapter 클래스, DB 액세스 클래스를 사용하여 UI 계층으로 전달하는 프로그램을 보았습니다 . DB 액세스는 DAL의 도메인입니다.
  • 물론 UI 객체는 UI 레이어로 제한되어야합니다. 다시 말하지만, 콘텐츠의 배열 / DTO 대신에 BL 레이어에 전달 된 사용자 데이터로 채워진 ListBoxes와 계층 적 데이터를 검색 한 DAL 클래스 (특히 내가 좋아하는)로이 위반이 발생했습니다. DB는 계층 적 데이터 구조를 반환하지 않고 TreeView 객체를 생성하고 채운 다음 UI에 다시 전달하여 양식에 동적으로 추가합니다.

그러나 전달하는 인스턴스가 DTO 또는 엔터티 자체 인 경우에는 문제가 없습니다.


1
이것은 충격적인 것처럼 들릴 수 있지만 .NET의 어두운 초기에는 일반적으로 권장되는 관행이며 아마도 대부분의 다른 스택이하는 것보다 낫습니다.
Wyatt Barnett

1
동의하지 않습니다. Microsoft가 Winforms 클라이언트가 DB에 액세스 한 단일 계층 앱의 실행을 승인했으며 DataAdapter가 보이지 않는 컨트롤로 양식에 직접 추가되었지만 OP의 N- 계층 설정과는 다른 특정 아키텍처 일뿐입니다. . 그러나 다중 계층 아키텍처에서는 .NET 이전에도 VB6 / DNA의 경우 DB 개체가 DB 계층에 남아있었습니다.
Avner Shahar-Kashtan

명확히하기 위해 : "데이터 계층"에서 UI에 직접 액세스하는 사람들을 보았습니다 (예 : 목록 상자)? 나는 프로덕션 코드에서 그러한 위반
Simon Whitehead

7
@SimonWhitehead 정확합니다. 구별에 대해 애매한 사람들은 ListBox와 배열 사이에 있고 ListBox를 DTO로 사용했습니다. 다른 사람들에게는 직관적이지 않은 몇 가지 보이지 않는 가정을 깨닫게 된 순간이었습니다.
Avner Shahar-Kashtan

1
@SimonWhitehead-예, VB6 및 VB.NET Framework 1.1 및 2.0 프로그램에서 이러한 괴물을 유지 해야하는 것을 보았습니다. 아주 못 생기고 매우 빠릅니다.
jfrankcarr

15

아직 불변 객체 에 대해 아무도 이야기하지 않았다는 점에 흥미가 있습니다. 나는 모든 다양한 레이어를 통해 불변의 객체를 전달하는 것이 실제로 각 레이어마다 짧은 수명의 객체를 많이 만드는 것이 아니라 좋은 것이라고 주장합니다 .

그의 블로그 에서 Eric Lippert의 불변성에 대한 훌륭한 토론이 있습니다.

반면에 레이어 사이에 가변 객체를 전달하는 것은 나쁜 디자인 이라고 주장합니다 . 본질적으로 주변 레이어가 코드를 손상시키는 방식으로 변경되지 않는다는 약속으로 레이어를 작성하고 있습니다.


13

객체 인스턴스를 전달하는 것이 일반적입니다. 상태 (예 : 인스턴스 변수)를 유지할 필요성을 줄이고 코드를 실행 컨텍스트에서 분리합니다.

체인 아래쪽에서 메서드의 매개 변수 요구 사항이 변경되면 호출 체인을 따라 여러 메서드의 서명을 변경해야하는 경우 리팩토링이 발생합니다. 그러나 리팩토링에 도움이되는 최신 소프트웨어 개발 도구를 사용하면이를 완화 할 수 있습니다.


6
나는 당신이 직면 할 수있는 또 다른 문제는 불변성과 관련이 있다고 말합니다. 개발자가 DTO를 수정 한 프로젝트에서 매우 복잡한 버그를 기억할 수 있습니다. 특정 클래스가 해당 객체에 대한 참조가 있는 유일한 클래스가 아니라는 사실에 대해서는 생각하지 않았습니다 .
Phil

8

어쩌면 사소하지만, 나중에 참조를 추가하거나 나중에 메모리 누수를 일으킬 가능성이있는 계층 중 하나에이 참조를 할당 할 위험이 있습니다.


요점은 맞지만 OP의 용어 ( "객체 인스턴스 전달")에서 나는 값을 전달하거나 (포인터가 아닌) 가비지 수집 환경 (Java, C #, Python, Go,)에 대해 이야기하고 있다는 것을 알고 있습니다. ..).
Mohammad Dehghan

7

코드의 원격 영역에 필요하기 때문에 단순히 객체를 전달하는 경우 선택적으로 적절한 IoC 컨테이너와 함께 제어 및 종속성 주입 디자인 패턴 의 역전을 사용하면 객체 인스턴스 운반과 관련된 문제를 해결할 수 있습니다. 나는 중간 크기의 프로젝트에서 이것을 사용했으며 그것을 사용하지 않고 큰 서버 코드를 작성하는 것을 다시는 고려하지 않을 것입니다.


흥미로운 것 같습니다. 이미 생성자 주입을 사용하고 있으며 고급 구성 요소가 하위 구성 요소를 제어한다고 생각합니다. 인스턴스 운반을 피하기 위해 IOC 컨테이너를 어떻게 사용합니까?
Puckl

나는 대답을 여기에서 찾았다 고 생각한다 : martinfowler.com/articles/injection.html
Puckl

1
참고로 Java로 작업하는 경우 Guice가 정말 좋으며 요청과 같은 항목으로 바인딩 범위를 지정하여 상위 레벨 구성 요소가 범위를 작성하고 인스턴스를 올바른 클래스에 바인딩 할 수 있습니다.
Dave

4

여러 레이어를 통해 데이터를 전달하는 것은 나쁘지 않습니다. 실제로 계층 구조를 위반하지 않고도 계층 시스템이 작동하는 유일한 방법입니다. 문제가 있다는 신호는 목표를 달성하기 위해 동일한 계층의 여러 객체로 데이터를 전달할 때입니다.


3

빠른 답변 : 객체 인스턴스를 전달하는 데 아무런 문제없습니다 . 또한 언급했듯이, 잠재적으로 매달려있는 참조 또는 메모리 누수를 일으킬 수있는 모든 레이어 에서이 참조를 할당하는 것을 건너 뛰는 것이 좋습니다.

프로젝트에서이 방법을 사용하여 계층간에 DTO (데이터 전송 개체)전달 하면 매우 유용한 방법입니다. 또한 dto 객체를 재사용하여 요약 정보처럼 한 번 더 복잡한 것을 구성합니다.


3

나는 주로 웹 UI 개발자이지만 직관적 인 불편 함이 인스턴스 통과에 관한 것이 아니라 컨트롤러와 약간의 절차를 밟고 있다는 사실에 더 가깝습니다. 컨트롤러가이 모든 세부 사항을 땀 나게해야합니까? 오디오를 재생하기 위해 하나 이상의 다른 객체 이름을 참조하는 이유는 무엇입니까?

OOP 디자인에서 나는 상록과 무엇이 변경 될 가능성이 더 큰지에 대해 생각하는 경향이 있습니다. 변경 대상은 더 큰 객체 상자에 넣는 경향이 있으므로 플레이어가 변경되거나 새로운 옵션이 추가 되어도 일관된 인터페이스를 유지할 수 있습니다. 또는 오디오 객체 또는 구성 요소를 도매로 교체하려는 경우가 있습니다.

이 경우 컨트롤러는 오디오 파일을 재생할 필요가 있는지 확인한 다음 일관되게 / 녹색으로 재생하는 방법이 필요합니다. 반면에 오디오 플레이어는 기술과 플랫폼이 변경되거나 새로운 선택이 추가됨에 따라 쉽게 변경 될 수 있습니다. 이러한 모든 세부 사항은 더 큰 복합 개체 인 IMO의 인터페이스 아래에 있어야하며 오디오 재생 방식에 대한 세부 사항이 변경 될 때 컨트롤러를 다시 작성할 필요가 없습니다. 그런 다음 파일 위치와 같은 세부 정보가 포함 된 객체 인스턴스를 더 큰 객체로 전달하면 스와핑이 모두 적절한 컨텍스트 내부에서 이루어집니다.

따라서이 경우 객체 인스턴스가 던져져 버릴 수도 있다고 생각하지 않습니다. 피카드 선장은 엔진 실로 내려 가서 날실 코어를 켜고 다리로 돌아가 좌표를 플로팅 한 다음 쉴드를 켠 후 "펀치-잇"버튼을 누르기 만하면됩니다. 우리를 Warp 9에서 행성 X로 보내십시오. " 승무원이 세부 사항을 정리하도록합니다 그가 그렇게 처리 할 때, 모든 선박의 배치와 모든 작동 방식을 몰라도 함대에있는 모든 선박을 선장 할 수 있기 때문입니다. 그리고 이것이 궁극적으로 가장 큰 OOP 디자인 승리 인 IMO입니다.


2

앱이 그런 종류의 일에 민감한 경우 대기 시간 문제가있을 수 있지만 결국에는 일반적인 디자인입니다.


2

이 문제는 언어에 변수가있는 경우 동적으로 범위가 지정된 변수 또는 스레드 로컬 저장소를 사용하여 해결할 수 있습니다. 이러한 메커니즘을 통해 일부 사용자 정의 변수를 활성화 체인 또는 제어 스레드와 연관시킬 수 있으므로 이러한 값을 다른 코드와 통신 할 수 있도록 코드와 관련이없는 코드로 전달할 필요가 없습니다. 그것들이 필요합니다.


2

다른 답변에서 지적했듯이 이것은 본질적으로 열악한 디자인이 아닙니다. 중첩 클래스와 중첩 클래스 사이에 긴밀한 결합을 만들 수 있지만 참조 중첩이 설계에 가치를 제공하는 경우 결합을 느슨하게하는 것은 유효한 옵션이 아닐 수 있습니다.

가능한 해결책 중 하나는 컨트롤러 클래스에서 중첩 된 참조를 "평평하게"하는 것입니다.

중첩 된 객체를 통해 매개 변수를 여러 번 전달하는 대신 모든 중첩 된 객체에 대한 컨트롤러 클래스 참조를 유지할 수 있습니다.

이것이 정확히 어떻게 구현되는지 (또는 유효한 솔루션인지) 다음과 같은 시스템의 현재 디자인에 달려 있습니다.

  • 너무 복잡하지 않고 컨트롤러에 중첩 된 객체의 일종의 맵을 유지할 수 있습니까?
  • 매개 변수를 적절한 중첩 객체에 전달할 때 중첩 객체가 매개 변수를 즉시 인식 할 수 있습니까? 아니면 중첩 객체를 통과하는 동안 추가 기능이 발생 했습니까?
  • 기타

이것은 GXT 클라이언트의 MVC 디자인 패턴에서 발생한 문제입니다. 우리의 GUI 구성 요소에는 여러 계층에 대한 중첩 된 GUI 구성 요소가 포함되어 있습니다. 모델 데이터가 업데이트되면 적절한 구성 요소에 도달 할 때까지 여러 레이어를 통과하게되었습니다. 새로운 GUI 컴포넌트 클래스가 모델 데이터를 수용하기를 원한다면, 새로운 클래스를 포함하는 모든 GUI 컴포넌트에서 모델 데이터를 업데이트하는 메소드를 작성해야했기 때문에 GUI 컴포넌트간에 원치 않는 연결이 발생했습니다.

이 문제를 해결하기 위해 View 클래스에서 모든 중첩 된 GUI 구성 요소에 대한 참조 맵을 유지 관리하여 모델 데이터가 업데이트 될 때마다 View는 업데이트 된 모델 데이터를 필요한 GUI 구성 요소로 직접 전송할 수있었습니다. . 각 GUI 구성 요소의 단일 인스턴스 만 있었기 때문에 이것은 잘 작동했습니다. 일부 GUI 구성 요소의 인스턴스가 여러 개인 경우 업데이트가 필요한 복사본을 식별하기가 어려워 제대로 작동하지 않는 것을 알 수있었습니다.


0

당신이 설명하는 것은 책임 체인 디자인 패턴 이라고합니다 . Apple은이 패턴을 이벤트 처리 시스템, 가치있는 용도로 사용합니다.

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