개별 속성이 아닌 전체 클래스를 매개 변수로 사용하도록 클래스 디자인


30

예를 들어, 광범위하게 공유되는 클래스가있는 응용 프로그램이 있다고 가정 해 보겠습니다 User. 이 클래스는 사용자, ID, 이름, 각 모듈에 대한 액세스 수준, 시간대 등에 대한 모든 정보를 제공합니다.

사용자 데이터는 분명히 시스템 전체에서 광범위하게 참조되지만, 어떤 이유로 든이 사용자 개체를이 클래스에 의존하는 클래스로 전달하는 대신 개별 속성 만 전달하도록 시스템이 설정됩니다.

사용자 ID가 필요한 클래스는 GUID userId를 매개 변수로 필요 로하며 때로는 사용자 이름도 필요할 수 있으므로 별도의 매개 변수로 전달됩니다. 경우에 따라 개별 메소드로 전달되므로 값이 클래스 레벨에서 전혀 유지되지 않습니다.

매번 User 클래스에서 다른 정보에 액세스해야 할 때마다 매개 변수를 추가하여 변경해야하며 새 오버로드 추가가 적절하지 않은 경우 메서드 또는 클래스 생성자에 대한 모든 참조도 변경해야합니다.

사용자는 하나의 예일뿐입니다. 이것은 우리 코드에서 널리 실행됩니다.

이것이 Open / Closed 원칙을 위반한다고 생각하는 것이 맞습니까? 기존 클래스를 변경하는 작업뿐만 아니라 처음에 클래스를 설정하여 향후 광범위한 변경이 필요할 가능성이 높습니까?

방금 User객체를 전달하면 작업중인 클래스를 약간 변경할 수 있습니다. 매개 변수를 추가해야하는 경우 클래스에 대한 참조를 수십 번 변경해야 할 수도 있습니다.

이 관행으로 인해 다른 원칙이 어겨 집니까? 의존성 역전? 추상화를 참조하지는 않지만 한 종류의 사용자 만 있으므로 사용자 인터페이스를 가질 필요가 없습니다.

기본 방어 프로그래밍 원칙과 같은 다른 비 SOLID 원칙을 위반하고 있습니까?

내 생성자가 다음과 같아야합니다.

MyConstructor(GUID userid, String username)

아니면 이거:

MyConstructor(User theUser)

포스트 편집 :

질문은 "Pass ID 또는 Object?"에서 대답하는 것이 좋습니다. 이것은 어느 쪽이든 결정이이 질문의 핵심 인 SOLID 원칙을 따르려는 시도에 어떻게 영향을 미치는지에 대한 질문에는 대답하지 않습니다.


11
@ gnat : 이것은 복제본이 아닙니다. 가능한 복제는 객체 계층에 깊이 도달하기위한 메소드 체인에 관한 것입니다. 이 질문은 그것에 대해 전혀 묻지 않는 것 같습니다.
Greg Burghardt

2
두 번째 형식은 전달되는 매개 변수 수가 다루기 어려울 때 자주 사용됩니다.
Robert Harvey

12
첫 번째 서명에 대해 마음에 들지 않는 점은 userId와 username이 실제로 동일한 사용자에서 나온다는 보장이 없다는 것입니다. 사방에 사용자를 전달하여 피할 수있는 잠재적 인 버그입니다. 그러나 결정은 실제로 호출 된 메소드가 인수로 수행하는 작업에 달려 있습니다.
26 중 17

9
구문 분석이라는 단어는 사용하는 상황에서 의미가 없습니다. 대신 "통과"를 의미 했습니까?
Konrad Rudolph

5
의 경우 ISOLID어떻습니까? MyConstructor기본적으로 지금은 " Guid와 a 가 필요합니다"라고 말합니다 string. 그래서 이유를 제공하는 인터페이스가없는 Guid과가 string,하자 User그 인터페이스를 구현하자 MyConstructor그 인터페이스를 구현하는 인스턴스에 의존을? MyConstructor변경 이 필요한 경우 인터페이스를 변경하십시오. -인터페이스를 제공 업체가 아닌 소비자 와 "소유"하는 데 크게 도움이되었습니다 . 따라서 " 소비자 로서 " 제공자 로서 이것을 할 수 있는 것 "대신"이것과 저것을하는 것이 필요합니다 "라고 생각하십시오 .
Corak

답변:


31

전체 User객체를 매개 변수로 전달하는 데 아무런 문제가 없습니다 . 실제로 코드를 명확하게하는 데 도움이 될 수 있으며 메소드 서명에을 (를) 요구할 경우 메소드가 수행하는 작업을 프로그래머에게보다 명확하게 할 수 있습니다 User.

단순한 데이터 유형을 전달하는 것은 그들이 아닌 다른 것을 의미 할 때까지 훌륭합니다. 이 예제를 고려하십시오.

public class Foo
{
    public void Bar(int userId)
    {
        // ...
    }
}

그리고 사용법 예 :

var user = blogPostRepository.Find(32);
var foo = new Foo();

foo.Bar(user.Id);

결함을 발견 할 수 있습니까? 컴파일러는 할 수 없습니다. 전달되는 "사용자 ID"는 정수입니다. 변수의 이름을 지정 user하지만 blogPostRepository객체 에서 값을 초기화하면 객체가 BlogPost아닌 User객체를 반환 하지만 코드는 컴파일되고 런타임 오류가 발생합니다.

이제이 변경된 예를 고려하십시오.

public class Foo
{
    public void Bar(User user)
    {
        // ...
    }
}

아마 Bar메소드는 "사용자 ID"를 사용하지만 메소드 서명은 필요 User개체를. 이제 이전과 동일한 예제 사용법으로 돌아가 보자. "user"전체를 전달하도록 수정하자 :

var user = blogPostRepository.Find(32);
var foo = new Foo();

foo.Bar(user);

이제 컴파일러 오류가 있습니다. blogPostRepository.Find방법은 반환 BlogPost우리가 영리하게 "사용자"를 호출 개체를. 그런 다음이 "사용자"를 Bar메서드에 전달하고 a BlogPost를 허용하는 메서드에 a 를 전달할 수 없으므로 컴파일러 오류가 즉시 발생 합니다 User.

언어의 타입 시스템은 정확한 코드를 더 빨리 작성하고 런타임이 아닌 컴파일 타임에 결함을 식별하기 위해 활용되고 있습니다.

실제로 사용자 정보 변경은 다른 문제의 증상 일 뿐이므로 많은 코드를 리팩터링해야합니다. 전체 User객체를 전달 하면 User클래스 에 대한 내용이 변경 될 때 사용자 정보를 받아들이는 모든 메서드 서명을 리팩터링하지 않아도되는 이점 외에도 위의 이점을 얻을 수 있습니다 .


6
나는 당신의 추론 자체가 실제로 필드를 전달하는 것을 가리키고 있지만 필드가 실제 가치를 둘러싼 사소한 래퍼가된다고 말합니다. 이 예에서 User는 UserID 유형의 필드를 가지며 UserID는 단일 정수 값 필드를 갖습니다. 이제 Bar를 선언하면 Bar가 사용자에 대한 모든 정보를 사용하지 않고 ID 만 사용한다는 것을 즉시 알 수 있지만 UserID에서 가져 오지 않은 정수를 Bar로 전달하는 것과 같은 어리석은 실수는 여전히 할 수 없습니다.
Ian

(계속) 물론 이런 종류의 프로그래밍 스타일은 특히 지루한 구문 지원을 제공하지 않는 언어에서 매우 지루합니다 (예를 들어 "UserID id"와 일치 할 수 있기 때문에 Haskell은이 스타일에 적합합니다). .
Ian

5
@Ian : OP가 제기 한 원래 문제를 중심으로 Id를 자체 유형의 스케이트에 포장하는 것은 User 클래스의 구조적 변경으로 인해 많은 메소드 서명을 리팩터링해야한다고 생각합니다. 전체 User 객체를 전달하면이 문제가 해결됩니다.
Greg Burghardt 2016 년

@Ian : 솔직하지만 C #에서 작업하더라도 Id와 정렬을 Struct로 감싸고 싶은 마음이 생겼습니다.
Greg Burghardt 2016 년

1
"그 자리에 포인터를 놓아도 아무런 문제가 없습니다." 또는 참조 할 수있는 포인터와 관련된 모든 문제를 피하십시오.
Yay295

17

이것이 Open / Closed 원칙을 위반한다고 생각하는 것이 맞습니까?

아니요, 그것은 그 원칙을 위반하지 않습니다. 이 원칙은 User코드를 사용하는 코드의 다른 부분에 영향을주는 방식으로 변경되지 않는 것과 관련 이 있습니다. 변경 사항 User이 그러한 위반 일 수 있지만 관련이 없습니다.

이 관행으로 인해 다른 원칙이 어겨 집니까? 의존성 역전 가능성?

아니요. 설명하는 것은 사용자 개체의 필수 부분 만 각 메서드에 주입하는 것입니다. 정반대의 종속성 반전입니다.

기본 방어 프로그래밍 원칙과 같은 다른 비 SOLID 원칙을 위반하고 있습니까?

아니요.이 방법은 완벽하게 유효한 코딩 방법입니다. 그러한 원칙을 위반하지 않습니다.

그러나 의존성 역전은 원칙 일 뿐이다. 깨지지 않는 법이 아닙니다. 순수한 DI는 시스템에 복잡성을 추가 할 수 있습니다. 전체 사용자 개체를 메서드 나 생성자에 전달하지 않고 필요한 사용자 값만 메서드에 주입하면 문제가 발생하므로 그렇게하지 마십시오. 그것은 원칙과 실용주의 사이의 균형을 얻는 것에 관한 것입니다.

귀하의 의견을 해결하려면 :

불필요하게 체인의 5 레벨 아래로 새로운 값을 구문 분석 한 다음 모든 참조를 기존의 5 가지 방법으로 모두 변경 해야하는 문제가 있습니다 ...

여기서 문제의 일부는 "불필요하게 [통과] ..."주석에 따라이 접근 방식이 마음에 들지 않는다는 것입니다. 그리고 그것은 충분히 공평합니다. 여기에는 정답이 없습니다. 부담 스러우면 그렇게하지 마십시오.

그러나 개방 / 폐쇄 원칙과 관련하여 엄격하게 준수하면 "... 기존의 5 가지 방법 모두에 대한 모든 참조를 변경하십시오 ..."는 해당 방법이 수정되어야 함을 나타냅니다. 수정이 마감되었습니다. 그러나 실제로 공개 / 폐쇄 원칙은 공개 API에는 적합하지만 앱 내부에는별로 의미가 없습니다.

...하지만 가능한 한 그 원칙을 지키려는 계획에는 미래의 변화에 ​​대한 필요성을 줄이는 전략이 포함됩니까?

그러나 당신은 YAGNI 영토 로 방황 하며 여전히 원칙과 직교합니다. Foo사용자 이름을 사용하는 방법 이 있고 Foo생년월일도 원한다면 원리에 따라 새로운 방법을 추가하십시오. Foo변경되지 않은 상태로 유지됩니다. 다시 한 번 퍼블릭 API에는 좋은 방법이지만 내부 코드에는 의미가 없습니다.

앞서 언급했듯이, 그것은 주어진 상황에 대한 균형과 상식에 관한 것입니다. 이러한 매개 변수가 자주 변경되면 예, User직접 사용하십시오 . 설명하는 대규모 변경 사항을 저장합니다. 그러나 자주 변경되지 않으면 필요한 것만 전달하는 것도 좋은 방법입니다.


불필요하게 체인의 5 레벨 아래로 새로운 값을 구문 분석 한 다음 모든 참조를 기존의 5 가지 방법 모두로 변경해야하는 문제가 있습니다. 왜 Open / Closed 원칙이 User 클래스에만 적용되고 현재 편집중인 클래스에는 적용되지 않습니까? 다른 클래스에서도 사용됩니까? 나는 그 원칙이 특별히 변화를 피하는 것에 관한 것이라는 것을 알고 있지만, 그 원칙을 이행 할 수있는 한 미래의 변화에 ​​대한 필요성을 줄이기위한 전략을 포함하는 계획을 반드시 준수해야합니까?
Jimbo

@Jimbo, 귀하의 의견을 반영하기 위해 답변을 업데이트했습니다.
David Arno

당신의 기여에 감사드립니다. BTW. Robert C Martin조차도 Open / Closed 원칙에 엄격한 규칙이 있음을 인정하지 않습니다. 경험상 필연적으로 깨질 것입니다. 원칙을 적용하는 것은 가능한 한 많이 준수하려고 시도하는 운동입니다. 그렇기 때문에 이전에 "실제"라는 단어를 사용했습니다.
Jimbo

User 자체가 아닌 User 매개 변수를 전달하는 것은 종속성 반전이 아닙니다.
James Ellis-Jones 10

@ JamesEllis-Jones, Dependency invertion은 종속성을 "ask"에서 "tell"으로 전환합니다. User인스턴스 를 전달한 다음 해당 객체를 쿼리하여 매개 변수를 가져 오면 종속성이 부분적으로 반전됩니다. 여전히 몇 가지 요구 사항이 있습니다. 진정한 의존성 역전은 100 % "말하지 말아라"입니다. 그러나 그것은 복잡한 가격에 온다.
David Arno

10

예, 기존 기능을 변경하는 것은 개방 / 폐쇄 원칙을 위반하는 것입니다. 요구 사항 변경으로 인해 수정을 위해 닫아야하는 것을 수정하고 있습니다. (요구 사항이 변경 될 경우 변경하지에) 더 좋은 디자인은 사물에 대한 사용자를 전달하는 것입니다 해야 사용자에서 작동합니다.

당신이 함께 통과 할 수 있기 때문에 그러나 그것은, 인터페이스 독방 원리의 충돌하여 실행할 수있는 방법으로 작업을 수행 할 기능 요구 사항보다 더 많은 정보를.

따라서 대부분의 것들과 마찬가지로- 그것은 달려 있습니다.

사용자 이름 만 사용하면 기능이 더욱 유연 해지며 사용자 이름과 위치에 관계없이 사용자 이름을 완벽하게 작동하는 사용자 개체를 만들 필요없이 사용할 수 있습니다. 데이터 소스가 변경 될 것으로 생각되면 변경 탄력성을 제공합니다.

전체 사용자를 사용하면 사용법을 명확하게하고 발신자와 더 확고한 계약을 맺을 수 있습니다. 더 많은 사용자가 필요하다고 생각하면 변경 탄력성을 제공합니다.


+1이지만 "추가 정보를 전달할 수 있습니다"라는 문구가 확실하지 않습니다. (사용자 사용자)를 전달하면 최소한 하나의 객체에 대한 참조 인 최소한의 정보를 전달합니다. 더 많은 정보를 얻기 위해 참조를 사용할 수는 있지만 호출 코드가이를 얻을 필요는 없음을 의미합니다. (GUID 사용자 ID, 문자열 사용자 이름)을 전달하면 호출 된 메서드는 항상 User.find (userid)를 호출하여 개체의 공용 인터페이스를 찾을 수 있으므로 아무 것도 숨기지 않습니다.
dcorking

5
@dcorking, " (사용자 theuser)를 전달하면 최소한 하나의 객체에 대한 참조 인 최소한의 정보를 전달합니다 ". 해당 객체와 관련된 최대 정보, 즉 전체 객체를 전달합니다. " 호출 된 메소드는 항상 User.find (userid) ...를 호출 할 수 있습니다 ." 잘 설계된 시스템에서는 해당 방법이에 액세스 할 수 없으므로 불가능합니다 User.find(). 사실도이 안 User.find . 사용자를 찾는 것은의 책임이되어서는 안됩니다 User.
David Arno

2
@dcorking-enh. 작게 발생하는 참조를 전달한다는 것은 기술적 우연의 일치입니다. 전체 User를 함수에 연결 하고 있습니다. 어쩌면 그것은 말이됩니다. 그러나이 기능은 사용자의 이름에만 관심을 가져야하며 사용자의 가입 날짜 또는 주소와 같은 정보를 전달해야합니다.
Telastyn

@DavidArno 아마도 OP에 대한 명확한 대답의 열쇠 일 것입니다. 사용자를 찾는 책임은 누구에게 있습니까? 파인더 / 공장을 클래스에서 분리하는 디자인 원칙의 이름이 있습니까?
dcorking

1
@dcorking 저는 이것이 단일 책임 원칙의 한 가지 의미라고 말하고 싶습니다. 사용자가 저장된 위치와 ID로 검색하는 방법을 아는 것은 User클래스마다 없어야 할 별도의 책임 입니다. UserRepository그러한 것들을 다루는 또는 이와 유사한 것이 있을 수 있습니다 .
Hulk

3

이 디자인은 매개 변수 객체 패턴을 따릅니다 . 메소드 서명에 많은 매개 변수가있어 발생하는 문제점을 해결합니다.

이것이 Open / Closed 원칙을 위반한다고 생각하는 것이 맞습니까?

아니요.이 패턴을 적용하면 OCP ( Open / close 원리 )가 가능합니다. 예를 들어, 파생 클래스 User는 소비 클래스에서 다른 동작을 유도하는 매개 변수로 제공 될 수 있습니다.

이 관행으로 인해 다른 원칙이 어겨 집니까?

일. SOLID 원칙을 바탕으로 설명하겠습니다.

단일 책임의 원칙은 당신이 설명대로 디자인이있는 경우 (SRP) 위반 될 수있다 :

이 클래스는 사용자, ID, 이름, 각 모듈에 대한 액세스 수준, 시간대 등에 대한 모든 정보를 제공합니다.

문제는 모든 정보에 있습니다. User클래스에 많은 속성이 있으면 소비하는 클래스의 관점에서 관련없는 정보를 전송 하는 거대한 데이터 전송 객체 가됩니다. 예 : 소비 클래스의 관점 UserAuthentication에서 속성 User.IdUser.Name관련이 있지만 관련이 없습니다 User.Timezone.

인터페이스 분리의 원칙 (ISP)도 비슷한 추론을 위반하지만 다른 관점을 추가한다. 예 : 소멸하는 클래스는 가정 UserManagement재산 필요 User.Name로 분할 할 User.LastNameUser.FirstName클래스가 UserAuthentication이에 대한 수정해야합니다.

다행히도 ISP는 문제를 해결할 수있는 방법을 제공합니다. 일반적으로 이러한 매개 변수 개체 또는 데이터 전송 개체는 작게 시작하여 시간이 지남에 따라 커집니다. 이것이 까다로워지면 다음과 같은 접근법을 고려하십시오. 소비하는 클래스의 요구에 맞는 인터페이스를 소개하십시오. 예 : 인터페이스를 소개하고 User클래스에서 인터페이스를 파생 시키 십시오.

class User : IUserAuthenticationInfo, IUserLocationInfo { ... }

각 인터페이스는 User소비 클래스가 작업을 완전히 채우는 데 필요한 클래스 의 관련 속성 하위 집합을 노출해야 합니다. 특성 클러스터를 찾으십시오. 인터페이스를 재사용하십시오. 소비 클래스의 UserAuthentication경우 IUserAuthenticationInfo대신을 사용하십시오 User. 그런 다음 User인터페이스를 "스텐실"로 사용하여 클래스를 여러 개의 구체적인 클래스로 나눕니다.


1
일단 사용자가 복잡해지면, 예를 들어, 사용자가 단지 3 개의 속성을 갖는 경우 가능한 7 가지 조합이있을 수있는 가능한 서브 인터페이스의 조합 폭발이 발생합니다. 귀하의 제안은 훌륭하게 들리지만 실행할 수 없습니다.
user949300 2016 년

1. 분석적으로 옳습니다. 그러나 도메인이 어떻게 모델링되는지에 따라 관련 정보의 비트가 클러스터되는 경향이 있습니다. 따라서 인터페이스와 속성의 가능한 모든 조합을 다룰 필요는 없습니다. 2. 개괄적 인 접근 방식은 보편적 인 해결책이 아니었지만 대답에 '가능한'및 'can'을 더 추가해야 할 수도 있습니다.
Theo Lenndorff 2016 년

2

내 코드 에서이 문제에 직면했을 때 기본 모델 클래스 / 객체가 답이라고 결론지었습니다.

일반적인 예는 리포지토리 패턴입니다. 리포지토리를 통해 데이터베이스를 쿼리 할 때 리포지토리의 많은 메서드가 동일한 매개 변수를 많이 사용합니다.

리포지토리에 대한 내 규칙은 다음과 같습니다.

  • 둘 이상의 메소드가 동일한 둘 이상의 매개 변수를 사용하는 경우 매개 변수는 모델 오브젝트로 그룹화되어야합니다.

  • 메소드가 둘 이상의 매개 변수를 사용하는 경우 매개 변수를 모델 오브젝트로 그룹화해야합니다.

  • 모델은 공통 기반에서 상속 할 수 있지만 실제로 의미가있는 경우에만 가능합니다 (일반적으로 상속을 염두에두고 나중에 리팩터링하는 것이 좋습니다).


프로젝트가 약간 복잡해지기 시작할 때까지 다른 레이어 / 영역에서 모델을 사용하는 데 따른 문제는 분명해지지 않습니다. 코드가 적을수록 더 많은 작업이나 더 많은 합병증이 발생합니다.

예, 서로 다른 레이어 / 목적 (예 : ViewModels와 POCO)을 제공하는 동일한 속성을 가진 2 개의 서로 다른 모델을 갖는 것이 좋습니다.


2

SOLID의 개별 측면을 확인하십시오.

  • 단일 책임 : 사람들이 수업의 일부만 지나가는 경향이있는 경우에는 위반 될 수 있습니다.
  • 열기 / 닫기 : 클래스의 섹션이 전달되는 곳과 관련이 없으며 전체 객체가 전달되는 곳만 해당됩니다. (그것이인지 불협화음이 시작되는 곳이라고 생각합니다. 먼 거리의 코드를 변경해야하지만 클래스 자체는 괜찮아 보입니다.)
  • Liskov 대체 : 문제 없음, 서브 클래스를 수행하지 않습니다.
  • 종속성 반전 (구체적인 데이터가 아니라 추상화에 따라 다름) 네, 위반했습니다 : 사람들은 추상화를 가지고 있지 않습니다. 그들은 클래스의 구체적인 요소를 꺼내서 그것을 통과시킵니다. 이것이 주요 문제라고 생각합니다.

디자인 본능을 혼동하는 경향이있는 한 가지는 클래스가 본질적으로 전역 객체를위한 것이며 본질적으로 읽기 전용이라는 것입니다. 이러한 상황에서 추상화를 위반해도 크게 아프지 않습니다. 수정되지 않은 데이터를 읽는 것만으로도 약한 커플 링이 만들어집니다. 그것이 거대한 더미가 될 때만 고통이 눈에 띄게됩니다.
디자인 본능을 복원하려면 객체가 전역 적이 지 않다고 가정하십시오. User객체가 언제 라도 변경 될 수 있다면 함수에는 어떤 컨텍스트가 필요 합니까? 개체의 어떤 구성 요소가 함께 변형 될 수 있습니까? 이것들은 User참조 된 서브 오브젝트 나 관련 필드의 "슬라이스"만을 노출시키는 인터페이스로서 중요하지 않은, 분리 될 수 있습니다 .

또 다른 원칙 : 일부를 사용하는 기능을 User보고 어떤 필드 (속성)가 함께 사용 되는지 확인하십시오. 하위 객체의 훌륭한 예비 목록입니다. 실제로 서로 속해 있는지 생각해야합니다.

작업이 많고 다소 어려우며, 특히 하위 개체가 겹치는 경우 함수에 전달해야하는 하위 개체 (하위 인터페이스)를 식별하기가 다소 어려워 지므로 코드의 유연성이 약간 떨어집니다.

User하위 객체가 겹치면 분할 이 실제로 추악 해지며 필요한 필드가 모두 겹치는 경우 사람들이 어느 것을 선택해야할지 혼동됩니다. 계층 적으로 분할하면 (예를 들어 UserMarketSegment, 어떤 것을 가지고 있는지 UserLocation) 사람들은 자신이 작성하는 기능의 레벨이 확실하지 않을 것입니다. Location 레벨 또는 레벨 에서 사용자 데이터를 다루고 MarketSegment있습니까? 시간이 지남에 따라 변경 될 수 있음을 정확히 알려주지는 않습니다. 즉, 때로는 전체 콜 체인에서 함수 서명을 변경하는 경우가 있습니다.

다시 말해 : 도메인을 실제로 알고 있고 어떤 모듈이 어떤 측면을 다루고 있는지에 대한 명확한 아이디어가 없다면 User프로그램 구조를 개선 할 가치가 없습니다.


1

이것은 정말 흥미로운 질문입니다. 의존합니다.

미래에 사용자 개체의 다른 매개 변수를 요구하기 위해 메서드가 내부적으로 변경 될 수 있다고 생각되면 반드시 전체를 전달해야합니다. 그러면 메소드 외부의 코드가 사용중인 매개 변수 측면에서 메소드 내부의 변경으로부터 보호되므로 외부에서 일련의 변경이 발생할 수 있습니다. 따라서 전체 사용자를 전달하면 캡슐화가 증가합니다.

사용자의 이메일을 말하는 것 외에 다른 것을 사용할 필요가 없다고 확신한다면이를 전달해야합니다.이 방법의 장점은 더 넓은 범위의 컨텍스트에서이 방법을 사용할 수 있다는 것입니다. 예를 들어 사용할 수 있습니다. 회사 이메일이나 방금 입력 한 이메일을 사용하여 유연성을 높입니다.

이것은 의존성을 주입할지 여부와 전역 적으로 사용 가능한 객체를 가질 지 여부를 포함하여 넓거나 좁은 범위를 갖도록 클래스를 작성하는 것에 대한 광범위한 질문의 일부입니다. 더 좁은 범위가 항상 좋다고 생각하는 불행한 경향이 있습니다. 그러나이 경우와 같이 캡슐화와 유연성 간에는 항상 상충 관계가 있습니다.


1

가능한 한 적은 수의 매개 변수를 전달하는 것이 가장 좋습니다. 따라서 테스트가 쉬워지고 전체 개체를 크레이트 할 필요가 없습니다.

예를 들어, user-id 또는 user-name 만 사용하려는 경우 이것이 전부입니다. 이 패턴이 여러 번 반복되고 실제 사용자 객체가 훨씬 더 크면 더 작은 인터페이스를 만드는 것이 좋습니다. 그것은 수

interface IIdentifieable
{
    Guid ID { get; }
}

또는

interface INameable
{
    string Name { get; }
}

이를 통해 조롱 테스트가 훨씬 쉬워지고 실제로 사용되는 값을 즉시 알 수 있습니다. 그렇지 않으면 종종 하나 또는 두 개의 속성이 필요하지만 다른 많은 종속성으로 복잡한 객체를 초기화해야하는 경우가 종종 있습니다.


1

다음은 때때로 발생하는 내용입니다.

  • 메소드는 소수의 특성 만 사용하더라도 많은 특성을 갖는 유형 User(또는 Product기타) 의 인수를 사용합니다.
  • 어떤 이유로, 코드의 일부는 User개체 가 완전히 채워져 있지 않아도 해당 메서드를 호출해야 합니다. 인스턴스를 만들고 메서드에 실제로 필요한 속성 만 초기화합니다.
  • 이것은 여러 번 발생합니다.
  • 잠시 후, User인수가있는 메소드를 발견하면 해당 메소드에 대한 호출을 찾아서 출처를 찾아서 User어떤 특성이 채워 졌는지 알 수 있습니다. 이메일 주소를 가진 "실제"사용자입니까, 아니면 사용자 ID 및 일부 권한을 전달하기 위해 작성 되었습니까?

User메소드가 필요로하는 특성이므로 몇 개의 특성 만 작성하고 채우는 경우 호출자는 메소드의 내부 작동에 대해 실제로 알고 있어야합니다.

더 나쁜 것은 의 인스턴스 있을 때 User채워진 속성을 알 수 있도록 인스턴스의 출처를 알아야합니다. 당신은 그것을 알고 싶지 않습니다.

시간이 지남에 따라 개발자 User는 메서드 인수의 컨테이너로 사용되는 것을 볼 때 일회용 시나리오를 위해 속성을 컨테이너에 추가하기 시작할 수 있습니다. 클래스가 거의 항상 null이거나 기본값 인 속성으로 복잡해지기 때문에 이제는 추악 해지고 있습니다.

이러한 손상은 피할 수 없지만, 몇 가지 속성에 액세스해야하기 때문에 객체를 전달할 때 반복해서 발생합니다. 위험 영역은 누군가 인스턴스를 생성 User하고 몇 개의 속성을 채우는 것을 처음으로 보게 되므로 메소드에 전달할 수 있습니다. 어두운 길이 기 때문에 발을 내립니다.

가능한 경우 전달해야 할 내용 만 전달하여 다음 개발자에게 적합한 예를 설정하십시오.

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