인터페이스에서 "선택적 메소드"로 Java 콜렉션이 구현 된 이유는 무엇입니까?


69

Java 콜렉션 프레임 워크를 확장 한 첫 번째 구현 중에 콜렉션 인터페이스에 선택적으로 선언 된 메소드가 포함되어 있다는 사실에 놀랐습니다. 구현되지 않은 경우 구현자는 UnsupportedOperationExceptions를 발생시킵니다. 이것은 즉시 API 디자인 선택이 좋지 않은 것으로 나타났습니다.

Joshua Bloch의 뛰어난 "Effective Java"책을 많이 읽은 후 나중에 이러한 결정에 대한 책임을진다는 사실을 알게 된 후에는이 책에 포함 된 원칙을 따르지 않는 것 같습니다. "선택적"메소드로 Collection을 확장하는 Collection과 MutableCollection이라는 두 가지 인터페이스를 선언하면 훨씬 더 유지 관리 가능한 클라이언트 코드가 생겼을 것입니다.

여기 에 문제에 대한 훌륭한 요약이 있습니다 .

두 개의 인터페이스 구현 대신에 선택적 메소드를 선택한 이유가 있습니까?


IMO, 그럴만한 이유가 없습니다. C ++ STL은 80 년대 초 Stepanov가 개발했습니다. 사용이 어색한 C ++ 템플릿 구문으로 제한되었지만 Java 컬렉션 클래스와 비교할 때 일관성과 유용성의 모범입니다.
케빈 클라인

C ++ STL 'porting'이 Java로있었습니다. 그러나 수업 횟수가 5 배 더 컸습니다. 이 마음으로 나는 더 큰 것이 더 일관되고 사용 가능하다는 것을 사지 않습니다. docs.oracle.com/javase/6/docs/technotes/guides/collections/…
m3th0dman

@ m3th0dman Java에는 C ++ 템플릿과 동등한 기능이 없기 때문에 Java에서 훨씬 더 큽니다.
케빈 클라인

어쩌면 내가 개발 한 좀 이상한 스타일,하지만 내 코드는 "읽기 전용"모든 컬렉션을 치료하는 경향이있다 (더 정확하게, 당신은 계산이나하는 동안 당신이 반복 뭔가) 를 제외하고 에 대한 몇 가지 사실 컬렉션을 만드는 방법. 어쨌든 좋은 연습 (예 : 동시성). 그리고 많은 사람들이 불평하는 선택적인 방법은 결코 나에게 진짜 문제 가 되지 않았습니다. "슈퍼"와 "확장자"가있는 제네릭의 악몽도 문제가되지 않았습니다. 다른 사람들이이 일반적인 관행을 사용하는지 궁금하십니까?
user949300

답변:


28

질문은 답을 제공합니다. 요컨대, 그들은 가능한 각 구현 옵션 옵션 세트에 대해 수정 가능하고 수정 불가능한보기, 삭제 전용, 추가 전용, 고정 길이, 불변 (스레딩에 대한) 등으로 필요한 인터페이스의 조합 조합 폭발을 목격했습니다.


6
Java에 constC ++과 같은 키워드 가 있으면이 모든 것을 피할 수 있습니다.
에티엔 느 마르텔

@Etienne : Python과 같은 메타 클래스가 더 좋습니다. 그런 다음 프로그래밍 방식으로 여러 인터페이스를 프로그래밍 방식으로 구축 할 수 있습니다. const의 문제점은 하나 또는 두 개의 차원 만 제공한다는 것 vector<int>, const vector<int>, vector<const int>, const vector<const int>입니다. 지금까지는 훌륭했지만 그래프를 구현하려고 시도하고 그래프 구조를 일정하게 만들려고하지만 노드 속성은 수정 가능하게 만들려고합니다.
Neil G

2
@Etienne, 내 할일 목록에서 "Learn Scala"를 부딪 치는 또 다른 이유!
glenviewjeff

2
내가 이해하지 못하는 것은 왜 can작업이 가능한지 테스트 하는 방법을 만들지 않은 것 입니까? 인터페이스를 간단하고 빠르게 유지합니다.
Mehrdad

3
@Etienne de Martel Nonsense. 조합 폭발에서 어떻게 도움이 될까요?
Tom Hawtin-tackline

10

그것은 지금처럼 Interface Segregation Principle잘 탐험되지 않았던 것처럼 들린다 . SOLID와 ISP가 품질 코드의 사실상의 표준이되기 전에는 이러한 방식의 작업 (즉, 인터페이스에 가능한 모든 작업이 포함되어 있고 필요하지 않은 예외에 대해 예외를 발생시키는 "축소 된"메소드가 있음)이 사용되었습니다.


2
왜 공감해야합니까? 누군가 ISP를 좋아하지 않습니까?
Wayne Molina

분산을 지원하는 프레임 워크에서는 기본적으로 불변 인 인터페이스와 공 변형 또는 반 변형 일 수있는 인터페이스의 측면을 분리하는 것이 큰 이점이 있다는 점도 주목할 가치가 있습니다. 이러한 지원이없는 경우에도 유형 삭제를 사용하지 않는 프레임 워크에서는 유형에 독립적 인 인터페이스의 측면을 분리하는 데 가치가 있습니다 (예 : Count포함 된 항목 유형에 대해 걱정할 필요없이 컬렉션을 얻을 수 있음 ). 그러나 Java와 같은 유형 삭제 기반 프레임 워크에서는 그러한 문제가 아닙니다.
supercat

4

어떤 사람들은 "선택적 방법"을 싫어할 수 있지만, 많은 경우 고도로 분리 된 인터페이스보다 더 나은 의미를 제공 할 수 있습니다. 무엇보다, 그들은 객체가 일생 동안 능력이나 특성을 얻을 수 있거나, 객체 (특히 래퍼 객체)가 언제 어떤 정확한 능력을보고해야하는지 알 수없는 가능성을 허용합니다.

좋은 디자인의 Java 콜렉션 클래스 모범 (paragon)이라고 부르지는 않지만, 훌륭한 콜렉션 프레임 워크는 그 특성과 능력에 대한 콜렉션을 요청하는 방법 과 함께 많은 선택적 메소드 기본으로 포함해야한다고 제안 합니다 . 이러한 디자인은 단일 래퍼 클래스가 기본 컬렉션이 보유 할 수있는 능력을 실수로 모호하게하지 않으면 서 다양한 컬렉션과 함께 사용할 수 있도록합니다. 메소드가 선택 사항이 아닌 경우 콜렉션이 지원할 수있는 모든 기능 조합에 대해 다른 랩퍼 클래스가 필요하거나 일부 상황에서 일부 랩퍼를 사용할 수 없게해야합니다.

예를 들어, 컬렉션이 인덱스로 항목을 쓰거나 끝에 항목을 추가 할 수는 있지만 중간에 항목을 삽입하는 것을 지원하지 않는 경우에는 래퍼로 항목을 캡슐화하려는 코드에서 실행되는 모든 작업을 기록하는 코드가 버전이 필요합니다 지원되는 기능의 정확한 조합을 제공하는 로깅 래퍼의 경우 또는 사용 가능한 것이없는 경우 추가 또는 색인 별 쓰기를 지원하지만 둘 다를 지원하지 않는 래퍼를 사용해야합니다. 그러나 통합 콜렉션 인터페이스가 세 가지 메소드를 모두 "선택적"으로 제공했지만 사용할 수있는 선택적 메소드를 표시하는 메소드를 포함하면 단일 랩퍼 클래스가 기능 조합을 구현하는 콜렉션을 처리 할 수 ​​있습니다. 지원하는 기능이 무엇인지 물으면 랩퍼는 캡슐화 된 콜렉션이 지원하는 모든 것을 간단히보고 할 수 있습니다.

"선택적 능력"의 존재는 어떤 경우에는 집합의 콜렉션이 구현의 존재에 의해 능력이 정의 된 경우 가능한 것보다 훨씬 더 효율적인 방식으로 특정 기능을 구현할 수있게한다. 예를 들어, concatenate메소드가 두 개의 다른 콜렉션 중에서 복합 콜렉션을 형성하는 데 사용되었다고 가정 하십시오. 첫 번째는 1,000,000 개의 요소가있는 ArrayList이고 마지막은 시작부터 반복 할 수있는 20 개의 요소 콜렉션이었습니다. 복합 컬렉션에 1,000,013 번째 요소 (인덱스 1,000,012)가 요청 된 경우 ArrayList에 포함 된 항목 수 (예 : 1,000,000)를 요청하고 요청 된 인덱스에서 해당 항목을 빼고 (수율 12) 두 번째 요소에서 12 개의 요소를 읽고 건너 뛸 수 있습니다. 컬렉션, 다음 요소를 반환합니다.

이러한 상황에서 복합 콜렉션은 인덱스로 항목을 즉시 리턴하는 방법이 없지만 1,000,013 번째 항목에 대해 복합 콜렉션을 요청하는 것은 개별적으로 1,000,013 개의 항목을 읽고 마지막을 제외한 모든 항목을 무시하는 것보다 훨씬 빠릅니다. 하나.


@ kevincline : 당신은 인용 문구에 동의하지 않습니까? 인터페이스 구현이 주요한 특징과 능력을 설명 할 수있는 수단의 디자인은 인터페이스 디자인의 가장 중요한 측면 중 하나라고 생각합니다. 비록 많은 관심을받지는 않더라도 말입니다. 인터페이스의 서로 다른 구현이 특정 공통 작업을 수행하기위한 서로 다른 최적의 방법을 가지려면 성능에 관심이있는 클라이언트가 중요한 시나리오에서 최상의 접근 방식을 선택하는 수단이 있어야합니다.
supercat

1
미완성, 불완전한 의견. " '컬렉션을 묻는 방법'..."은 컴파일 타임 검사가 필요한 것을 런타임으로 옮긴다 고 말하려고했습니다.
kevin cline

@kevincline : 클라이언트가 특정 능력을 가지고 있어야하며 클라이언트 없이는 살 수없는 경우 컴파일 타임 검사가 도움이 될 수 있습니다. 반면에, 컬렉션은 컴파일 타임 보증이 가능한 것 이상의 컬렉션을 가질 수있는 상황이 많이 있습니다. 어떤 경우에는 그 계약의 모든 합법적 구현하도록 지정 파생 된 인터페이스를 가지고 의미가 있습니다 파생 된 인터페이스는 기본 인터페이스에서 선택 사항입니다 특정 방법을 지원해야합니다,하지만 경우에 코드가 있는지 여부 뭔가를 처리 할 수있을 것입니다 경우 그것은 몇 가지 기능이 있습니다 ...
supercat

...하지만 존재하는 기능의 이점을 얻을 수 있지만 객체의 유형이 해당 기능을 지원한다고 약속하는지 여부를 유형 시스템에 묻는 것보다 코드가 객체가 기능을 지원하는지 여부를 묻는 것이 좋습니다. 특정 "특정"인터페이스가 널리 사용되는 경우, AsXXX인터페이스를 구현하는 경우 호출되는 오브젝트를 리턴하거나 가능한 경우 해당 인터페이스를 지원하는 랩퍼 오브젝트를 리턴하거나 던지는 메소드를 기본 인터페이스에 포함시킬 수 있습니다. 그렇지 않은 경우 예외. 예를 들어, ImmutableCollection인터페이스는 계약에 의해 필요할 수 있습니다.
supercat

2
제안에 문제가 있습니다. 개체의 기능이 수명 동안 변경 될 수있는 경우 "요청"패턴에 동시성 문제가 있습니다. (어쨌든 예외 위험을 감수해야한다면 물어 보지 않아도 될 것입니다 ...)
jhominal

-1

나는 당시에 더 잘 알지 못했던 원래 개발자에게 귀속되었습니다. Java 2 및 Collections가 처음 릴리스 된 1998 년 이후 OO 디자인에서 먼 길을 왔습니다. 명백한 나쁜 디자인처럼 보이는 것은 OOP 초기에 그렇게 명확하지 않았습니다.

그러나 추가 캐스팅을 방지하기 위해 수행되었을 수 있습니다. 두 번째 인터페이스라면 컬렉션 인스턴스를 캐스팅하여 추악한 옵션 메소드를 호출해야합니다. 이제 UnsupportedOperationException을 즉시 포착하고 코드를 수정합니다. 그러나 두 개의 인터페이스가 있다면 instanceof와 cast를 사용해야합니다. 아마도 그들은 유효한 상충 관계라고 생각했을 것입니다. 또한 초기 Java 2 일에 인스턴스의 성능이 느리기 때문에 심한 인상을 받았고, 과잉 사용을 방지하려고 시도했을 수도 있습니다.

물론 이것은 모두 사색적인 추측입니다. 원래 컬렉션 건축가 중 하나가 차임하지 않는 한 확실히 대답 할 수는 없습니다.


7
어떤 캐스팅? 메소드가 a Collection가 아닌 a를 반환하면 MutableCollection수정되지 않는다는 명백한 신호입니다. 왜 아무도 캐스팅해야하는지 모르겠습니다. 별도의 인터페이스가 있으면 런타임에 예외가 발생하지 않고 컴파일 타임에 이러한 종류의 오류가 발생합니다. 오류가 빠를수록 좋습니다.
에티엔 느 마르텔

1
유연성이 떨어지기 때문에 컬렉션 라이브러리의 가장 큰 장점 중 하나는 어디에서나 높은 수준의 인터페이스를 반환하고 실제 구현에 대해 걱정할 필요가 없다는 것입니다. 두 개의 인터페이스를 사용하는 경우 이제 더 밀접하게 연결됩니다. 대부분의 경우 ImmutableList가 아닌 List를 반환하고 싶을 것입니다. 일반적으로 결정하기 위해 호출 클래스에 그대로 두려고하기 때문입니다.
Jberg

6
읽기 전용 컬렉션을 제공하는 경우 컬렉션을 수정하고 싶지 않기 때문입니다. 예외를 던지고 문서에 의존하는 것은 마치 패치처럼 느껴집니다. C ++에서는 const객체 를 반환 하여 사용자에게 객체를 수정할 수 없음을 즉시 알려줍니다.
Etienne de Martel

7
1998 년은 "OO 디자인의 초기 시절"이 아니 었습니다.
quant_dev
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.