불변 객체에 대한 인터페이스를 선언하지 마십시오 다음 전달 된 것이 불변이라고 가정하려는 코드로

불변 객체에 대한 인터페이스를 선언하지 마십시오

[편집] 해당 객체가 DTO (Data Transfer Object) 또는 POD (Plain Old Data)를 나타내는 경우

합리적인 지침입니까?

지금까지 변경 불가능한 봉인 클래스에 대한 인터페이스를 만들었습니다 (데이터를 변경할 수 없음). 불변성에 관심이있는 곳에서는 인터페이스를 사용하지 않도록 조심하려고 노력했습니다.

불행히도, 인터페이스가 코드에 퍼지기 시작합니다 (그리고 내가 걱정하는 코드는 아닙니다). 인터페이스가 전달 된 다음 전달 된 것이 불변이라고 가정하려는 코드로 전달하려고합니다.

이 문제로 인해 불변의 객체에 대한 인터페이스를 선언하지 않는 것을 고려하고 있습니다.

이것은 단위 테스트와 관련하여 파급 효과가있을 수 있지만 그 외에는 합리적인 지침으로 보입니까?

아니면 내가보고있는 “확산 인터페이스”문제를 피하기 위해 사용해야하는 다른 패턴이 있습니까?

(나는 여러 가지 이유로이 불변의 객체를 사용하고 있습니다 : 주로 많은 멀티 스레드 코드를 작성하기 때문에 스레드 안전성을 위해; 그러나 메소드에 전달 된 객체의 방어 복사본을 만들 수 없기 때문에 코드가 훨씬 간단 해집니다. 인터페이스를 사용했다면 불변 인 것을 아는 많은 경우가 있습니다. 실제로 인터페이스를 제공하지 않으면 인터페이스를 통해 참조되는 객체의 방어 복사본을 만들 수도 없습니다. 복제 작업 또는 직렬화 방법 …)

[편집하다]

객체를 불변으로 만들고 싶은 이유에 대해 더 많은 컨텍스트를 제공하려면 Eric Lippert의이 블로그 게시물을 참조하십시오.

http://blogs.msdn.com/b/ericlippert/archive/tags/immutability/

또한 멀티 스레드 작업 대기열에서 조작 / 전달되는 항목과 같은 하위 수준 개념으로 작업하고 있음을 지적해야합니다. 이들은 본질적으로 DTO입니다.

또한 Joshua Bloch 는 그의 책 Effective Java에서 불변 객체를 사용할 것을 권장합니다 .


후속 조치

의견 주셔서 감사합니다. 계속해서이 가이드 라인을 DTO 및 그들의 ilk에 사용하기로 결정했습니다. 지금까지는 잘 작동하지만 일주일 밖에 걸리지 않았지만 여전히 좋아 보입니다.

이것에 관해 내가 묻고 싶은 다른 문제들이 있습니다; 특히 내가 “심해 또는 얕은 불변성”이라고 부르는 것 (딥 및 얕은 복제에서 훔친 명명법)-그것은 또 다른 질문입니다.



답변

내 의견으로는, 당신의 규칙은 좋은 것입니다 (적어도 나쁜 것은 아닙니다). 그러나 당신이 묘사하는 상황 때문입니다. 나는 모든 상황에서 그것에 동의한다고 말하지 않을 것입니다. 그래서 내 내부 교육자의 관점에서, 당신의 규칙이 기술적으로 너무 광범위하다고 말해야 할 것입니다.

일반적으로 불변 개체는 기본적으로 데이터 전송 개체 (DTO)로 사용되지 않는 한 정의하지 않습니다. 즉, 데이터 속성은 포함하지만 논리는 거의없고 종속성은 없습니다. 그렇다면 여기에있는 것처럼 인터페이스 대신 콘크리트 유형을 직접 사용하는 것이 안전하다고 말하고 싶습니다.

나는 동의하지 않는 단위 테스팅 순수 주의자들이있을 것이라고 확신하지만, DTO 수업은 단위 테스팅 및 의존성 주입 요구 사항에서 안전하게 제외 될 수 있다고 생각합니다. 종속성이 없기 때문에 팩토리를 사용하여 DTO를 작성할 필요가 없습니다. 모든 것이 필요에 따라 DTO를 직접 생성하는 경우 실제로 다른 유형을 주입 할 방법이 없으므로 인터페이스가 필요하지 않습니다. 논리가 포함되어 있지 않기 때문에 단위 테스트 할 것이 없습니다. 의존성이없는 한 논리가 포함되어 있어도 필요한 경우 논리를 단위 테스트하는 것이 쉽지 않습니다.

따라서 모든 DTO 클래스가 인터페이스를 구현하지 않아야한다는 규칙을 만드는 것은 잠재적으로 불필요하지만 소프트웨어 디자인에 해를 끼치 지 않을 것이라고 생각합니다. 데이터를 변경할 수 없어야한다는 요구 사항이 있으며 인터페이스를 통해이를 적용 할 수 없으므로 해당 규칙을 코딩 표준으로 설정하는 것이 합법적이라고 말하고 싶습니다.

그러나 더 큰 문제는 깨끗한 DTO 레이어를 엄격하게 적용해야한다는 것입니다. 인터페이스가없는 불변 클래스가 DTO 계층에만 존재하고 DTO 계층에 로직과 종속성이없는 한 안전합니다. 그러나 계층을 혼합하기 시작하고 비즈니스 계층 클래스의 두 배인 인터페이스가없는 클래스가 있다면 많은 문제가 발생하기 시작합니다.


답변

나는 코드를 오용하기 어렵게 만드는 것에 대해 소란스러워했다. 나는 변경 멤버를 숨기고 읽기 전용 인터페이스를 만들었고, 내 일반적인 서명 등에 많은 제약을가했습니다. 가상의 동료를 믿지 않기 때문에 디자인 결정을 내렸을 때가 대부분이었습니다. “언젠가 그들은 새로운 엔트리 레벨의 남자를 고용 할 것이며 클래스 XYZ가 DTO ABC를 업데이트 할 수 없다는 것을 알지 못할 것입니다.” 다른 경우에 나는 나무를 통해 숲을 보지 못하는 명백한 해결책을 무시하고 잘못된 문제에 집중하고있었습니다.

더 이상 DTO를위한 인터페이스를 만들지 않습니다. 나는 제 코드를 만지는 사람들 (주로 나 자신)이 무엇이 허용되고 무엇이 의미가 있는지를 알고 있다고 가정합니다. 똑같은 바보 같은 실수를 계속하면 보통 인터페이스를 강화하려고하지 않습니다. 이제 나는 왜 같은 실수를 계속하는지 이해하려고 대부분의 시간을 보냅니다. 일반적으로 내가 과도하게 분석하거나 핵심 개념을 놓치고 있기 때문입니다. 편집증을 포기한 이후로 코드 작업이 훨씬 쉬워졌습니다. 또한 시스템 작업에 대한 내부 지식이 필요한 “프레임 워크”가 줄어 듭니다.

내 결론은 가장 간단한 것을 찾는 것입니다. 안전한 인터페이스를 만드는 복잡성의 추가로 개발 시간이 낭비되고 간단한 코드가 복잡해집니다. 라이브러리를 사용하는 10,000 명의 개발자가있을 때 이와 같은 것에 대해 걱정하십시오. 나를 믿으십시오. 불필요한 긴장에서 벗어날 것입니다.


답변

괜찮은 지침처럼 보이지만 이상한 이유가 있습니다. 인터페이스 (또는 추상 기본 클래스)가 불변의 일련의 객체에 균일하게 액세스 할 수있는 곳이 많이 있습니다. 전략은 여기에 빠지는 경향이 있습니다. 상태 객체는 여기에 속하는 경향이 있습니다. 불변의 것처럼 보이도록 인터페이스를 구성하고 API에서와 같이 문서화하는 것이 너무 불합리하다고 생각하지 않습니다.

즉, 사람들은 Plain Old Data (이하 POD) 객체, 심지어 단순한 (불변의) 구조를 지나치게 인터페이스하는 경향이 있습니다. 코드에 기본적인 구조에 대한 대안이 없다면 인터페이스가 필요하지 않습니다. 아니요, 단위 테스트는 설계를 변경하기에 충분한 이유가 아닙니다 (데이터베이스 액세스를 모의하는 것은 인터페이스를 제공하는 이유가 아니며 향후 변경을위한 유연성입니다). 기본 기본 구조를 그대로 사용하십시오.


답변

불변의 것을 알면 많은 경우 코드가 훨씬 간단 해집니다. 인터페이스를 사용해 본 적이없는 경우입니다.

액세서 구현자가 걱정해야 할 문제라고 생각하지 않습니다. 경우 interface X불변하기위한 것입니다, 그것은 그들이 불변의 방식으로 인터페이스를 구현하는 것을 보장하기 인터페이스 구현의 책임 아닌가요?

그러나 내 마음에는 불변 인터페이스와 같은 것이 없습니다. 인터페이스에 의해 지정된 코드 계약은 객체의 노출 된 메소드에만 적용되며 객체의 내부에는 영향을 미치지 않습니다.

불변성을 인터페이스가 아닌 데코레이터로 구현하는 것이 훨씬 일반적이지만 해당 솔루션의 실행 가능성은 실제로 객체의 구조와 구현의 복잡성에 달려 있습니다.


답변

인터페이스가 전달 된 다음 전달 된 것이 불변이라고 가정하려는 코드로 전달하려고합니다.

C #에서 “불변”유형의 개체를 예상하는 메서드는 C # 인터페이스가 불변성 계약을 지정할 수 없으므로 인터페이스 유형의 매개 변수를 가져서는 안됩니다. 따라서 C #에서 제안하는 지침은 처음부터 수행 할 수 없기 때문에 의미가 없습니다 (향후의 언어 버전에서는이를 수행 할 수 없음).

귀하의 질문은 불변성에 대한 Eric Lippert의 기사에 대한 미묘한 오해에서 비롯됩니다. 에릭은 인터페이스를 정의하지 않았다 IStack<T>IQueue<T>불변의 스택과 큐에 대한 계약을 지정할 수 있습니다. 그들은하지 않습니다. 그는 편의상 그것들을 정의했다. 이러한 인터페이스를 통해 빈 스택 및 대기열에 대해 서로 다른 유형을 정의 할 수있었습니다. 빈 스택을 나타 내기 위해 인터페이스 또는 별도의 유형을 요구하지 않고 단일 유형을 사용하여 불변 스택의 다른 디자인 및 구현 을 제안 할 수 있지만 결과 코드는 깨끗하지 않고 조금 덜 효율적입니다.

이제 Eric의 디자인을 고수합시다. 불변 스택을 필요로하는 메소드 는 일반적인 의미에서 스택의 추상 데이터 유형을 나타내는 Stack<T>일반 인터페이스 IStack<T>가 아닌 유형의 매개 변수를 가져야합니다 . Eric의 불변 스택을 사용할 때이 작업을 수행하는 방법은 명확하지 않으며 기사에서이 내용을 다루지 않았지만 가능합니다. 빈 스택 유형에 문제가 있습니다. 빈 스택을 얻지 않도록하여이 문제를 해결할 수 있습니다. 더미 값을 스택의 첫 번째 값으로 푸시하고 절대로 팝하지 않으면이를 보장 할 수 있습니다. 이 방법을 사용하면 안전의 결과 캐스트 할 수 PushPop로를 Stack<T>.

Stack<T>구현이 IStack<T>유용 할 수 있습니다. 반드시 불변 스택이 아닌 스택, 모든 스택이 필요한 메소드를 정의 할 수 있습니다. 이러한 메소드는 유형의 매개 변수를 가질 수 있습니다 IStack<T>. 이를 통해 불변 스택을 전달할 수 있습니다. 이상적으로 IStack<T>는 표준 라이브러리 자체의 일부입니다. .NET에는는 IStack<T>없지만 불변 스택이 구현할 수있는 다른 표준 인터페이스가 있으므로 유형이 더 유용합니다.

앞에서 언급 한 불변 스택의 대체 설계 및 구현에는이라는 인터페이스가 사용 IImmutableStack<T>됩니다. 물론 인터페이스 이름에 “Immutable”을 삽입한다고해서이를 구현하는 모든 유형을 변경할 수있는 것은 아닙니다. 그러나이 인터페이스에서 불변의 계약은 언어 적입니다. 좋은 개발자는 그것을 존중해야합니다.

소규모의 내부 라이브러리를 개발하는 경우 팀의 모든 사람과 계약을 이행하기 위해 동의 할 수 IImmutableStack<T>있으며 매개 변수 유형으로 사용할 수 있습니다 . 그렇지 않으면 인터페이스 유형을 사용하지 않아야합니다.

C # 질문에 태그를 추가했기 때문에 C # 사양에는 DTO 및 POD와 같은 것이 없다는 것을 추가하고 싶습니다. 따라서 폐기하거나 정확하게 정의하면 질문이 개선됩니다.