"인터페이스 프로그래밍"이란 무엇을 의미합니까?
나는 이것을 몇 번 언급했지만 그것이 무엇을 의미하는지 명확하지 않습니다. 언제 그리고 왜 이것을 하시겠습니까?
나는 인터페이스가 무엇을하는지 알고 있지만 이것에 대해 명확하지 않다는 사실은 내가 그것들을 올바르게 사용하는 것을 놓치고 있다고 생각하게 만든다.
다음과 같은 경우에만 해당됩니까?
IInterface classRef = new ObjectWhatever()
구현하는 모든 클래스를 사용할 수 IInterface
있습니까? 언제해야합니까? 내가 생각할 수있는 유일한 방법은 메소드가 있고 구현하는 것을 제외하고 어떤 객체가 전달 될지 확신 할 수없는 경우입니다 IInterface
. 얼마나 자주 그렇게해야할지 모르겠습니다.
또한 인터페이스를 구현하는 객체를받는 메서드를 어떻게 작성할 수 있습니까? 가능합니까?
여기에는 인터페이스와 느슨하게 결합 된 코드, 제어 반전 등에 대한 모든 종류의 훌륭한 세부 사항을 다루는이 질문에 대한 멋진 답변이 있습니다. 상당히 진지한 토론이 있기 때문에 인터페이스가 유용한 이유를 이해하기 위해 약간의 내용을 분석 할 기회를 갖고 싶습니다.
처음 인터페이스에 노출되기 시작했을 때 나도 그 관련성에 대해 혼란 스러웠습니다. 왜 필요한지 이해하지 못했습니다. Java 또는 C #과 같은 언어를 사용하는 경우 이미 상속이 있고 인터페이스를 더 약한 상속 형식으로보고 "왜 귀찮게합니까?"라고 생각했습니다. 어떤 의미에서 인터페이스는 일종의 약한 상속 형태라고 생각할 수 있습니다.하지만 그 이상으로 인터페이스를 언어 구조로 사용하는 것을 마침내 이해했습니다. 잠재적으로 많은 관련되지 않은 객체 클래스.
예를 들어 SIM 게임이 있고 다음과 같은 클래스가 있다고 가정합니다.
class HouseFly inherits Insect {
void FlyAroundYourHead(){}
void LandOnThings(){}
}
class Telemarketer inherits Person {
void CallDuringDinner(){}
void ContinueTalkingWhenYouSayNo(){}
}
분명히이 두 개체는 직접 상속 측면에서 공통점이 없습니다. 그러나 둘 다 짜증 난다고 말할 수 있습니다.
하자 우리의 게임은 임의의 일종 가질 필요가 말하는 것은 그들이 저녁 식사를 먹을 때 게임 플레이어를 괴롭힌다. 이것은 HouseFly
하나 Telemarketer
또는 둘 다일 수 있지만 단일 기능으로 두 가지를 어떻게 허용합니까? 그리고 각기 다른 유형의 개체에 동일한 방식으로 "성가신 일을 수행"하도록 요청하는 방법은 무엇입니까?
깨달아야 할 핵심은 a Telemarketer
와 둘 다 HouseFly
모델링 측면에서 비슷하지 않더라도 일반적으로 느슨하게 해석 된 행동을 공유한다는 것입니다. 따라서 둘 다 구현할 수있는 인터페이스를 만들어 보겠습니다.
interface IPest {
void BeAnnoying();
}
class HouseFly inherits Insect implements IPest {
void FlyAroundYourHead(){}
void LandOnThings(){}
void BeAnnoying() {
FlyAroundYourHead();
LandOnThings();
}
}
class Telemarketer inherits Person implements IPest {
void CallDuringDinner(){}
void ContinueTalkingWhenYouSayNo(){}
void BeAnnoying() {
CallDuringDinner();
ContinueTalkingWhenYouSayNo();
}
}
이제 우리는 각자의 방식으로 성가 시게 할 수있는 두 개의 클래스가 있습니다. 그리고 그들은 동일한 기본 클래스에서 파생 될 필요가 없으며 공통 고유의 특성을 공유 할 필요가 없습니다. 단순히 계약을 충족하기 IPest
만하면됩니다. 그 계약은 간단합니다. 당신은 BeAnnoying
. 이와 관련하여 다음을 모델링 할 수 있습니다.
class DiningRoom {
DiningRoom(Person[] diningPeople, IPest[] pests) { ... }
void ServeDinner() {
when diningPeople are eating,
foreach pest in pests
pest.BeAnnoying();
}
}
여기에는 많은 식당과 여러 해충을 수용하는 식당이 있습니다. 인터페이스 사용에 유의하십시오. 이것은 우리의 작은 세계에서 pests
배열 의 구성원 이 실제로 Telemarketer
객체 또는 객체 가 될 수 있음을 의미 HouseFly
합니다.
이 ServeDinner
메서드는 저녁 식사가 제공되고 식당에있는 사람들이 식사를해야 할 때 호출됩니다. 우리의 작은 게임에서 해충이 일을 할 때입니다. 각 해충은 IPest
인터페이스 를 통해 성가 시도록 지시받습니다 . 이러한 방법으로, 우리는 쉽게 모두를 가질 수 Telemarketers
와 HouseFlys
자신의 방법으로 각 성가신 일 - 우리는 우리가 뭔가 가지고에만 신경 DiningRoom
해충이다 오브젝트를, 우리는 정말 그것이 무엇인지 상관 없어 그들은에서 아무것도 할 수 다른 사람과 공통입니다.
이 매우 인위적인 의사 코드 예제 (예상했던 것보다 훨씬 더 오래 끌 렸음)는 인터페이스를 사용할 수 있다는 측면에서 마침내 저에게 불이 들어온 종류를 설명하기위한 것입니다. 예제의 어리 석음에 대해 미리 사과 드리지만 이해에 도움이되기를 바랍니다. 그리고 여기에 게시 된 다른 답변은 오늘날 디자인 패턴 및 개발 방법론에서 인터페이스 사용의 범위를 실제로 다룹니다.
제가 학생들에게 줄 때 사용한 구체적인 예는
List myList = new ArrayList(); // programming to the List interface
대신에
ArrayList myList = new ArrayList(); // this is bad
이것들은 쇼트 프로그램에서도 똑같아 보이지만, 프로그램에서 myList
100 번 사용 하면 차이를 볼 수 있습니다. 첫 번째 선언은 인터페이스에 myList
의해 정의 된 메서드 만 호출하도록합니다 List
( ArrayList
특정 메서드 없음 ). 이런 식으로 인터페이스를 프로그래밍했다면 나중에 정말 필요한지 결정할 수 있습니다.
List myList = new TreeList();
한 지점에서 코드를 변경하기 만하면됩니다. 나머지 코드는 인터페이스로 프로그래밍했기 때문에 구현 을 변경하여 손상되는 작업을 수행하지 않는다는 것을 이미 알고 있습니다 .
메서드 매개 변수와 반환 값에 대해 이야기 할 때 이점이 훨씬 더 분명합니다 (제 생각에는). 예를 들면 다음과 같습니다.
public ArrayList doSomething(HashMap map);
이 메서드 선언은 두 가지 구체적인 구현 ( ArrayList
및 HashMap
)에 연결됩니다. 다른 코드에서 해당 메서드가 호출 되 자마자 해당 유형이 변경되면 호출 코드도 변경해야합니다. 인터페이스에 프로그래밍하는 것이 좋습니다.
public List doSomething(Map map);
이제 여러분이 어떤 종류 List
를 반환하는지, 어떤 종류 Map
가 매개 변수로 전달 되는지 는 중요하지 않습니다 . doSomething
메서드 내에서 변경 한 내용은 호출 코드를 강제로 변경하지 않습니다.
인터페이스 프로그래밍은 "이 기능이 필요하고 어디에서 왔는지 상관하지 않습니다."라고 말합니다.
(Java에서) List
인터페이스 ArrayList
와 LinkedList
구체적인 클래스를 고려하십시오 . 내가 신경 쓰는 것은 반복을 통해 액세스해야하는 여러 데이터 항목을 포함하는 데이터 구조가 있다는 것 List
뿐이라면 , (이것이 99 %의 시간입니다) 선택합니다. 목록의 양쪽 끝에서 일정한 시간 삽입 / 삭제가 필요하다는 것을 알고 있다면 LinkedList
구체적인 구현을 선택할 수 있습니다 (또는 Queue 인터페이스를 사용할 가능성이 더 높습니다 ). 인덱스에 의한 임의 액세스가 필요하다는 것을 알고 있다면 ArrayList
구체적인 클래스를 선택합니다 .
인터페이스를 사용하는 것은 클래스 간의 불필요한 결합을 제거하는 것 외에도 코드를 쉽게 테스트 할 수있는 핵심 요소입니다. 클래스에 대한 작업을 정의하는 인터페이스를 만들면 해당 기능을 사용하려는 클래스가 클래스를 직접 구현하지 않고도 사용할 수 있습니다. 나중에 다른 구현을 변경하고 사용하기로 결정한 경우 구현이 인스턴스화되는 코드 부분 만 변경하면됩니다. 나머지 코드는 구현 클래스가 아닌 인터페이스에 따라 달라 지므로 변경할 필요가 없습니다.
이것은 단위 테스트를 만드는 데 매우 유용합니다. 테스트중인 클래스에서 인터페이스에 의존하고 생성자 또는 속성 설정자를 통해 인터페이스의 인스턴스를 클래스 (또는 필요에 따라 인터페이스의 인스턴스를 빌드 할 수있는 팩토리)에 삽입합니다. 클래스는 메서드에서 제공된 (또는 생성 된) 인터페이스를 사용합니다. 테스트를 작성할 때 인터페이스를 모의 또는 가짜로 만들고 단위 테스트에 구성된 데이터로 응답하는 인터페이스를 제공 할 수 있습니다. 테스트중인 클래스가 구체적인 구현이 아닌 인터페이스 만 다루기 때문에 이렇게 할 수 있습니다. 모의 또는 가짜 클래스를 포함하여 인터페이스를 구현하는 모든 클래스가 수행합니다.
편집 : 아래는 Erich Gamma가 "구현이 아닌 인터페이스에 대한 프로그램"이라는 그의 인용문에 대한 링크입니다.
http://www.artima.com/lejava/articles/designprinciples.html
Inversion of Control을 살펴 봐야합니다.
이러한 시나리오에서는 다음과 같이 작성하지 않습니다.
IInterface classRef = new ObjectWhatever();
다음과 같이 작성합니다.
IInterface classRef = container.Resolve<IInterface>();
이것은 container
객체 의 규칙 기반 설정으로 이동하여 ObjectWhatever가 될 수있는 실제 객체를 구성합니다. 중요한 것은이 규칙을 다른 유형의 객체를 모두 사용하는 것으로 대체 할 수 있으며 코드는 계속 작동한다는 것입니다.
IoC를 테이블에서 제외하면 특정 작업을 수행 하는 객체와 통신 할 수 있지만 어떤 유형의 객체 나 어떻게 수행하는지 는 알 수없는 코드를 작성할 수 있습니다 .
이것은 매개 변수를 전달할 때 유용합니다.
"또한 인터페이스를 구현하는 객체를 취하는 메서드를 어떻게 작성할 수 있습니까? 가능합니까?"라는 괄호로 묶인 질문에 대해 C #에서는 다음과 같이 매개 변수 유형에 인터페이스 유형을 사용합니다.
public void DoSomethingToAnObject(IInterface whatever) { ... }
이것은 "특정한 일을하는 대상과의 대화"에 바로 연결됩니다. 위에 정의 된 메서드는 개체에서 무엇을 기대해야하는지 알고 있으며 IInterface에서 모든 것을 구현하지만 어떤 유형의 개체인지는 신경 쓰지 않고 인터페이스가 무엇인지 계약을 준수한다는 점만 중요합니다.
예를 들어, 당신은 아마도 계산기에 익숙 할 것이고 아마도 당신의 날에 꽤 많이 사용했을 것입니다. 그러나 대부분의 경우 그것들은 모두 다릅니다. 반면에 표준 계산기가 어떻게 작동해야하는지 알고 있으므로 각 계산기가 가지고있는 특정 기능을 사용할 수없는 경우에도 모두 사용할 수 있습니다.
이것이 인터페이스의 아름다움입니다. 특정 동작을 기대할 수있는 객체를 전달받을 것이라는 것을 알고있는 코드를 작성할 수 있습니다. 그것이 어떤 종류의 물건인지는 신경 쓰지 않고 필요한 행동을 지원하는 것뿐입니다.
구체적인 예를 들어 보겠습니다.
우리는 Windows 양식을위한 맞춤형 번역 시스템을 가지고 있습니다. 이 시스템은 양식의 컨트롤을 반복하고 각각의 텍스트를 번역합니다. 시스템은 텍스트 속성이있는 제어 유형 및 유사한 기본 항목과 같은 기본 제어를 처리하는 방법을 알고 있지만 기본적으로는 부족합니다.
이제 컨트롤은 제어 할 수없는 미리 정의 된 클래스에서 상속되므로 다음 세 가지 중 하나를 수행 할 수 있습니다.
- 번역 시스템에 대한 지원을 구축하여 작업중인 제어 유형을 구체적으로 감지하고 올바른 비트를 번역합니다 (유지 관리 악몽).
- 기본 클래스로 지원 빌드 (모든 컨트롤이 미리 정의 된 다른 클래스에서 상속되기 때문에 불가능)
- 인터페이스 지원 추가
그래서 우리는 nr을했습니다. 3. 모든 컨트롤은 "자체"를 번역 텍스트 / 규칙의 컨테이너로 번역하는 기능인 하나의 메서드를 제공하는 인터페이스 인 ILocalizable을 구현합니다. 따라서 폼은 어떤 종류의 컨트롤을 찾았는지 알 필요가 없으며 특정 인터페이스를 구현하고 컨트롤을 지역화하기 위해 호출 할 수있는 메서드가 있다는 것만 알고 있습니다.
인터페이스에 대한 프로그래밍은 Java 또는 .NET에서 볼 수있는 추상 인터페이스와는 전혀 관련이 없습니다. OOP 개념도 아닙니다.
이것이 의미하는 바는 객체 나 데이터 구조의 내부를 엉망으로 만들지 않는다는 것입니다. 추상 프로그램 인터페이스 또는 API를 사용하여 데이터와 상호 작용합니다. Java 또는 C #에서는 원시 필드 액세스 대신 공용 속성 및 메서드를 사용하는 것을 의미합니다. C의 경우 원시 포인터 대신 함수를 사용하는 것을 의미합니다.
편집 : 그리고 데이터베이스에서는 직접 테이블 액세스 대신 뷰와 저장 프로 시저를 사용하는 것을 의미합니다.
구현이 아닌 인터페이스에 대한 코드는 Java 또는 인터페이스 구성과 관련이 없습니다.
이 개념은 Patterns / Gang of Four 책에서 눈에 띄게되었지만 아마도 그 이전에 있었을 것입니다. 이 개념은 Java가 존재하기 훨씬 전에 확실히 존재했습니다.
자바 인터페이스 구조는이 아이디어를 돕기 위해 만들어졌고 사람들은 원래 의도보다는 의미의 중심으로서 구조에 너무 집중하게되었습니다. 그러나 이것이 우리가 Java, C ++, C # 등에서 공용 및 개인 메서드와 속성을 갖는 이유입니다.
이는 객체 또는 시스템의 공용 인터페이스와 상호 작용하는 것을 의미합니다. 내부적으로 수행하는 작업을 걱정하거나 예상하지 마십시오. 구현 방법에 대해 걱정하지 마십시오. 객체 지향 코드에서 우리가 공개 대 비공개 메소드 / 속성을 갖는 이유입니다. private 메서드는 클래스 내에서 내부적으로 만 사용하기 때문에 공용 메서드를 사용하도록 의도되었습니다. 클래스 구현을 구성하며 공용 인터페이스를 변경하지 않고 필요에 따라 변경할 수 있습니다. 기능과 관련하여 클래스의 메서드가 동일한 매개 변수로 호출 할 때마다 동일한 예상 결과로 동일한 작업을 수행한다고 가정합니다. 이를 통해 작성자는 사람들이 상호 작용하는 방식을 깨지 않고 클래스 작동 방식, 구현 방식을 변경할 수 있습니다.
또한 Interface 구성을 사용하지 않고 구현이 아닌 인터페이스에 프로그래밍 할 수 있습니다. Interface 구성이없는 C ++의 구현이 아닌 인터페이스로 프로그래밍 할 수 있습니다. 두 개의 대규모 엔터프라이즈 시스템은 시스템 내부의 개체에 대한 메서드를 호출하는 대신 공용 인터페이스 (계약)를 통해 상호 작용하는 한 훨씬 더 강력하게 통합 할 수 있습니다. 인터페이스는 동일한 입력 매개 변수가 주어지면 항상 동일한 예상 방식으로 반응해야합니다. 구현이 아닌 인터페이스에 구현 된 경우 이 개념은 여러 곳에서 작동합니다.
Java 인터페이스가 '구현이 아닌 인터페이스로의 프로그래밍'개념과 관련하여 무엇이든 할 수 있다는 생각을 흔들어보십시오. 그들은 개념을 적용하는 데 도움을 줄 수 있지만 개념이 아닙니다 .
인터페이스의 작동 방식을 이해하는 것 같지만 언제 사용하고 어떤 이점을 제공하는지 확실하지 않습니다. 다음은 인터페이스가 적합한 경우의 몇 가지 예입니다.
// if I want to add search capabilities to my application and support multiple search
// engines such as google, yahoo, live, etc.
interface ISearchProvider
{
string Search(string keywords);
}
그런 다음 GoogleSearchProvider, YahooSearchProvider, LiveSearchProvider 등을 만들 수 있습니다.
// if I want to support multiple downloads using different protocols
// HTTP, HTTPS, FTP, FTPS, etc.
interface IUrlDownload
{
void Download(string url)
}
// how about an image loader for different kinds of images JPG, GIF, PNG, etc.
interface IImageLoader
{
Bitmap LoadImage(string filename)
}
그런 다음 JpegImageLoader, GifImageLoader, PngImageLoader 등을 만듭니다.
대부분의 추가 기능 및 플러그인 시스템은 인터페이스에서 작동합니다.
또 다른 널리 사용되는 용도는 Repository 패턴입니다. 다른 소스의 우편 번호 목록을로드하고 싶다고 가정 해 보겠습니다.
interface IZipCodeRepository
{
IList<ZipCode> GetZipCodes(string state);
}
그런 다음 XMLZipCodeRepository, SQLZipCodeRepository, CSVZipCodeRepository 등을 만들 수 있습니다. 웹 응용 프로그램의 경우 종종 초기에 XML 리포지토리를 만들어 Sql 데이터베이스가 준비되기 전에 무언가를 시작하고 실행할 수 있습니다. 데이터베이스가 준비되면 XML 버전을 대체하기 위해 SQLRepository를 작성합니다. 나머지 코드는 인터페이스에서 단독으로 실행되기 때문에 변경되지 않습니다.
메소드는 다음과 같은 인터페이스를 허용 할 수 있습니다.
PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
{
Console.WriteLine(zipCode.ToString());
}
}
비슷한 클래스 집합이있을 때 코드를 훨씬 더 확장하고 유지 관리하기 쉽습니다. 저는 주니어 프로그래머이므로 전문가는 아니지만 비슷한 것을 요구하는 프로젝트를 막 끝냈습니다.
저는 의료 기기를 실행하는 서버와 통신하는 클라이언트 측 소프트웨어를 작업합니다. 고객이 때때로 구성해야하는 몇 가지 새로운 구성 요소가있는이 장치의 새 버전을 개발 중입니다. 새로운 구성 요소에는 두 가지 유형이 있으며 서로 다르지만 매우 유사합니다. 기본적으로 두 개의 구성 양식, 두 개의 목록 클래스, 모든 것 중 두 개를 만들어야했습니다.
거의 모든 실제 논리를 포함하는 각 제어 유형에 대해 추상 기본 클래스를 만든 다음 두 구성 요소 간의 차이점을 처리하기 위해 파생 된 유형을 만드는 것이 가장 좋습니다. 그러나 항상 유형에 대해 걱정해야한다면 기본 클래스는 이러한 구성 요소에 대한 작업을 수행 할 수 없었을 것입니다. .
이러한 구성 요소에 대한 간단한 인터페이스를 정의했으며 모든 기본 클래스가이 인터페이스와 통신합니다. 이제 내가 무언가를 변경하면 거의 모든 곳에서 '그냥 작동'하며 코드 중복이 없습니다.
Java로 프로그래밍하는 경우 JDBC가 좋은 예입니다. JDBC는 인터페이스 세트를 정의하지만 구현에 대해서는 언급하지 않습니다. 이 인터페이스 세트에 대해 애플리케이션을 작성할 수 있습니다. 이론적으로는 JDBC 드라이버를 선택하면 응용 프로그램이 작동합니다. 더 빠르거나 "더 좋거나"더 저렴한 JDBC 드라이버가 있거나 어떤 이유로 든 이론상 속성 파일을 다시 구성 할 수 있으며 응용 프로그램을 변경하지 않고도 응용 프로그램이 계속 작동합니다.
인터페이스 프로그래밍은 굉장하며 느슨한 결합을 촉진합니다. @lassevk가 언급했듯이 Inversion of Control은 이것을 잘 사용합니다.
또한 SOLID 주체를 살펴보십시오 . 여기에 비디오 시리즈입니다
하드 코딩 된 (강력하게 결합 된 예제) 인터페이스를 살펴보고 마지막으로 IoC / DI 도구 (NInject)로 진행합니다.
많은 설명이 있지만 더 간단하게 만들 수 있습니다. 예를 들어 List
. 다음과 같이 목록을 구현할 수 있습니다.
- 내부 배열
- 연결 목록
- 다른 구현
인터페이스를 구축함으로써 List
. 목록의 정의 또는 List
실제 의미에 대해서만 코딩 합니다.
모든 유형의 구현을 내부적으로 사용할 수 있습니다 array
. 그러나 어떤 이유로 버그 또는 성능과 같은 구현을 변경하고 싶다고 가정하십시오. 그런 다음 선언 List<String> ls = new ArrayList<String>()
을 List<String> ls = new LinkedList<String>()
.
코드의 다른 곳에서는 다른 것을 변경해야합니다. 다른 모든 것은 List
.
나는이 질문에 늦었지만 여기서 "구현이 아닌 인터페이스로의 프로그램"이라는 줄이 GoF (Gang of Four) Design Patterns 책에서 좋은 논의가 있었다고 언급하고 싶습니다.
그것은 p. 18 :
구현이 아닌 인터페이스에 프로그래밍
변수를 특정 구체적인 클래스의 인스턴스로 선언하지 마십시오. 대신 추상 클래스로 정의 된 인터페이스에만 커밋하십시오. 이것이이 책에서 디자인 패턴의 공통 주제임을 알게 될 것입니다.
그 이상으로 다음과 같이 시작되었습니다.
추상 클래스에 의해 정의 된 인터페이스 측면에서만 객체를 조작하는 데는 두 가지 이점이 있습니다.
- 클라이언트는 개체가 클라이언트가 기대하는 인터페이스를 준수하는 한 자신이 사용하는 특정 개체 유형을 인식하지 못합니다.
- 클라이언트는 이러한 개체를 구현하는 클래스를 인식하지 못합니다. 클라이언트는 인터페이스를 정의하는 추상 클래스에 대해서만 알고 있습니다.
즉, 클래스 (또는 하위 클래스)의 특정 구현에 대해 너무 구체적이기 때문에 quack()
오리를위한 bark()
메서드와 개 를위한 메서드를 갖도록 클래스를 작성하지 마십시오 . 대신 같은 일반적인 충분한 기본 클래스에서 사용할 수있는 이름을 사용하는 방법을 쓰기 giveSound()
또는 move()
그들이 오리, 개, 심지어 자동차에 사용할 수 있습니다, 다음 클래스의 클라이언트가 바로 말할 수 있도록, .giveSound()
보다는 객체에 보낼 올바른 메시지를 발행하기 전에 유형 을 사용 quack()
할지 bark()
또는 결정 할지에 대해 생각 합니다.
이미 선택한 답변 (및 여기에있는 다양한 유익한 게시물) 외에도 Head First Design Patterns 사본을 가져 오는 것이 좋습니다 . 매우 쉽게 읽을 수 있으며 질문에 직접 답하고 이것이 중요한 이유를 설명하며 해당 원칙 (및 기타)을 사용하는 데 사용할 수있는 많은 프로그래밍 패턴을 보여줍니다.
기존 게시물에 추가하기 위해 때로는 개발자가 별도의 구성 요소에서 동시에 작업 할 때 인터페이스 코딩이 대규모 프로젝트에 도움이됩니다. 필요한 것은 인터페이스를 미리 정의하고 다른 개발자가 구현중인 인터페이스에 코드를 작성하는 동안 여기에 코드를 작성하는 것입니다.
단위 테스트에도 좋습니다. 인터페이스의 요구 사항을 충족하는 자체 클래스를 종속 된 클래스에 삽입 할 수 있습니다.
따라서이 기능을 제대로 수행하기 위해 인터페이스의 장점은 특정 클래스에서 메서드 호출을 분리 할 수 있다는 것입니다. 대신 인터페이스의 인스턴스를 생성하여 해당 인터페이스를 구현하는 클래스에서 구현이 제공됩니다. 따라서 유사하지만 약간 다른 기능을 가진 많은 클래스를 가질 수 있으며 어떤 경우 (인터페이스의 의도와 관련된 경우) 어떤 객체인지 상관하지 않습니다.
예를 들어, 이동 인터페이스를 가질 수 있습니다. 무언가를 '이동'하게 만드는 메소드와 이동 인터페이스를 구현하는 모든 객체 (Person, Car, Cat)를 전달하여 이동하도록 지시 할 수 있습니다. 메서드가 없으면 모든 클래스 유형을 알 수 있습니다.
플러그인으로 확장 할 수있는 'Zebra'라는 제품이 있다고 가정 해보십시오. 일부 디렉토리에서 DLL을 검색하여 플러그인을 찾습니다. 모든 DLL을로드하고 리플렉션을 사용하여를 구현하는 클래스를 찾은 IZebraPlugin
다음 해당 인터페이스의 메서드를 호출하여 플러그인과 통신합니다.
이것은 특정 플러그인 클래스와 완전히 독립적으로 만듭니다. 클래스가 무엇인지 상관하지 않습니다. 인터페이스 사양을 충족하는 데만 관심이 있습니다.
인터페이스는 이와 같은 확장 성 지점을 정의하는 방법입니다. 인터페이스와 통신하는 코드는 더 느슨하게 결합되어 있습니다. 사실 다른 특정 코드와는 전혀 결합되지 않습니다. 원래 개발자를 만난 적이없는 사람들이 몇 년 후에 작성한 플러그인과 상호 운용 할 수 있습니다.
대신 가상 함수와 함께 기본 클래스를 사용할 수 있습니다. 모든 플러그인은 기본 클래스에서 파생됩니다. 그러나 이것은 클래스가 하나의 기본 클래스 만 가질 수있는 반면에 여러 인터페이스를 구현할 수 있기 때문에 훨씬 더 제한적입니다.
C ++ 설명.
인터페이스를 클래스 공용 메소드로 생각하십시오.
그런 다음 자체 함수를 수행하기 위해 이러한 공용 메서드에 '종속'되는 템플릿을 만들 수 있습니다 (클래스 공용 인터페이스에 정의 된 함수 호출을 수행함). 이 템플릿이 Vector 클래스와 같은 컨테이너이고 의존하는 인터페이스가 검색 알고리즘이라고 가정 해 보겠습니다.
벡터가 호출하는 함수 / 인터페이스를 정의하는 모든 알고리즘 클래스는 '계약'을 충족합니다 (원래 답변에서 설명 된대로). 알고리즘은 동일한 기본 클래스 일 필요도 없습니다. 유일한 요구 사항은 Vector가 의존하는 함수 / 방법 (인터페이스)이 알고리즘에 정의되어 있다는 것입니다.
이 모든 것의 요점은 Vector가 의존하는 인터페이스 (버블 검색, 순차 검색, 빠른 검색)를 제공하는 한 다른 검색 알고리즘 / 클래스를 제공 할 수 있다는 것입니다.
검색 알고리즘이 의존하는 인터페이스 / 계약을 이행하도록하여 Vector와 동일한 검색 알고리즘을 활용하는 다른 컨테이너 (목록, 대기열)를 설계 할 수도 있습니다.
이렇게하면 너무 커진 상속 트리로 문제를 지나치게 복잡하게 만들지 않고 생성하는 모든 새 객체에 대해 반복해서 특정 알고리즘을 작성하는 대신 한 번 알고리즘을 작성할 수 있으므로 시간이 절약됩니다 (OOP 원칙 '코드 재사용').
사물이 작동하는 방식에 대한 '누락'에 관해서는; 대부분의 표준 TEMPLATE 라이브러리 프레임 워크가 작동하는 방식이므로 (적어도 C ++에서는) 이렇게됩니다.
물론 상속 및 추상 클래스를 사용할 때 인터페이스 프로그래밍 방법이 변경됩니다. 그러나 원칙은 동일하며 공용 함수 / 메서드는 클래스 인터페이스입니다.
이것은 거대한 주제이며 디자인 패턴의 기본 원칙 중 하나입니다.
추상화에 의존하지 않더라도 인터페이스에 프로그래밍하는 것이 유리할 수 있습니다.
인터페이스 프로그래밍 은 객체의 상황에 맞는 적절한 하위 집합을 사용하도록합니다. 다음과 같은 이유로 도움이됩니다.
- 우리가 문맥 상 부적절한 일을하는 것을 방지하고
- 향후 구현을 안전하게 변경할 수 있습니다.
예를 들어 및 인터페이스 Person
를 구현 하는 클래스를 고려하십시오 .Friend
Employee
class Person implements AbstractEmployee, AbstractFriend {
}
그 사람의 생일과 관련하여 우리는 Friend
그 사람을 Employee
.
function party() {
const friend: Friend = new Person("Kathryn");
friend.HaveFun();
}
사람의 작업 맥락에서 우리는 Employee
작업장 경계가 흐려지는 것을 방지하기 위해 인터페이스로 프로그래밍 합니다.
function workplace() {
const employee: Employee = new Person("Kathryn");
employee.DoWork();
}
큰. 우리는 다른 상황에서 적절하게 행동했으며 소프트웨어가 잘 작동합니다.
먼 미래에 우리의 사업이 개와 함께 일하는 것으로 바뀌면 소프트웨어를 상당히 쉽게 바꿀 수 있습니다. 첫째, 우리는 만들 Dog
클래스가 구현 모두 Friend
와 Employee
. 그런 다음, 우리는 안전하게 변경 new Person()
에 new Dog()
. 두 함수에 수천 줄의 코드가 있더라도 다음 사항이 사실임을 알기 때문에 간단한 편집이 작동합니다.
- 함수
party
는의Friend
하위 집합 만 사용합니다Person
. - 함수
workplace
는의Employee
하위 집합 만 사용합니다Person
. - 클래스
Dog
는Friend
및Employee
인터페이스를 모두 구현합니다 .
반면에 party
또는 workplace
에 대해 프로그래밍 된 경우 Person
둘 다 Person
특정 코드를 가질 위험이 있습니다 . 에서 Person
로 변경하면 지원하지 않는 특정 코드 Dog
를 제거하기 위해 코드를 살펴 봐야합니다 .Person
Dog
교훈 : 인터페이스 프로그래밍은 코드가 적절하게 작동하고 변화에 대비하는 데 도움이됩니다. 또한 추상화에 의존하도록 코드를 준비하여 더 많은 이점을 제공합니다.
Java에서 이러한 구체적인 클래스는 모두 CharSequence 인터페이스를 구현합니다.
CharBuffer, 문자열, StringBuffer, StringBuilder
이러한 구체적인 클래스에는 Object 이외의 공통 부모 클래스가 없으므로 각각이 문자 배열과 관련이 있다는 사실 외에는 해당 클래스를 나타내거나 조작하는 것 외에는 관련이 없습니다. 예를 들어, String 객체가 인스턴스화되면 String의 문자는 변경할 수 없지만 StringBuffer 또는 StringBuilder의 문자는 편집 할 수 있습니다.
그러나 이러한 각 클래스는 CharSequence 인터페이스 메서드를 적절하게 구현할 수 있습니다.
char charAt(int index)
int length()
CharSequence subSequence(int start, int end)
String toString()
어떤 경우에는 String을 받아들이는 데 사용되었던 Java 클래스 라이브러리 클래스가 이제 CharSequence 인터페이스를 받아들이도록 수정되었습니다. 따라서 StringBuilder의 인스턴스가있는 경우 String 개체를 추출하는 대신 (새 개체 인스턴스를 인스턴스화하는 것을 의미) 대신 CharSequence 인터페이스를 구현할 때 StringBuilder 자체를 전달할 수 있습니다.
일부 클래스가 구현하는 Appendable 인터페이스는 기본 구체적 클래스 개체 인스턴스의 인스턴스에 문자를 추가 할 수있는 모든 상황에서 거의 동일한 이점을 제공합니다. 이러한 모든 구체적인 클래스는 Appendable 인터페이스를 구현합니다.
BufferedWriter, CharArrayWriter, CharBuffer, FileWriter, FilterWriter, LogStream, OutputStreamWriter, PipedWriter, PrintStream, PrintWriter, StringBuffer, StringBuilder, StringWriter, Writer
간단히 말해서 ...새로운 클래스 Swimmer를 작성하여 기능 swim ()을 추가하고 Dog 클래스의 객체를 사용해야하고이 Dog 클래스는 swim ()을 선언하는 Animal 인터페이스를 구현합니다. 내가 말하는 것에 대한 다이어그램]. 계층 구조 (동물)의 맨 위는 매우 추상적이고 맨 아래 (개)는 매우 구체적입니다. "인터페이스 프로그래밍"에 대해 생각하는 방식은 Swimmer 클래스를 작성할 때이 경우에는 Animal 객체 인 계층 구조보다 훨씬 상위에있는 인터페이스에 대해 코드를 작성하고자한다는 것입니다. 인터페이스에는 구현 세부 정보가 없으므로 코드가 느슨하게 결합됩니다. 구현 세부 정보는 시간이 지남에 따라 변경 될 수 있지만 상호 작용하는 것은 구현이 아닌 인터페이스에 대한 것이므로 나머지 코드에는 영향을주지 않습니다.
짧은 이야기 : 우체부는 집으로 집에 가서 배달 할 주소가 적힌 표지 (편지, 서류, 수표, 상품권, 신청서, 러브 레터)를받습니다.
표지가 없다고 가정하고 우체부에게 집으로 돌아가서 모든 것을 받고 다른 사람에게 배달해달라고 부탁하고 우체부가 혼란 스러울 수 있다고 가정합니다.
그래서 커버 (우리 이야기에서는 인터페이스)로 그것을 감싸는 것이 좋습니다. 그러면 그는 그의 일을 잘 할 것입니다.
이제 우체부 업무는 표지 만 받고 배달하는 것입니다. (그는 표지 안에있는 것을 신경 쓰지 않습니다).
interface
실제 유형이 아닌 유형을 작성 하고 실제 유형으로 구현하십시오.
인터페이스 생성은 구성 요소가 나머지 코드에 쉽게 적용됨을 의미합니다.
예를 들어 보겠습니다.
아래와 같이 AirPlane 인터페이스가 있습니다.
interface Airplane{
parkPlane();
servicePlane();
}
Planes의 Controller 클래스에 다음과 같은 메서드가 있다고 가정합니다.
parkPlane(Airplane plane)
과
servicePlane(Airplane plane)
프로그램에서 구현됩니다. 당신의 코드 는 깨지지 않습니다 . 내 말은, 인수를 허용하는 한 변경할 필요가 없습니다 AirPlane
.
그것은 실제의 형태에도 불구하고 어떤 비행기를 받아 들일 것입니다 때문에 flyer
, highflyr
, fighter
, 등
또한 컬렉션에서 :
List<Airplane> plane;
// 모든 비행기를 탈 것입니다.
다음 예는 이해를 명확하게합니다.
이를 구현하는 전투기가 있으므로
public class Fighter implements Airplane {
public void parkPlane(){
// Specific implementations for fighter plane to park
}
public void servicePlane(){
// Specific implementatoins for fighter plane to service.
}
}
HighFlyer 및 기타 clasess에 대해서도 동일합니다.
public class HighFlyer implements Airplane {
public void parkPlane(){
// Specific implementations for HighFlyer plane to park
}
public void servicePlane(){
// specific implementatoins for HighFlyer plane to service.
}
}
이제 AirPlane
여러 번 사용하는 컨트롤러 클래스를 생각해보십시오 .
Controller 클래스가 아래와 같이 ControlPlane이라고 가정합니다.
public Class ControlPlane{
AirPlane plane;
// so much method with AirPlane reference are used here...
}
여기 마술이 온다
새 AirPlane
유형 인스턴스를 원하는만큼 만들 수 있으며 변경하지 않습니다.
ControlPlane
수업 코드 .
인스턴스를 추가 할 수 있습니다 ..
JumboJetPlane // implementing AirPlane interface.
AirBus // implementing AirPlane interface.
이전에 생성 된 유형의 인스턴스도 제거 할 수 있습니다.
인터페이스는 계약과 유사하며 구현 클래스가 계약 (인터페이스)에 작성된 메서드를 구현하도록합니다. Java는 다중 상속을 제공하지 않기 때문에 "인터페이스 프로그래밍"은 다중 상속을 달성하는 좋은 방법입니다.
이미 다른 클래스 B를 확장하고있는 클래스 A가 있지만 해당 클래스 A도 특정 지침을 따르거나 특정 계약을 구현하기를 원하는 경우 "인터페이스 프로그래밍"전략을 사용하면됩니다.
Q :-... "인터페이스를 구현하는 클래스를 사용할 수 있습니까?"
A :-네.Q : -... "언제 그렇게해야합니까?"
A :-인터페이스를 구현하는 클래스가 필요할 때마다.
참고 : 우리는 클래스에 의해 구현이 아닌 인터페이스를 인스턴스화 할 수있는 - 사실을.
- 왜?
- 인터페이스에는 정의가 아닌 메서드 프로토 타입 만 있기 때문입니다 (로직이 아닌 함수 이름 만 있음).
AnIntf anInst = new Aclass ();
// Aclass가 AnIntf를 구현하는 경우에만 이를 수행 할 수 있습니다.
// anInst에는 Aclass 참조가 있습니다.
참고 :
이제 Bclass와 Cclass가 동일한 Dintf를 구현하면 어떤 일이 발생하는지 이해할 수 있습니다.
Dintf bInst = new Bclass();
// now we could call all Dintf functions implemented (defined) in Bclass.
Dintf cInst = new Cclass();
// now we could call all Dintf functions implemented (defined) in Cclass.
우리가 가진 것 :
동일한 인터페이스 프로토 타입 (인터페이스의 함수 이름), 다른 구현을 호출합니다.
참고 문헌 :
프로토 타입-Wikipedia
인터페이스에 대한 프로그램을 통해 인터페이스에서 정의한 계약 구현을 원활하게 변경할 수 있습니다. 계약과 특정 구현 간의 느슨한 결합을 허용합니다.
IInterface classRef = new ObjectWhatever()
IInterface를 구현하는 클래스를 사용할 수 있습니까? 언제해야합니까?
이 SE 질문을 좋은 예를 들어보십시오.
Java 클래스 용 인터페이스를 선호해야하는 이유는 무엇입니까?
인터페이스 히트 성능을 사용합니까?
그렇다면 얼마나?
예. 1 초 미만의 성능 오버 헤드가 약간 발생합니다. 그러나 응용 프로그램에 인터페이스 구현을 동적으로 변경해야하는 경우 성능 영향에 대해 걱정하지 마십시오.
2 비트 코드를 유지하지 않고도 어떻게 피할 수 있습니까?
응용 프로그램에 필요한 경우 인터페이스의 여러 구현을 피하려고하지 마십시오. 하나의 특정 구현과 인터페이스의 긴밀한 결합이없는 경우 한 구현을 다른 구현으로 변경하기 위해 패치를 배포해야 할 수 있습니다.
좋은 사용 사례 : 전략 패턴 구현 :
또한 여기에 훌륭하고 설명적인 답변이 많이 있으므로이 방법을 사용할 때 알아 낸 추가 정보를 포함하여 여기에 내 관점을 제공하고 싶습니다.
단위 테스트
지난 2 년 동안 저는 취미 프로젝트를 작성했고 이에 대한 단위 테스트를 작성하지 않았습니다. 약 50K 줄을 작성한 후 단위 테스트를 작성하는 것이 정말 필요하다는 것을 알았습니다. 나는 인터페이스를 사용하지 않았고 (또는 아주 드물게) ... 첫 단위 테스트를했을 때 그것이 복잡하다는 것을 알게되었습니다. 왜?
클래스 변수 및 / 또는 매개 변수로 입력에 사용되는 많은 클래스 인스턴스를 만들어야했기 때문입니다. 따라서 테스트는 통합 테스트와 비슷해 보입니다 (모든 클래스가 함께 연결되었으므로 클래스의 완전한 '프레임 워크'를 만들어야 함).
인터페이스에 대한 두려움 그래서 저는 인터페이스를 사용하기로 결정했습니다. 내 두려움은 (사용되는 모든 클래스에서) 모든 기능을 여러 번 구현해야한다는 것이 었습니다. 어떤면에서 이것은 사실이지만 상속을 사용하면 많이 줄일 수 있습니다.
인터페이스와 상속 의 조합은 사용하기에 매우 좋다는 것을 알았습니다. 아주 간단한 예를 들어 보겠습니다.
public interface IPricable
{
int Price { get; }
}
public interface ICar : IPricable
public abstract class Article
{
public int Price { get { return ... } }
}
public class Car : Article, ICar
{
// Price does not need to be defined here
}
이러한 방식으로 코드를 복사 할 필요는 없지만 자동차를 인터페이스 (ICar)로 사용하는 이점이 있습니다.
먼저 몇 가지 정의부터 시작하겠습니다.
인터페이스 n. 개체의 작업에 의해 정의 된 모든 서명 집합을 개체에 대한 인터페이스라고합니다.
n을 입력 합니다. 특정 인터페이스
의 간단한 예 인터페이스는 상기 정의 된 바와 같은 모든 PDO 객체 메소드 것 query()
, commit()
, close()
전체가 아닌 별도 등. 이러한 메서드, 즉 인터페이스는 개체에 보낼 수있는 요청 인 전체 메시지 집합을 정의합니다.
유형 상기 정의 된 바와 같은 특정 인터페이스이다. 내가 보여주기 위해 만들어 낸 모양의 인터페이스를 사용합니다 : draw()
, getArea()
, getPerimeter()
등
객체가 데이터베이스 유형의 경우 우리가 메시지 / 데이터베이스 인터페이스의 요청을 수락한다는 것을 의미, query()
, commit()
등 오브젝트는 여러 유형이 될 수 있습니다. 인터페이스를 구현하는 한 데이터베이스 객체는 모양 유형이 될 수 있으며,이 경우 하위 유형이됩니다 .
많은 객체는 다양한 인터페이스 / 유형을 가질 수 있으며 해당 인터페이스를 다르게 구현할 수 있습니다. 이를 통해 객체를 대체하여 사용할 객체를 선택할 수 있습니다. 다형성이라고도합니다.
클라이언트는 구현이 아닌 인터페이스 만 인식합니다.
그래서 인터페이스에 에센스 프로그래밍과 같은 추상 클래스의 몇 가지 유형을 포함 할 Shape
경우에만 즉, 지정된 인터페이스에 draw()
, getCoordinates()
, getArea()
등 그리고 다른 구체적인 클래스는 이러한 Circle 클래스, 광장 클래스, 삼각형 클래스로 그 인터페이스를 구현해야합니다. 따라서 구현이 아닌 인터페이스에 대한 프로그램.
나는 interface
s가 언어에서 가장 중요한 것이라고 생각하지 않는다 : 상속하는 클래스가 더 일반적으로 사용된다. 그러나 어쨌든 그들은 중요합니다!
예를 들어 (이것은 Java
코드이지만 C#
다른 많은 언어에 간단히 적용 할 수 있습니다) :
interface Convertable<T> {
T convert();
}
public class NumerableText implements Convertable<Integer> {
private String text = "";
public NumerableText() { }
public NumerableText(String text) {
this.text = text;
}
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
public Integer convert() {
return this.text.hashCode();
}
}
public class NumerableTextArray implements Convertable<Integer> {
private String[] textArray = "";
public NumerableTextArray() { }
public NumerableTextArray(String[] textArray) {
this.textArray = textArray;
}
public String[] getTextArray() {
return this.textArray;
}
public void setTextArray(String[] text) {
this.textArray = textArray;
}
public Integer convert() {
Integer value = 0;
for (String text : textArray)
value += text.hashCode();
return value;
}
}
public class Foo {
public static void main() {
Convertable<Integer> num1 = new NumerableText("hello");
Convertable<Integer> num2 = new NumerableTextArray(new String[] { "test n°1", "test n°2" });
System.out.println(String.valueOf(num1.convert()));
System.out.println(String.valueOf(num2.convert()));
//Here are you two numbers generated from two classes of different type, but both with the method convert(), which allows you to get that number.
}
}
"인터페이스 프로그램"은 하드 코드를 제대로 제공하지 않음을 의미합니다. 즉, 이전 기능을 손상시키지 않고 코드를 확장해야합니다. 이전 코드를 편집하는 것이 아니라 확장 기능입니다.
참고 URL : https://stackoverflow.com/questions/383947/what-does-it-mean-to-program-to-an-interface
'program story' 카테고리의 다른 글
매개 변수를 setTimeout () 콜백에 어떻게 전달할 수 있습니까? (0) | 2020.09.29 |
---|---|
함수에 return 문이 하나만 있어야합니까? (0) | 2020.09.29 |
이러한 구조가 사전 및 사후 증가 정의되지 않은 동작을 사용하는 이유는 무엇입니까? (0) | 2020.09.29 |
다중보기 유형으로 RecyclerView를 만드는 방법은 무엇입니까? (0) | 2020.09.29 |
pip install mysql-python이 EnvironmentError와 함께 실패 : mysql_config not found (0) | 2020.09.29 |