인터페이스를 별도의 패키지에 넣어야합니까? [닫은]


80

저는 많은 구성 요소와 종속성이있는 다소 큰 프로젝트에서 작업하는 팀이 처음입니다. 모든 구성 요소에 interfaces대해 해당 구성 요소에 대한 노출 된 인터페이스가 배치 되는 패키지가 있습니다. 이것이 좋은 습관입니까?

나의 일상적인 관행은 항상 인터페이스와 구현이 동일한 패키지로 이동하는 것이 었습니다.


4
이 태그가 [언어에 구애받지 않음] 인 이유는 무엇입니까?
finnw

모듈이 직접 호출이 아닌 일부 rpc를 통해 통신되는 경우 이것이 선호 될 수있는 또 다른 상황-인터페이스는 클라이언트에 대한 프록시를 생성하는 데 유용합니다.
redzedi

답변:


101

인터페이스와 구현을 모두 배치하는 것은 흔한 일이며 문제가되지 않는 것 같습니다.

예를 들어 Java API를 예로 들어 보겠습니다. 대부분의 클래스에는 동일한 패키지에 포함 된 인터페이스와 구현이 모두 있습니다.

예를 들어 java.util패키지를 살펴보십시오 .

그와 같은 인터페이스를 포함 Set, Map, List또한 구현하면서 같은 HashSet, HashMapArrayList.

또한 Javadocs는 패키지 내용을 표시 할 때 문서를 인터페이스클래스 보기 로 분리하므로 이러한 조건에서 잘 작동하도록 설계되었습니다 .

인터페이스가 엄청나게 많은 경우가 아니라면 인터페이스 전용 패키지를 갖는 것은 실제로 약간 과도 할 수 있습니다. 그러나 그렇게하기 위해 인터페이스를 자체 패키지로 분리하는 것은 나쁜 습관처럼 들립니다.

인터페이스 이름을 구현과 구별해야하는 경우 인터페이스를보다 쉽게 ​​식별 할 수 있도록 명명 규칙을 사용할 수 있습니다.

  • 인터페이스 이름 앞에 I. 이 접근 방식은 .NET 프레임 워크의 인터페이스를 사용합니다. 그것이 IList목록에 대한 인터페이스 라고 말하는 것은 상당히 쉬울 것 입니다.

  • - able접미사를 사용합니다 . 이 방법은 같은 자바 API에서 자주 볼 Comparable, Iterable그리고 Serializable몇 가지 이름을.


43
+1 난 단지 일관성 그렇다하더라도 당신이 .NETverse에있어 않는 한 나는 이름 * 규칙을 권장하지 것이지만
CurtainDog

7
나는 자바 코드에서이 규칙을 사용했습니다 내가 찾은 것이 유용 .... YMMV
hhafez

5
나는 이것이 다소 오래되었다는 것을 알고 있지만 지금 제안한 것은 인터페이스 이름을 명확하게 지정하고 구현 이름을 더 강하게 지정하는 것입니다. 예 : Interface = Store Implementation = StoreImpl 주로 인터페이스가 구현에 사용되어야 함을 강조합니다.
frostymarvelous

1
나는이 접근 방식이 옳게 보이지만 아키텍처 문제를 야기한다고 생각합니다. 예를 들어 인터페이스 만 사용하는 라이브러리를 정의한 다음 공용 라이브러리 인 인프라 계층에서 구현할 수있는 방법은 없습니다.
Luca Masera

1
구현에 "impl"접미사를 사용하지 않는 것과 같은 방식으로 인터페이스에 "I"접두사를 사용하지 않는다는 의견에 동의합니다. 다음은 이러한 경우를 반영하는 멋진 기사입니다. octoperf.com/blog/2016 / 10 / 27 / impl-classes-are-evil
raspacorp 2019-04-16

19

모든 언어에 대해 동일한 패키지에 함께 넣는 것이 좋습니다. 중요한 것은 외부 세계에 노출되는 것과 외부에서 어떻게 보이는지입니다. 구현이 동일한 패키지에 있는지 여부를 알거나 신경 쓰지 않을 것입니다.

이 특정 인스턴스를 살펴 보겠습니다.

하나의 패키지에 모든 공개 항목이 있고 공개적으로 노출되지 않은 다른 패키지에는 비공개 항목이있는 경우 라이브러리의 클라이언트는 하나의 패키지를 봅니다. 공개적으로 노출 된 항목이있는 패키지로 비공개 항목을 이동하지만 패키지 내에서 노출하지 않는 경우 클라이언트는 정확히 동일한 항목을 보게됩니다.

따라서 이것은 정당한 이유가없는 규칙의 냄새가납니다. 공개적으로 보이는 것에 영향을 미치지 않고 공개적으로 보이는 것에 기반하여 결정을 내리는 것입니다.

즉, 특정 인스턴스에서 인터페이스와 구현을 별도의 패키지로 분할하는 것이 좋은 생각 인 것 같으면 바로 진행하여 수행하십시오. 이 작업을 수행하는 이유는 패키지가 거대하거나 표준 구현 대신 링크 할 수있는 대체 구현이 있기 때문입니다.


질문에 'Java'태그가 주어지면 Java에서 ' 공개적으로 노출 된 패키지' 의 의미는 무엇 입니까? 아니면 귀하의 답변이 Java에만 국한되지 않습니까? 패키지 자체가 아닌 패키지 멤버 (예 : 클래스, 인터페이스 및 해당 멤버) 만 'public', 'private'등과 같은 액세스 제어 지정자를 가지고 있다고 생각했습니다. (이것은 @ question stackoverflow.com/questions/4388715/… )
bacar

방금 표현을 명확히했습니다. "공개적으로 노출 된 패키지"는 정말 "공개적으로 노출 된 패키지"를 의미했습니다.
CJS

확실히 모든 패키지에는 공개 된 것이 있어야합니다. 그렇지 않은 경우 패키지를 어떻게 사용할 수 있습니까?
bacar

이 특별한 경우에는 아무것도 공개되지 않은 패키지가 있었지만 공개 된 패키지에서 사용했습니다. 따라서 전자는 직접 사용할 수없고 후자를 통해 간접적으로 만 사용할 수 있습니다.
CJS

1
그러나 같은 질문이 적용됩니다. 사물을 공개 한 패키지는 아무것도 공개하지 않은 패키지를 어떻게 사용할 수 있습니까? 나는 자바에서 "전자는 직접적으로 사용할 수없고 후자를 통해서만 간접적으로 만 사용할 수있다." 구현 패키지에 전용 멤버 만있는 경우 클라이언트에서와 마찬가지로 인터페이스 패키지에서 액세스 할 수 없습니다. & 마찬가지로 다른 액세스 범위에 대해서도 마찬가지입니다. Java 에는 friend(C ++) 또는 internal(C #)에 해당하는 것이 없습니다 .
bacar

10

OSGi와 같은 많은 프레임 워크에서는 거의해야합니다. 나는 이것이 항아리 수준 대신 패키지에서 느슨한 결합을 촉진한다고 생각합니다.


7

인터페이스를 다른 패키지에 넣는 한 가지 주장은 제품 또는 서비스 소비자에게 배포 할 수있는 'api'jar를 만드는 것이 더 쉽다는 것입니다. 인터페이스와 구현을 함께 사용하여이 작업을 완벽하게 수행 할 수 있지만 다른 패키지에있는 경우 스크립트가 더 간단합니다.


7
API 프로젝트를 분리 (별도 배포 가능) 할 수 있으며 여전히 동일한 패키지에있는 인터페이스와 구현을 중지하지 않습니다. 테스트 된 클래스와 동일한 패키지에 단위 테스트가 있지만 여전히 별도의 빌드 아티팩트에있는 것과 비슷합니다. Jar와 패키지는 Java에서 거의 직교합니다.
George

7

Practical Software Engineering : A Case-Study Approach 라는 책 은 별도의 프로젝트 / 패키지에 인터페이스를 배치하는 것을 옹호합니다.

이 책에서 말하는 PCMEF + 아키텍처는 다음과 같은 원칙을 가지고 있습니다.

  1. 하향 종속성 원칙 (DDP)
  2. 상향 알림 원칙 (UNP)
  3. NCP (Neighbor Communication Principle)
  4. 명시 적 연결 원칙 (EAP)
  5. 주기 제거 원칙 (CEP)
  6. 클래스 명명 원칙 (CNP)
  7. 지인 패키지 원칙 (APP)

원칙 # 3 및 # 7에 대한 설명은 이것이 좋은 생각 인 이유를 설명합니다.

Neighbor Communication Principle은 패키지가 인접 패키지와 만 직접 통신 할 수 있도록 요구합니다. 이 원칙은 시스템이 상호 통신하는 물체의 비압축성 네트워크로 분해되지 않도록합니다. 이 원칙을 시행하기 위해 인접하지 않은 객체 사이의 메시지 전달은 위임을 사용합니다 (9.1.5.1 단원). 더 복잡한 시나리오에서는 아는 사람 패키지 (9.1.8.2 절)를 사용하여 인터페이스를 그룹화하여 먼 패키지와 관련된 협업을 지원할 수 있습니다.

지인 패키지 원칙은 이웃 커뮤니케이션 원칙의 결과입니다. 아는 사람 패키지는 메서드 호출에 대한 인수에서 구체적인 개체 대신 개체가 전달하는 인터페이스로 구성됩니다. 인터페이스는 모든 PCMEF 패키지에서 구현 될 수 있습니다. 이를 통해 인접하지 않은 패키지 간의 통신을 효과적으로 허용하면서 종속성 관리를 단일 아는 사람 패키지로 중앙 집중화 할 수 있습니다. 아는 사람 패키지의 필요성은 9.1.8.2 절에 설명되어 있으며 다음에 PCMEF 컨텍스트에서 다시 논의됩니다.

이 링크를 참조하십시오 : http://comp.mq.edu.au/books/pse/about_book/Ch9.pdf


5

예, 특정 구현을 게시하지 않고도 인터페이스를 게시 할 수 있기 때문에 매우 좋은 방법입니다. 즉, 외부 인터페이스를 게시 할 필요가없는 경우 구현과 동일한 패키지에 인터페이스 정의를 넣는 데 문제가 없습니다.


2
JCP에 대한 인터페이스를 정의하는 것이 좋을 수도 있지만 프로젝트를 위해 이러한 인터페이스의 구체적인 구현을 여러 개 생성하는 경우 인터페이스를 별도로 패키지화하는 것이 불필요한 부담처럼 보입니다.
Brian Reiter

2
어떤 경우에도 큰 부담은 아닙니다 ...
Paul Sonier 2009-06-17

4
-1 네, 그렇습니다. 구현을 찾기가 불필요하게 어렵고 함께 속한 것을 분리합니다.
Michael Borgwardt

5
그리고 구현을 게시하지 않는 것은 패키지 전용으로 만들 수있는 완전히 다른 문제이며 별도의 패키지에 배치하여 수행 할 수 없습니다. 비공개 패키지와 같은 것은 없습니다.
마이클 Borgwardt

1
또한 모든 인터페이스를 불필요하게 공개하는 것처럼 들립니다.
Adeel Ansari

4

우리는 내가 일하는 곳에서 이것을한다 (즉, 하나의 패키지에 인터페이스를 넣고 다른 패키지에 구현). 우리가 얻을 수있는 주요 이점은 구현간에 쉽게 교체 할 수 있다는 것입니다.


7
구현을 전환하는 데 어떻게 도움이됩니까?
Janusz

그것이 바로 우리가 여기 그 일을하는 이유입니다 카메론 교황에 의해 답을 참조
hhafez

3

Java 경험은 많지 않지만 인터페이스를 구현하는 구체적인 클래스가있는 어셈블리가 클라이언트는 웹 서비스 시나리오에서 중개인 공장이나 유선을 통해 프록시되기 때문입니다.


2

나는 일반적으로 인터페이스를 구현에 넣었지만 왜 그것들을 분리하고 싶은지 알 수 있습니다. 예를 들어, 누군가가 당신의 인터페이스를 기반으로 클래스를 다시 구현하고 싶었다고 가정 해보자. 그들은 단지 인터페이스보다는 당신의 구현과 함께 jar / lib / etc가 필요할 것이다. 분리 된 상태에서 "여기에 해당 인터페이스를 구현했습니다"라고 말하고 작업을 완료 할 수 있습니다. 내가 말했듯이, 내가하는 일이 아니라 일부 사람들이 왜 원하는지 알 수 있습니다.


2

나는 프로젝트에서 이것을하고 있으며 다음과 같은 이유로 잘 작동합니다.

  • 별도로 패키징 된 인터페이스 및 구현은 다양한 유형의 Map 또는 Set과 다른 사용 사례를위한 것입니다. 나무만을위한 패키지 (java.util.tree.Map, java.util.tree.Set)를 가질 이유가 없습니다. 이는 표준 데이터 구조이므로 다른 데이터 구조와 함께 사용하십시오. 그러나 정말 간단한 디버그 인터페이스와 예쁜 프로덕션 인터페이스가있는 퍼즐 게임을 다루는 경우 프런트 엔드의 일부로 com.your.app.skin.debug 및 com.your.app이있을 수 있습니다. .skin.pretty. 나는 그것들을 같은 패키지에 넣지 않을 것입니다. 왜냐하면 그것들은 다른 일을하기 때문이며, 동일한 패키지에 있다면 두 사람에 대한 비공식 네임 스페이스를 생성하기 위해 SmurfNamingConvention (DebugAttackSurface, DebugDefenceSurface, PrettyAttackSurface 등)에 의지 할 것이라는 것을 알고 있습니다.

  • 별도의 패키지에있는 관련 인터페이스 및 구현을 찾는 문제에 대한 해결 방법은 내 패키지에 대한 명명 구성을 채택하는 것입니다. 예를 들어 com.your.app.skin.framework에 모든 인터페이스를 가질 수 있으며 동일한 수준의 패키지 트리에있는 다른 패키지가 구현이라는 것을 알 수 있습니다. 단점은 이것이 색다른 관습이라는 것입니다. 솔직히 6 개월 만에이 대회가 얼마나 좋은지 보겠습니다. :)

  • 나는이 기술을 종교적으로 사용하지 않습니다. 특정 구현에서만 의미가있는 인터페이스가 있습니다. 나는 그것들을 프레임 워크 패키지에 붙이지 않는다. 40 개의 다른 구현 클래스를 만들 것 같지 않은 패키지가 있으므로 신경 쓰지 않습니다.

  • 내 응용 프로그램은 guice를 사용하고 인터페이스가 매우 많습니다.

프로그램 설계에 대한 질문은 일반적으로 장단점을 수반하며 모든 답을 한 번에 해결할 수있는 것은 아닙니다. 왜 작은 200 라인 프로그램에서이 기술을 사용하겠습니까? 내 다른 아키텍처 선택을 고려할 때 내 특정 문제에 대해 의미가 있습니다. :)


1

나는 그것들을 같은 패키지로 선호합니다. 인터페이스 전용 패키지를 만드는 것은 무의미합니다. 이는 인터페이스 접두사 및 접미사 (예 : Java의 경우 "I"및 "Impl")를 좋아하는 팀에 특히 중복됩니다.

인터페이스 세트를 공용 API로 게시해야하는 경우 완전히 별도의 프로젝트에 유지하고 대신 프로젝트 종속성을 만드는 것이 더 합리적입니다. 그러나 그것은 모두 내가 생각하는 상황의 선호도와 편리함으로 귀결됩니다.


0

프로젝트를 반영하는 패키지에 넣으십시오. 동일한 프로젝트의 일부인 경우 인터페이스와 구현을 함께 배치하는 것이 좋고 일반적이지만 API를 작성하는 경우 다른 사람이 프로젝트와 관련된 패키지 이름을 선택할 수 있습니다.

일반적으로 동일한 프로젝트에 대한 것이라면 인터페이스를 impls와 별도의 패키지로 유지하는 데 따른 이점이 없습니다. 복잡해지면 인터페이스 배열에 관계없이 다른 패키지 이름 지정 문제가있을 수 있습니다.


0

OSGi 프레임 워크의 경우 구현 패키지를 숨기면서 전체 패키지를 쉽게 내보낼 수 있도록 서로 다른 패키지에있는 것이 더 좋습니다.


-1

구현과는 별도의 패키지에 인터페이스를 넣는 데에는 많은 이유가 있습니다. 예를 들어 종속성 주입 을 지원하는 컨테이너를 사용 하여 런타임에 필요한 구현을 연결할 수 있습니다.이 경우 인터페이스 만 빌드시 존재하면되고 구현은 런타임에 제공 될 수 있습니다. 또는 여러 구현 (예 : 테스트를 위해 모의 구현 사용 )을 제공하거나 런타임에 특정 구현의 여러 버전 (예 : A / B 테스트 등)을 제공 할 수 있습니다. 이러한 종류의 사용 사례의 경우 인터페이스와 구현을 개별적으로 패키지화하는 것이 더 편리합니다.


당신은 당신이 나와있는 것들 중 하나 할 다른 패키지에 구현을 넣어 필요가 없습니다
Crowie

그것은 공평하지만, 여전히 요구 사항의 "빌드 타임 대 런타임에 존재"하는 차이는 개별적으로 패키징하는 강력한 사례라고 생각합니다. 나는 그것을 더 명확하게 반영하기 위해 텍스트를 업데이트했습니다.
Tyson
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.