기본 클래스와 동일한 파일에서 인터페이스를 선언하는 것이 좋습니다.


29

교환 가능하고 테스트 가능하려면 일반적으로 논리가있는 서비스에는 인터페이스가 있어야합니다. 예 :

public class FooService: IFooService 
{ ... }

디자인 측면에서 나는 이것에 동의하지만,이 접근법으로 나를 귀찮게하는 것 중 하나는 하나의 서비스에 대해 두 가지 (클래스와 인터페이스)를 선언해야하며 우리 팀에서는 일반적으로 두 개의 파일 (하나 클래스와 인터페이스의 경우). IDE (VS2010)에서 "Go to definition (정의로 이동)"을 사용하면 실제 클래스가 아닌 인터페이스 (다른 클래스가 인터페이스를 참조하므로)를 가리 키기 때문에 탐색에 어려움이 있습니다.

FooService와 동일한 파일에 IFooService를 작성하면 위의 이상한 점이 줄어들 것이라고 생각했습니다. 결국, IFooService와 FooService는 매우 관련이 있습니다. 이것이 좋은 습관입니까? IFooService가 자체 파일에 있어야하는 이유가 있습니까?


3
특정 인터페이스를 한 번만 구현 한 경우 인터페이스를 논리적으로 필요로하지 않습니다. 왜 수업을 직접 사용하지 않습니까?
Joris Timmermans

11
느슨한 결합 및 테스트 가능성을위한 @MadKeithV?
Louis Rhys

10
@ MadKeithV : 맞습니다. 당신이 IFooService에 의존하는 코드에 대한 단위 테스트를 작성할 때, 당신은 일반적으로 MockFooService, 제공 할 것 입니다 그 인터페이스의 두 번째 구현을.
Doc Brown

5
@DocBrown-여러 구현이있는 경우 분명히 주석이 사라지지만 "단일 구현"으로 직접 이동할 수있는 유틸리티도 있습니다 (최소한 두 개가 있기 때문에). 그런 다음 질문은 인터페이스를 사용하는 시점에서 아직 인터페이스 설명 / 문서에 포함되어 있지 않아야하는 구체적인 구현에 어떤 정보가 있는가입니다.
Joris Timmermans

답변:


24

자체 파일에있을 필요는 없지만 팀은 표준을 결정하고 준수해야합니다.

또한, "정의에 가서"인터페이스로 이동합니다,하지만 당신이있는 경우에 그 권리있어 ReSharper에서이 설치는 큰 문제가되지 않습니다 그래서, 그것은 그 인터페이스에서 파생 된 클래스 / 인터페이스 목록을 엽니 한 번의 클릭이 있습니다. 그래서 인터페이스를 별도의 파일로 유지합니다.

 


5
모든 IDE가 우리의 디자인에 맞게 조정되어야하고 다른 방법으로 조정하지 않으면이 대답에 동의합니다.
marktani

1
또한 Resharper가 없으면 F12와 Shift + F12는 거의 동일한 작업을 수행합니다. 마우스 오른쪽 버튼을 클릭하지 않아도 :) (공평하게 말하자면 Resharper 버전의 기능을 모를 경우 인터페이스를 직접 사용하는 것만 필요합니다)
Daniel B

@DanielB : Resharper에서 Alt-End는 구현으로 이동하거나 가능한 구현 목록을 제공합니다.
StriplingWarrior

@StriplingWarrior 그렇다면 바닐라 VS도 정확하게하는 것처럼 보입니다. F12 = 정의로 이동, Shift + F12 = 구현으로 이동 (Resharper 없이도 작동한다고 확신합니다. 가장 유용한 탐색 바로 가기 중 하나입니다. 단어를 널리 퍼 뜨리고 있습니다.
Daniel B

@DanielB : Shift-F12의 기본값은 "사용 찾기"입니다. jetbrains.com/resharper/webhelp/…
StriplingWarrior

15

나는 당신이 그것들을 별도의 파일로 유지해야한다고 생각합니다. 당신이 말했듯이, 아이디어는 테스트 가능하고 상호 교환 가능하게 유지하는 것입니다. 인터페이스를 구현과 동일한 파일에 배치하면 인터페이스를 특정 구현과 연관시킵니다. 모의 객체 또는 다른 구현을 생성하기로 결정한 경우 인터페이스가 FooService와 논리적으로 분리되지 않습니다.


10

SOLID에 따르면 인터페이스를 작성해야 할뿐만 아니라 다른 파일에 있어야 할뿐만 아니라 다른 어셈블리에 있어야합니다.

왜? 어셈블리로 컴파일되는 소스 파일을 변경하려면 어셈블리를 다시 컴파일해야하며 어셈블리를 변경하려면 종속 어셈블리를 다시 컴파일해야합니다. 따라서 SOLID를 기반으로하는 목표가 구현 A를 구현 B로 대체 할 수있는 경우 클래스 C가 인터페이스 I에 의존하지 않아도 차이점을 알 필요는 없지만 어셈블리를 I로 확인해야합니다. 변경되지 않으므로 사용법을 보호합니다.

"그러나 그것은 단지 재 컴파일 일뿐입니다." 스마트 폰 앱에있을 수도 있지만 사용자의 데이터 대역폭에서 더 쉽습니다. 변경된 바이너리 하나를 다운로드하거나 해당 바이너리와 그에 의존하는 코드로 바이너리를 5 개 다운로드합니까? LAN상의 데스크탑 컴퓨터가 모든 프로그램을 사용하도록 작성된 것은 아닙니다. 대역폭과 메모리가 저렴한 경우에도 작은 패치 릴리스는 Active Directory 또는 유사한 도메인 관리 계층을 통해 전체 LAN으로 푸시하는 것이 쉽지 않기 때문에 작은 패치 릴리스는 가치가 있습니다. 사용자는 다음에 로그인 할 때 몇 분이 아닌 몇 분만 적용되어 전체를 다시 설치할 수 있습니다. 말할 것도없이, 프로젝트를 빌드 할 때 다시 컴파일해야하는 어셈블리 수가 적을수록 더 빠르게 빌드됩니다.

면책 조항 : 항상 가능하지는 않습니다. 가장 쉬운 방법은 중앙 집중식 "인터페이스"프로젝트를 만드는 것입니다. 여기에는 단점이 있습니다. 인터페이스 프로젝트와 구현 프로젝트는 퍼시스턴스 레이어 나 앱의 다른 주요 컴포넌트를 재사용하는 다른 앱에서 참조되어야하기 때문에 코드 재사용 성이 떨어집니다. 인터페이스를 더 밀접하게 결합 된 어셈블리로 분할하여이 문제를 극복 할 수 있지만 앱에 더 많은 프로젝트가있어 전체 빌드가 매우 어려워집니다. 핵심은 균형과 느슨하게 결합 된 디자인을 유지하는 것입니다. 일반적으로 필요에 따라 파일을 이동할 수 있으므로 클래스에 많은 변경이 필요하거나 인터페이스의 새로운 구현이 정기적으로 필요할 것입니다 (아마도 새로 지원되는 다른 버전의 소프트웨어와 인터페이스하는 경우)


8

별도의 파일이 불편한 이유는 무엇입니까? 나에게는 훨씬 깔끔하고 깨끗합니다. 솔루션 탐색기에서 더 적은 파일을 보려면 "Interfaces"라는 하위 폴더를 만들고 IFooServer.cs 파일을 고정시키는 것이 일반적입니다.

인터페이스가 자체 파일에 정의 된 이유는 클래스가 일반적으로 자체 파일에 정의되어있는 것과 같은 이유입니다. 논리 구조와 파일 구조가 동일하면 프로젝트 관리가 더 간단하므로 주어진 클래스가 정의 된 파일을 항상 알 수 있습니다 이를 통해 디버깅 (예외 스택 추적은 일반적으로 파일과 줄 번호를 제공함) 또는 소스 제어 저장소에 소스 코드를 병합 할 때 더 쉽게 사용할 수 있습니다.


6

코드 파일에 단일 클래스 또는 단일 인터페이스 만 포함시키는 것이 좋습니다. 그러나 이러한 코딩 관행은 코드를보다 효과적으로 구성하여 작업하기 쉽도록하기위한 수단입니다. 당신과 당신의 팀이라면, 클래스가 그들의 인터페이스와 함께 유지된다면 더 쉽게 작업 할 수 있다는 것을 알게됩니다.

개인적으로 나는 당신의 경우처럼 인터페이스를 구현하는 클래스가 하나뿐 일 때 동일한 파일에 인터페이스와 클래스를 갖는 것을 선호합니다.

내비게이션 관련 문제와 관련하여 ReSharper를 적극 권장 합니다. 특정 인터페이스 메소드를 구현하는 메소드로 바로 이동할 수있는 매우 유용한 단축키가 포함되어 있습니다.


3
+1 팀의 관행이 파일 구조를 지시하고 표준 접근 방식과 반대되는 점을 지적해야합니다.

3

인터페이스가 클래스 에 대한 별도의 파일 일뿐 아니라 별도의 어셈블리 에 있기를 원하는 경우가 많습니다 .

예를 들어, 회선의 양쪽 끝을 제어 할 수있는 경우 WCF 서비스 계약 인터페이스를 클라이언트와 서비스 가 공유 할 수 있습니다 . 인터페이스를 자체 어셈블리로 이동하면 자체 어셈블리 종속성이 줄어 듭니다. 이를 통해 클라이언트는 소비를 훨씬 쉽게하고 구현과의 결합을 느슨하게합니다.


2

인터페이스 1 의 단일 구현을 갖는 것은 이치에 맞지 않습니다 . 공용 인터페이스와 해당 인터페이스를 구현하는 공용 클래스를 동일한 파일에 넣으면 인터페이스가 필요하지 않을 가능성이 큽니다.

인터페이스와 함께 위치하는 클래스가 abstract 이고 인터페이스의 모든 구현이 해당 추상 클래스를 상속해야한다는 것을 알고 있으면 두 파일을 같은 파일에 배치하는 것이 좋습니다. 당신은 여전히 ​​인터페이스를 사용하기로 한 결정을 면밀히 조사해야합니다. 추상 클래스 자체가 좋은지 스스로에게 물어보고 대답이 긍정적이면 인터페이스를 삭제하십시오.

그러나 일반적으로 "하나의 공용 클래스 / 인터페이스는 하나의 파일에 해당"전략을 고수해야합니다. 따라하기 쉽고 소스 트리를보다 쉽게 ​​탐색 할 수 있습니다.


1 모의 프레임 워크를 선택하면 코드에이 추가 요구 사항이 적용되므로 테스트 목적으로 인터페이스가 필요한 경우는 예외입니다.


3
실제로 .NET에서 하나의 클래스에 대해 하나의 인터페이스를 갖는 것은 매우 일반적인 패턴입니다. 단위 테스트는 모의, 스터브, 스파이 또는 다른 테스트 더블로 종속성을 대체 할 수 있기 때문입니다.
Pete

2
@Pete 나는 이것을 메모로 포함하도록 대답을 편집했습니다. 이 패턴을 "정상"이라고 부르지 않을 것입니다. 테스트 가능성 문제가 기본 코드베이스에 "누설"되기 때문입니다. 최신 조롱 프레임 워크는이 문제를 상당 부분 극복 할 수 있도록 도와 주지만, 인터페이스가 있으면 많은 일이 간단 해집니다.
dasblinkenlight

2
@KeithS 인터페이스가없는 클래스는 서브 클래스로 이해가되지 않는다는 것을 확신 할 수 없을 때만 디자인에 포함되어야합니다 sealed. 나중에 언젠가 두 번째 서브 클래스를 추가 할 수 있다고 생각되면 즉시 인터페이스를 작성하십시오. PS 적절한 품질의 리팩토링 보조자가 단일 ReSharper 라이센스의 적당한 가격으로 귀하가 될 수 있습니다. :)
dasblinkenlight

1
@KeithS 그리고 서브 클래 싱을 위해 특별히 설계되지 않은 모든 클래스는 봉인되어야합니다. 실제로, 클래스가 기본적으로 봉인되어 있기 때문에 현재 언어에서 클래스를 표시하여 재정의 할 함수를 지정하도록하는 것과 같은 방식으로 상속을위한 클래스를 명시 적으로 지정해야합니다 virtual.
dasblinkenlight

2
@ KeithS : 하나의 구현이 두 개가되면 어떻게됩니까? 구현이 변경 될 때와 동일합니다. 변경 사항을 반영하도록 코드를 변경합니다. YAGNI가 여기에 적용됩니다. 앞으로 어떤 변화가 일어날 지 절대 알 수 없으므로 가장 간단한 일을하십시오. (불행히도이 최대 값으로 엉망 테스트)
Groky

2

인터페이스는 Robert C. Martin의 Agile Principles, Practices and Patterns에 정의 된대로 구현이 아닌 클라이언트에 속합니다. 따라서 동일한 장소에서 인터페이스와 구현을 결합하는 것은 원칙에 위배됩니다.

클라이언트 코드는 인터페이스에 따라 다릅니다. 이를 통해 구현 하지 않고도 클라이언트 코드와 인터페이스를 컴파일하고 배포 할 수 있습니다 . 클라이언트 코드에 대한 플러그인 처럼 작동하는 다른 구현을 가질 수 있습니다 .

업데이트 : 이것은 민첩한 원칙이 아닙니다. 94 년으로 거슬러 올라가는 Design Patterns의 Gang of Four는 이미 인터페이스를 준수하고 인터페이스를 프로그래밍하는 클라이언트에 대해 이야기하고 있습니다. 그들의 견해는 비슷합니다.


1
인터페이스 사용은 애자일이 소유 한 영역을 훨씬 뛰어 넘습니다. 애자일은 특정 방식으로 언어 구성을 사용하는 개발 방법론입니다. 인터페이스는 언어 구성입니다.

1
예, 그러나 우리가 민첩한 것에 대해 이야기하고 있다는 사실에 관계없이 인터페이스는 여전히 클라이언트에 속하며 구현이 아닙니다.
Patkos Csaba

나는 이것에 당신과 함께 있습니다.
Marjan Venema

0

나는 개인적으로 인터페이스 앞에서 "I"를 좋아하지 않는다. 이러한 유형의 사용자로서 이것이 인터페이스인지 여부를 원하지 않습니다. 유형은 흥미로운 것입니다. 의존성 측면에서 FooService는 IFooService의 한 가지 가능한 구현입니다. 인터페이스는 사용되는 장소 근처에 있어야합니다. 반면에 구현은 클라이언트에 영향을주지 않고 쉽게 변경할 수있는 장소에 있어야합니다. 따라서 두 파일을 선호합니다. 일반적으로.


2
사용자는 유형이 인터페이스인지 여부를 알고 싶습니다. 예를 들어 유형을 사용할 수 없다는 의미이기 때문에 new다른 유형에서는 공변 또는 반 변형으로 사용할 수 있습니다. 그리고는 I.Net에서 확립 된 명명 규칙이므로 다른 유형과의 일관성을 위해서만 준수해야합니다.
svick

I인터페이스에서 시작 하는 것은 두 파일의 분리에 대한 나의 주장에 대한 소개 일뿐입니다. 코드는 더 읽기 쉽고 종속성의 반전을 더 명확하게 만듭니다.
ollins

4
.NET에서는 인터페이스 이름 앞에 'I'를 사용하는 것이 사실상 표준입니다. .NET 프로젝트의 표준을 따르지 않으면 내 관점에서 '최소한 놀람의 원칙'을 위반하는 것입니다.
Pete

내 요점은 두 파일로 분리되어 있습니다. 하나는 추상화를위한 것이고 다른 하나는 구현을위한 것입니다. 사용 I환경이 정상인 경우 : 사용하십시오! (그러나 나는 그것을 좋아하지 않는다 :))
ollins

그리고 P.SE의 경우 인터페이스 앞에 I 접두사를 붙이면 포스터가 다른 클래스에서 상속하지 않고 인터페이스에 대해 이야기하고 있음을 더 분명하게 알 수 있습니다.

0

인터페이스 코딩은 좋지 않을 수 있으며, 실제로 특정 상황에 대한 반 패턴으로 취급되며 법이 아닙니다. 일반적으로 안티 패턴 인터페이스에 코딩하는 좋은 예는 애플리케이션에서 구현이 하나 뿐인 인터페이스가있는 경우입니다. 인터페이스 파일이 너무 귀찮아서 구현 클래스 파일에서 선언을 숨겨야하는 경우도 있습니다.


인터페이스 응용 프로그램에서 구현이 하나만있는 것이 반드시 나쁜 것은 아닙니다. 기술 변화로부터 애플리케이션을 보호 할 수 있습니다. 예를 들어, 데이터 지속성 기술을위한 인터페이스입니다. 현재 데이터 지속성 기술에 대한 구현은 하나 뿐이지 만 변경 될 경우 앱을 보호해야합니다. 그 당시에는 한 명의 임프 만있었습니다.
TSmith

0

클래스와 인터페이스를 동일한 파일에 넣는 방법 중 하나를 사용하는 방법에는 제한이 없습니다. 인터페이스는 그것을 구현하는 클래스와 동일한 파일에 있더라도 동일한 방법으로 모의 등을 위해 여전히 사용될 수 있습니다. 이 질문은 순전히 조직의 편의 중 하나로 인식 될 수 있습니다.이 경우 저는 여러분의 삶을 편하게 해주는 모든 일을합니다!

물론, 같은 파일에 두 파일을두면 실제보다 더 프로그래밍 방식으로 결합 된 것으로 간주하는 것이 위험 할 수 있습니다.

개인적으로 하나의 규칙을 사용하는 코드베이스를 발견하면 어느 쪽도 걱정하지 않아도됩니다.


그것은 단지 "조직적 편의성"이 아니라 위험 관리, 배포 관리, 코드 추적 성, 변경 관리, 소스 제어 노이즈, 보안 관리의 문제이기도합니다. 이 질문에는 많은 각도가 있습니다.
TSmith
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.