여러 인터페이스를 결합하는 빈 인터페이스


20

두 개의 인터페이스가 있다고 가정하십시오.

interface Readable {
    public void read();
}

interface Writable {
    public void write();
}

경우에 따라 구현 객체는 이들 중 하나만 지원할 수 있지만 많은 경우 구현시 두 인터페이스를 모두 지원합니다. 인터페이스를 사용하는 사람들은 다음과 같은 작업을 수행해야합니다.

// can't write to it without explicit casting
Readable myObject = new MyObject();

// can't read from it without explicit casting
Writable myObject = new MyObject();

// tight coupling to actual implementation
MyObject myObject = new MyObject();

이 옵션들 중 어느 것도 매우 편리하지 않으며, 메소드 매개 변수로 원한다고 생각할 때 훨씬 더 편리합니다.

한 가지 해결책은 랩핑 인터페이스를 선언하는 것입니다.

interface TheWholeShabam extends Readable, Writable {}

그러나 이것은 하나의 특정 문제가 있습니다. Readable과 Writable 모두 지원하는 모든 구현 은 인터페이스를 사용하는 사람들과 호환되도록하려면 TheWholeShabam을 구현해야합니다. 비록 두 인터페이스의 보장 된 존재 외에는 아무것도 제공하지 않습니다.

이 문제에 대한 깨끗한 해결책이 있습니까? 아니면 랩퍼 인터페이스로 가야합니까?

최신 정보

실제로 읽고 쓸 수있는 객체가 필요한 경우가 많으므로 인수에서 우려를 분리하는 것이 항상 깨끗한 해결책은 아닙니다.

업데이트 2

(답변으로 추출되므로 댓글을 달기가 더 쉽습니다)

업데이트 3

이것에 대한 기본 사용 사례는 스트림 이 아님 을 명심하십시오 (물론 지원되어야하지만). 스트림은 입력과 출력을 매우 구체적으로 구분하며 책임이 명확하게 분리되어 있습니다. 오히려 매우 구체적인 상태가 첨부 된 하나의 객체에 쓰고 읽을 수있는 하나의 객체가 필요한 바이트 버퍼와 같은 것을 생각하십시오. 이 객체들은 비동기 I / O, 인코딩 등과 같은 것들에 매우 유용하기 때문에 존재합니다.

업데이트 4

내가 시도한 첫 번째 것 중 하나는 아래 주어진 제안과 동일했지만 (허용 된 답변을 확인하십시오) 너무 약한 것으로 판명되었습니다.

유형을 반환 해야하는 클래스가 있다고 가정하십시오.

public <RW extends Readable & Writable> RW getItAll();

이 메소드를 호출하면 객체를받는 변수에 의해 일반 RW가 결정되므로이 변수를 설명 할 방법이 필요합니다.

MyObject myObject = someInstance.getItAll();

이것은 작동하지만 다시 한 번 구현에 연결하고 실제로 반환 된 내용에 따라 런타임에 클래스 캐스트 예외를 throw 할 수 있습니다.

또한 유형 RW의 클래스 변수를 원하는 경우 클래스 레벨에서 제네릭을 정의해야합니다.


5
문구는 "전체 shebang"
케빈 클라인

이것은 좋은 질문이지만, 예제 인터페이스가 일반적으로 다른 역할을하므로 물을 약간
흐릿하게하기

@Basueta 네이밍이 단순화되었지만 실제로 읽고 쓸 수있는 것은 실제로 나의 유스 케이스를 잘 전달합니다. 어떤 경우에는 읽기 전용을 원하고 어떤 경우에는 쓰기 전용이며 놀라운 경우에는 읽기 및 쓰기가 가능합니다.
nablex

읽기 및 쓰기가 가능한 단일 스트림이 필요하고 다른 사람들의 답변 / 의견으로 판단 할 때 내가 유일한 사람이라고 생각하지 않는 시간을 생각할 수 없습니다. 논란의 여지가 적은 인터페이스 쌍을 선택하는 것이 더 도움이 될 것입니다 ...
vaughandroid

@Baqueta java.nio * 패키지와 관련이 있습니까? 스트림을 고수하면 유스 케이스는 실제로 ByteArray * Stream을 사용하는 장소로 제한됩니다.
nablex

답변:


19

예, 모두 확장하는 underspecified 유형으로 당신의 방법 매개 변수를 선언 할 수 ReadableWritable:

public <RW extends Readable & Writable> void process(RW thing);

메소드 선언은 끔찍해 보이지만이를 사용하는 것이 통합 인터페이스에 대해 알아야하는 것보다 쉽습니다.


2
Konrads의 두 번째 접근 방식을 선호합니다. process(Readable readThing, Writable writeThing)그리고를 사용하여 호출해야합니다 process(foo, foo).
Joachim Sauer

1
올바른 구문이 <RW extends Readable&Writable>아닌가요?
에서 오는

1
@JoachimSauer : 왜 시각적으로보기 흉한 방법을 쉽게 넘어 설 수있는 접근 방식을 선호하십니까? process (foo, bar)를 호출하고 foo와 bar가 다르면 메소드가 실패 할 수 있습니다.
Michael Shaw

@ MichaelShaw : 내가 말하는 것은 서로 다른 객체 일 때 실패해서는 안된다는 것 입니다. 왜 그래야합니까? 그렇다면 process여러 가지 다른 일을하고 단일 책임 원칙을 위반 한다고 주장합니다 .
Joachim Sauer

@JoachimSauer : 왜 실패하지 않습니까? for (i = 0; j <100; i ++)는 for (i = 0; i <100; i ++)만큼 유용한 루프가 아닙니다. for 루프는 동일한 변수를 읽고 쓰며 SRP를 위반하지 않습니다.
Michael Shaw

12

당신이 필요로하는 곳에있는 장소가 있다면 myObject모두로 Readable하고 Writable당신이 할 수있는 :

  • 그 장소를 리팩터링 하시겠습니까? 읽기와 쓰기는 서로 다른 두 가지입니다. 방법이 둘 다를 수행하는 경우 단일 책임 원칙을 따르지 않을 수 있습니다.

  • 패스 myObjectA와 두 번 Readable과로 Writable(두 개의 인수). 메소드가 동일한 오브젝트인지 아닌지를 어떻게 처리합니까?


1
이것은 인수로 사용할 때 작동 할 수 있으며 별도의 관심사입니다. 그러나 때로는 동시에 읽고 쓸 수있는 객체를 원할 수도 있습니다 (예를 들어 ByteArrayOutputStream을 사용하려는 것과 같은 이유로)
nablex

어떻게 오세요? 출력 스트림은 이름에서 알 수 있듯이 쓸 수있는 입력 스트림입니다. C #에서 동일 -있다 StreamWriterStreamReader(그리고 많은 다른)
콘라드 Morawski

바이트 수 (toByteArray ())를 얻기 위해 ByteArrayOutputStream에 씁니다. 이것은 쓰기 + 읽기와 같습니다. 인터페이스 뒤의 실제 기능은 거의 동일하지만보다 일반적인 방식입니다. 때로는 읽기 또는 쓰기 만 원할 수도 있고 둘 다 원할 수도 있습니다. 다른 예는 ByteBuffer입니다.
nablex

2
나는 그 두 번째 시점에서 조금 반동했지만, 잠시 생각을 한 후에는 실제로 나쁜 생각처럼 보이지 않습니다. 읽기와 쓰기를 분리 할뿐만 아니라보다 유연한 기능을 만들고 입력 상태의 양을 줄입니다.
Phoshi

2
@Phoshi 문제는 문제가 항상 분리되지는 않는다는 것입니다. 때로는 읽고 쓸 수있는 객체를 원하고 동일한 객체임을 보장하고자합니다 (예 : ByteArrayOutputStream, ByteBuffer, ...)
nablex

4

읽기 또는 쓰기 가 필요하지 않지만 둘 다 필요한 상황에서는 현재 답변이 없습니다 . A에 쓸 때 A에서 데이터를 읽고 A에서 쓰지 않고 B에서 읽지 않고 실제로 동일한 객체임을 희망 할 수 있습니다. 사용 사례는 풍부합니다 (예 : ByteBuffer를 사용하는 모든 곳).

어쨌든, 나는 작업중 인 모듈을 거의 완성했으며 현재 래퍼 인터페이스를 선택했습니다.

interface Container extends Readable, Writable {}

이제 최소한 할 수 있습니다 :

Container container = IOUtils.newContainer();
container.write("something".getBytes());
System.out.println(IOUtils.toString(container));

컨테이너 (현재 3 개)의 자체 구현은 모두 별도의 인터페이스와 달리 컨테이너를 구현하지만 구현에서 누군가 이것을 잊어 버린 경우 IOUtils는 유틸리티 메소드를 제공합니다.

Readable myReadable = ...;
// assuming myReadable is also Writable you can do this:
Container container = IOUtils.toByteContainer(myReadable);

이것이 최적의 솔루션은 아니지만 컨테이너가 여전히 상당히 큰 유스 케이스이기 때문에 여전히 최선의 방법이라고 생각합니다.


1
나는 이것이 절대적으로 좋다고 생각합니다. 다른 답변에서 제안 된 다른 접근법 중 일부보다 낫습니다.
Tom Anderson

0

일반적인 MyObject 인스턴스가 주어지면 항상 읽기 또는 쓰기를 지원하는지 여부를 알아야합니다. 따라서 다음과 같은 코드가 있습니다.

if (myObject instanceof Readable)  {
    Readable  r = (Readable) myObject;
    readThisReadable( r );
}

간단한 경우에, 나는 당신이 이것을 향상시킬 수 있다고 생각하지 않습니다. 그러나 Readable을 다른 파일에 읽은 후 다른 파일 readThisReadable 려면 어색합니다.

그래서 아마 이것과 함께 갈 것입니다 :

interface TheWholeShabam  {
    public boolean  isReadable();
    public boolean  isWriteable();
    public void     read();
    public void     write();
}

매개 변수로이 촬영, readThisReadable지금 readThisWholeShabam바로 MyObject를하지, 구현이 TheWholeShabam 것을 모든 클래스를 처리 할 수 있습니다. 그리고 그것은 쓸 경우를 작성하고, 그렇지 않은 경우를 쓸 수 없습니다. (실제 "다형성"이 있습니다.)

따라서 첫 번째 코드 세트는 다음과 같습니다.

TheWholeShabam  myObject = ...;
if (myObject.isReadable()
    readThisWholeShebam( myObject );

readThisWholeShebam () 가독성 검사를 수행하여 여기에 줄을 저장할 수 있습니다.

이것은 우리의 이전 Readable-only가 isWriteable () ( false 반환 )과 write () (아무것도하지 않음) 을 구현해야 하지만 이제는 갈 수 없었던 모든 종류의 장소와 TheWholeShabam을 처리하는 모든 코드를 갈 수 있음을 의미합니다 물체는 우리가 더 이상 노력하지 않고 그것을 처리 할 것입니다.

또 다른 것 : 읽지 않는 클래스에서 read () 호출 및 무언가를 휴지통에 올리지 않고 쓰지 않는 클래스에서 write () 호출을 처리 할 수 ​​있다면 isReadable () 및 isWriteable을 건너 뛸 수 있습니다 () 메소드. 이것이 작동하는 경우 가장 우아한 방법입니다.

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