인터페이스 — 요점이 무엇입니까?


269

인터페이스의 이유는 저를 정말로 피합니다. 내가 이해 한 바에 따르면 C #에는 존재하지 않는 존재하지 않는 다중 상속에 대한 일종의 해결 방법입니다 (또는 그렇게 들었습니다).

내가 보는 것은 멤버와 함수를 미리 정의한 다음 클래스에서 다시 정의해야한다는 것입니다. 따라서 인터페이스가 중복됩니다. 그것은 단지 구문론적인 것 같은 느낌이 든다. .. 음, 나에게 쓰레기 (제발 악의를 가지지 말아라. 쓸모없는 것들처럼 정크).

스택 오버플로의 다른 C # 인터페이스 스레드에서 가져온 아래 예제에서 인터페이스 대신 Pizza라는 기본 클래스를 만들었습니다.

쉬운 예 (다른 스택 오버플로 기여에서 가져온)

public interface IPizza
{
    public void Order();
}

public class PepperoniPizza : IPizza
{
    public void Order()
    {
        //Order Pepperoni pizza
    }
}

public class HawaiiPizza : IPizza
{
    public void Order()
    {
        //Order HawaiiPizza
    }
}

여기 에이 질문과 중복되는 느낌이 들지만, 모두 인터페이스의 계약 부분을 설명하는 것 같아서 확실하지 않습니다.
Lasse V. Karlsen

7
멋지고 깔끔한 사용자가 되려고 노력하면서, 나는 뭔가를 게시하기 전에 다양한 포럼에서 먼저 내 대답을 찾는 경향이 있습니다. 불행히도, 그들 대부분은 이후 단계에서 시작되었고 나머지는 도움이되지 않았습니다. 나는 이미 "왜 그렇게합니까?" 불필요하게 지나치게 복잡해 보였습니다. Btw. 매우 빠른 답변을 주신 모든 분들께 감사드립니다. 나는 먼저 그것들을 모두 소화해야하지만, 나는 지금 그들의 요점에 대해 합리적으로 좋은 생각을 가지고 있다고 생각합니다. 항상 다른 각도에서 본 것 같습니다. 도와 주셔서 정말로 고맙습니다.
Nebelhom


1
또한 인터페이스는 struct유형 과 같은 상속을 설정하는 데 도움이됩니다 .
ja72

3
흠, OP는 "내가 이해 한 바에 따르면 C #에는 존재하지 않는 존재하지 않는 다중 상속에 대한 인터페이스가 일종의 해결 방법입니다. 인터페이스 대신 기본 클래스를 사용하십시오. " 그리고 대부분의 답변은 (추상적 인) 기본 클래스로 구현할 수있는 예제를 제공하거나 다중 상속 시나리오에 인터페이스가 어떻게 필요한지 보여주는 예제를 제공했습니다. 그 답변은 모두 훌륭하지만 OP가 이미 알고있는 것을 반복하지는 않습니까? OP가 예없이 대답을 선택하게 된 것은 당연합니다. LOL
RayLuo

답변:


176

요점은 인터페이스가 계약을 나타냅니다 . 모든 구현 클래스에는 일련의 공개 메소드가 있어야합니다. 기술적으로 인터페이스는 구문, 즉 어떤 메소드가 있는지, 어떤 인수가 있고 어떤 것이 리턴되는지를 제어합니다. 일반적으로 문서에 의해서만 의미 체계를 캡슐화합니다.

그런 다음 인터페이스를 다르게 구현하고 마음대로 바꿀 수 있습니다. 귀하의 예에서, 모든 피자 인스턴스는 알 수없는 피자 유형의 인스턴스를 처리하는 모든 곳에서 IPizza사용할 수 있습니다 IPizza. 유형이 상속되는 모든 인스턴스 IPizza에는 Order()메소드 가 있으므로 순서를 지정할 수 있습니다 .

파이썬은 정적으로 타입이 아니므로 타입은 런타임에 유지되고 조회됩니다. 따라서 Order()모든 객체 에서 메소드를 호출 할 수 있습니다 . 객체가 그러한 메소드를 가지고 있고 아마도“Meh.«라고 말하지 않는다면 런타임은 행복합니다. C #에서는 그렇지 않습니다. 컴파일러는 올바른 호출을 담당하며 임의 object의 랜덤을 갖는 경우 런타임 중에 인스턴스에 해당 메소드가 있는지 여부를 아직 알지 못합니다. 컴파일러의 관점에서 확인할 수 없으므로 유효하지 않습니다. (반사 또는 dynamic키워드로 그러한 일을 할 수는 있지만 지금은 조금 먼 것 같습니다.)

또한 일반적인 의미의 인터페이스가 반드시 C # interface일 필요는 없으며 추상 클래스 또는 일반 클래스 일 수 있습니다 (대부분의 경우 모든 하위 클래스가 공통 코드를 공유 해야하는 경우 유용 할 수 있음) 그러나 interface충분합니다).


1
+1, 비록 (계약의 의미에서) 인터페이스가 추상 클래스 나 일반 클래스 일 수는 없다.
Groo

3
몇 분 안에 인터페이스를 이해할 수 없다고 덧붙입니다. 수년간 객체 지향 프로그래밍 경험이 없다면 인터페이스를 이해하는 것이 합리적이지 않다고 생각합니다. 책에 대한 링크를 추가 할 수 있습니다. 나는 제안 할 것이다 : .NET의 Dependency Injection 은 부드러운 소개가 아니라 실제로 구멍이 얼마나 깊이 있는지입니다.
knut

2
아, DI에 대한 실마리가 없다는 문제가 있습니다. 그러나 asker의 주요 문제는 파이썬에서 모든 것이없이 작동 할 때 왜 필요한지였습니다. 그것이 내 대답의 가장 큰 단락이 제공하려고 시도한 것입니다. 인터페이스를 사용하는 모든 패턴과 실습을 파헤칠 필요는 없다고 생각합니다.
Joey

1
그렇다면 질문은 "동적 인 유형의 언어를 프로그래밍하기가 더 쉬울 때 왜 정적 유형의 언어를 사용 하는가?"가됩니다. 그 질문에 답하는 전문가는 아니지만 성능이 결정적인 문제라고 말할 수 있습니다. 파이썬 객체를 호출 할 때 프로세스는 런타임 동안 해당 객체에 "주문"이라는 메소드가 있는지 여부를 결정해야하지만, C # 객체를 호출하면 해당 메소드를 구현하는 것으로 이미 설정되어 있으며 그러한 주소로 전화를 걸 수 있습니다.
Boluc Papuccuoglu

3
@BolucPapuccuoglu : 그 외에도 foo구현을 알고있는 경우 정적으로 유형이 지정된 객체 IWidget를 사용하면 프로그래머가 foo.woozle()문서를 보고 해당 메소드가 수행해야하는 작업을IWidget 알 수 있는 호출을 보는 프로그래머 있습니다. 프로그래머는 실제 구현을위한 코드가 어디에서 나오는지 알 수 없지만 IWidget인터페이스 계약 을 따르는 모든 유형 foo은 해당 계약과 일치하는 방식으로 구현 됩니다. 반면에 역동적 인 언어에서는 foo.woozle()의미 가 무엇인지에 대한 명확한 참조 점이 없습니다 .
supercat

440

아무도 인터페이스가 어떻게 유용한 지에 대한 명확한 용어를 실제로 설명하지 않았으므로, 나는 그것을 쏴볼 것입니다 (그리고 Shamim의 답변에서 약간의 아이디어를 훔칠 것입니다).

피자 주문 서비스에 대한 아이디어를 얻을 수 있습니다. 여러 종류의 피자를 가질 수 있으며 각 피자에 대한 일반적인 조치는 시스템에서 주문을 준비하는 것입니다. 각 피자 는 준비해야 하지만 각 피자 는 다르게 준비됩니다 . 예를 들어, 크러스트 피자를 주문할 때 시스템은 특정 재료가 식당에서 구할 수 있는지 확인하고 딥 디쉬 피자에 필요하지 않은 재료는 따로 보관해야합니다.

코드로 작성하면 기술적으로 할 수 있습니다.

public class Pizza()
{
    public void Prepare(PizzaType tp)
    {
        switch (tp)
        {
            case PizzaType.StuffedCrust:
                // prepare stuffed crust ingredients in system
                break;

            case PizzaType.DeepDish:
                // prepare deep dish ingredients in system
                break;

            //.... etc.
        }
    }
}

그러나 딥 디쉬 피자 (C # 용어로)는 Prepare()박제 빵 껍질과 방법 에 다른 속성을 설정해야 할 수 있으므로 많은 선택적 속성이 생겨 클래스가 확장되지 않습니다 (새로 추가하면 어떻게됩니까? 피자 종류).

이를 해결하는 올바른 방법은 인터페이스를 사용하는 것입니다. 인터페이스는 모든 피자를 준비 할 수 있지만 각 피자를 다르게 준비 할 수 있다고 선언합니다. 따라서 다음 인터페이스가있는 경우

public interface IPizza
{
    void Prepare();
}

public class StuffedCrustPizza : IPizza
{
    public void Prepare()
    {
        // Set settings in system for stuffed crust preparations
    }
}

public class DeepDishPizza : IPizza
{
    public void Prepare()
    {
        // Set settings in system for deep dish preparations
    }
}

이제 주문 처리 코드는 재료를 처리하기 위해 어떤 종류의 피자를 주문했는지 정확히 알 필요가 없습니다. 그것은 단지있다 :

public PreparePizzas(IList<IPizza> pizzas)
{
    foreach (IPizza pizza in pizzas)
        pizza.Prepare();
}

각 피자 종류가 다르게 준비되어 있지만 코드의이 부분은 우리가 다루는 피자 종류를 신경 쓸 필요가 없으며 피자를 요구한다는 것을 알고 있으므로 각 피자 Prepare는 자동으로 각 피자를 올바르게 준비합니다 컬렉션에 여러 유형의 피자가 있더라도 유형에 따라 다릅니다.


11
+1, 인터페이스 사용을 보여주기 위해 List를 사용하는 예제가 마음에 들었습니다.
danielunderwood

21
좋은 대답이지만, 아마도이 경우에 인터페이스가 추상 클래스를 사용하는 것보다 더 나은 이유를 명확히하기 위해 수정해야 할 수도 있습니다 (예를 들어, 간단한 클래스의 경우 추상 클래스가 실제로 더 나을 수 있습니까?)
Jez

3
이것이 가장 좋은 대답입니다. 약간의 오타가 있거나 코드를 복사하여 붙여 넣을 수 있습니다. C # 인터페이스에서는와 같은 액세스 수정자를 선언하지 않습니다 public. 인터페이스 내부는 그냥void Prepare();
Dush

26
이것이 어떻게 질문에 대답하는지 모르겠습니다. 이 예제는 기본 클래스와 추상 메서드를 사용하여 쉽게 수행 할 수 있었으며 원래 질문이 지적한 것과 정확히 같습니다.
Jamie Kitson

4
인터페이스가 많이 나올 때까지 인터페이스가 제대로 작동하지 않습니다. 추상화 여부에 관계없이 단일 클래스에서만 상속 할 수 있지만 원하는대로 단일 클래스에서 여러 가지 다른 인터페이스를 구현할 수 있습니다. 갑자기 문틀에 문이 필요하지 않습니다. IOpenable은 문, 창, 레터 박스 등 무엇이든 할 수 있습니다.
mtnielsen

123

나에게 처음 시작할 때 코드를 더 쉽고 빠르게 작성하기 위해 그것들을 보지 않을 때만 이것에 대한 요점이 분명 해졌다. 이것은 그들의 목적이 아니다. 그들은 여러 가지 용도로 사용됩니다.

(이것의 사용을 시각화하는 것은 쉽지 않기 때문에 이것은 피자 비유를 잃을 것입니다)

화면에서 간단한 게임을 만들고 있다고 말하면 상호 작용하는 생물이 있습니다.

A : 프론트 엔드와 백엔드 구현 사이에 느슨한 결합을 도입하여 향후 코드를보다 쉽게 ​​유지 관리 할 수 ​​있습니다.

트롤 만있을 것이므로 이것을 시작할 수 있습니다.

// This is our back-end implementation of a troll
class Troll
{
    void Walk(int distance)
    {
        //Implementation here
    }
}

프런트 엔드 :

function SpawnCreature()
{
    Troll aTroll = new Troll();

    aTroll.Walk(1);
}

2 주가 지나면 마케팅 부서에서 Orcs가 필요하다고 판단합니다. 그들은 트위터에서 그들에 대해 읽을 때 다음과 같이해야합니다.

class Orc
{
    void Walk(int distance)
    {
        //Implementation (orcs are faster than trolls)
    }
}

프런트 엔드 :

void SpawnCreature(creatureType)
{
    switch(creatureType)
    {
         case Orc:

           Orc anOrc = new Orc();
           anORc.Walk();

          case Troll:

            Troll aTroll = new Troll();
             aTroll.Walk();
    }
}

그리고 이것이 어떻게 지저분 해지는 지 알 수 있습니다. 여기에서 인터페이스를 사용하여 프론트 엔드를 한 번 작성하고 (중요한 비트는 테스트) 테스트 한 다음 필요에 따라 추가 백엔드 항목을 꽂을 수 있습니다.

interface ICreature
{
    void Walk(int distance)
}

public class Troll : ICreature
public class Orc : ICreature 

//etc

프런트 엔드는 다음과 같습니다.

void SpawnCreature(creatureType)
{
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    creature.Walk();
}

프론트 엔드는 이제 ICreature 인터페이스에만 관심을 갖습니다. 트롤이나 오크의 내부 구현에 신경 쓰지 않고 ICreature를 구현한다는 사실에만 신경 쓰지 않습니다.

이 관점에서 이것을 볼 때 주목해야 할 중요한 점은 추상 생물체 클래스를 쉽게 사용할 수 있다는 것입니다.이 관점에서 이것은 동일한 효과가 있습니다.

그리고 창조물을 공장으로 추출 할 수 있습니다.

public class CreatureFactory {

 public ICreature GetCreature(creatureType)
 {
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    return creature;
  }
}

그리고 우리의 프론트 엔드는 다음과 같이 될 것입니다.

CreatureFactory _factory;

void SpawnCreature(creatureType)
{
    ICreature creature = _factory.GetCreature(creatureType);

    creature.Walk();
}

프론트 엔드는 이제 트롤과 오크가 구현 된 라이브러리 (공장이 별도의 라이브러리에있는 경우)에 대한 참조를 가질 필요가 없습니다.

B : 당신은 다른 생물체가 당신의 균질 한 데이터 구조에서 가질 수있는 기능을 가지고 있다고 하자.

interface ICanTurnToStone
{
   void TurnToStone();
}

public class Troll: ICreature, ICanTurnToStone

프런트 엔드는 다음과 같습니다.

void SpawnCreatureInSunlight(creatureType)
{
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    creature.Walk();

    if (creature is ICanTurnToStone)
    {
       (ICanTurnToStone)creature.TurnToStone();
    }
}

C : 의존성 주입 사용법

프론트 엔드 코드와 백엔드 구현 사이에 매우 느슨한 결합이있을 때 대부분의 의존성 주입 프레임 워크는 작업하기가 더 쉽습니다. 위의 팩토리 예제를보고 팩토리가 인터페이스를 구현하도록하는 경우 :

public interface ICreatureFactory {
     ICreature GetCreature(string creatureType);
}

우리의 프론트 엔드는 생성자를 통해 (일반적으로) MVC API 컨트롤러와 같이 이것을 주입 할 수 있습니다.

public class CreatureController : Controller {

   private readonly ICreatureFactory _factory;

   public CreatureController(ICreatureFactory factory) {
     _factory = factory;
   }

   public HttpResponseMessage TurnToStone(string creatureType) {

       ICreature creature = _factory.GetCreature(creatureType);

       creature.TurnToStone();

       return Request.CreateResponse(HttpStatusCode.OK);
   }
}

DI 프레임 워크 (예 : Ninject 또는 Autofac)를 사용하면 런타임에 생성자에서 ICreatureFactory가 필요할 때마다 CreatureFactory의 인스턴스가 작성되도록 코드를 설정할 수 있습니다. 이는 코드를 훌륭하고 단순하게 만듭니다.

또한 컨트롤러에 대한 단위 테스트를 작성할 때 조롱 된 ICreatureFactory를 제공 할 수 있습니다 (예를 들어 구체적인 구현에 DB 액세스가 필요하면 단위 테스트를 원하지 않는 경우) 컨트롤러에서 코드를 쉽게 테스트 할 수 있습니다 .

D : 다른 용도가 있습니다. 예를 들어 '레거시'이유로 잘 구성되지 않은 두 개의 프로젝트 A와 B가 있고 A는 B에 대한 참조가 있습니다.

그런 다음 B에서 이미 A에서 메소드를 호출해야하는 기능을 찾으십시오. 순환 참조를 얻을 때 구체적인 구현을 사용하여이를 수행 할 수 없습니다.

A의 클래스가 구현하는 인터페이스를 B로 선언 할 수 있습니다. 구체적인 객체가 A 유형 인 경우에도 B의 메소드에는 문제없이 인터페이스를 구현하는 클래스의 인스턴스가 전달 될 수 있습니다.


6
그것은 당신이 무엇인지 말하려고 한 답변을 찾을 때 성가신 것이 아니라, 훨씬 더 잘 수행 - stackoverflow.com/a/93998/117215
패디

18
좋은 소식은 이제 죽은 페이지이며 당신은 아닙니다 :). 좋은 예입니다!
Sealer_05

1
당신의 C 토론은 저를 조금 잃었습니다. 그러나 A 및 B 토론은 인터페이스가 여러 클래스에서 공통적 인 종류의 기능을 제공하는 데 어떻게 사용될 수 있는지를 설명하기 때문에 A와 B 토론을 좋아합니다. 여전히 내게 퍼지되는 인터페이스 영역은 느슨한 커플 링에 인터페이스를 사용하는 방법입니다. 아마도 그것은 당신의 C 토론이 다루고 있었던 것입니까? 그렇다면 더 자세한 예가 필요하다고 생각합니다. :)
user2075599

1
귀하의 예에서 ICreature는 트롤과 오크의 추상 기본 클래스 (Creature?)가 더 적합 할 것 같습니다. 그런 다음 생물 사이의 공통 논리를 구현할 수 있습니다. 그것은 당신이 전혀 ICreature 인터페이스가 필요하지 않다는 것을 의미합니다 ...
Skarsnik

1
@Skarsnik-이 노트에 관한 내용은 "이 관점에서 볼 때 주목해야 할 중요한 점은 추상적 생물체 클래스를 쉽게 사용할 수 있다는 것입니다.이 관점에서 이것은 동일합니다. 효과."
패디

33

설명 된 예는 다음과 같습니다.

public interface IFood // not Pizza
{
    public void Prepare();

}

public class Pizza : IFood
{
    public void Prepare() // Not order for explanations sake
    {
        //Prepare Pizza
    }
}

public class Burger : IFood
{
    public void Prepare()
    {
        //Prepare Burger
    }
}

27

위의 예는 의미가 없습니다. 클래스를 사용하여 위의 모든 예제를 수행 할 수 있습니다 ( 계약으로 만 작동하려는 경우 추상 클래스 ).

public abstract class Food {
    public abstract void Prepare();
}

public class Pizza : Food  {
    public override void Prepare() { /* Prepare pizza */ }
}

public class Burger : Food  {
    public override void Prepare() { /* Prepare Burger */ }
}

인터페이스와 동일한 동작을 얻습니다. List<Food>어떤 클래스가 맨 위에 있는지 알지 않고 반복 하여 만들 수 있습니다 .

더 적절한 예는 다중 상속입니다.

public abstract class MenuItem {
    public string Name { get; set; }
    public abstract void BringToTable();
}

// Notice Soda only inherits from MenuItem
public class Soda : MenuItem {
    public override void BringToTable() { /* Bring soda to table */ }
}


// All food needs to be cooked (real food) so we add this
// feature to all food menu items
public interface IFood {
    void Cook();
}

public class Pizza : MenuItem, IFood {
    public override void BringToTable() { /* Bring pizza to table */ }
    public void Cook() { /* Cook Pizza */ }
}

public class Burger : MenuItem, IFood {
    public override void BringToTable() { /* Bring burger to table */ }
    public void Cook() { /* Cook Burger */ }
}

그런 다음 모든 것을 사용할 수 있으며 MenuItem각 메소드 호출을 처리하는 방법에 신경 쓰지 않습니다.

public class Waiter {
    public void TakeOrder(IEnumerable<MenuItem> order) 
    {
        // Cook first
        // (all except soda because soda is not IFood)
        foreach (var food in order.OfType<IFood>())
            food.Cook();

        // Bring them all to the table
        // (everything, including soda, pizza and burger because they're all menu items)
        foreach (var menuItem in order)
            menuItem.BringToTable();
    }
}

2
실제로 "인터페이스 대신 추상 클래스를 사용하는 것이 어떻습니까?"라는 질문에 대한 답을 찾기 위해 여기까지 스크롤을 내려야했습니다. 실제로 C #의 다중 상속 부족 만 처리하는 것 같습니다.
SyntaxRules

2
예, 이것이 저에게 가장 좋은 설명입니다. 인터페이스가 추상 클래스보다 더 유용한 이유를 명확하게 설명하는 유일한 방법입니다. (예 : 피자는 MenuItem이며 음식이기도합니다. 추상 클래스에서는 둘 중 하나만 가능하지만 둘다는 안됩니다)
Maxter

25

유추에 대한 간단한 설명

해결해야 할 문제 : 다형성의 목적은 무엇입니까?

유추 : 그래서 저는 건설 현장의 감독입니다.

상인은 항상 건설 현장을 걷습니다. 누가 그 문을 통과할지 모르겠습니다. 그러나 나는 기본적으로 그들에게 무엇을해야하는지 말한다.

  1. 목수라면 나무 발판을 만드십시오.
  2. 배관공이라면 "파이프를 설치하십시오"라고 말합니다
  3. 전기 기술자라면 "케이블을 뽑아 광섬유 케이블로 교체하십시오"라고 말합니다.

위의 접근 방식의 문제점은 내가해야한다는 것입니다. 그것은 특정 거래에 대한 모든 것을 알아야한다는 것을 의미합니다. 이 접근 방식과 관련된 비용 / 혜택이 있습니다.

무엇을해야할지 아는 의미 :

  • 이것은 목수의 코드가 : BuildScaffolding()에서 BuildScaffold()(예를 들어 약간의 이름 변경)으로 변경되면 호출 클래스 (예 : Foreperson클래스)도 변경해야합니다. 대신 (기본적으로 코드를 두 번 변경해야합니다) ) 딱 하나만. 다형성을 사용하면 기본적으로 동일한 결과를 얻기 위해 한 번만 변경하면됩니다.

  • 둘째, 당신은 끊임없이 묻지 않아도됩니다. 당신은 누구입니까? 알았어 ... 너 누구 니? 알았어. .... 다형성-코드를 건조시키고 특정 상황에서 매우 효과적입니다.

  • 다형성을 사용하면 기존 코드를 변경하지 않고도 추가 직업 클래스를 쉽게 추가 할 수 있습니다. (즉, 두 번째 SOLID 설계 원칙 : 개방형 원칙).

해결책

누가 문을 걷든지 상관없이 "Work ()"라고 말할 수 있고, 배관공은 파이프를 다루고, 전기공은 전선을 다루는 직업을 존중합니다.

이 접근법의 장점은 다음과 같습니다. (i) 누가 그 문을 통해 들어가고 있는지 정확히 알 필요는 없습니다. 내가 알아야 할 것은 그들이 일종의 tradie가되고 일을 할 수 있다는 것입니다. , (ii) 특정 거래에 대해 알 필요가 없습니다. 비극은 그것을 처리 할 것입니다.

그래서 이것 대신에 :

If(electrician) then  electrician.FixCablesAndElectricity() 

if(plumber) then plumber.IncreaseWaterPressureAndFixLeaks() 

나는 이런 식으로 할 수 있습니다 :

ITradesman tradie = Tradesman.Factory(); // in reality i know it's a plumber, but in the real world you won't know who's on the other side of the tradie assignment.

tradie.Work(); // and then tradie will do the work of a plumber, or electrician etc. depending on what type of tradesman he is. The foreman doesn't need to know anything, apart from telling the anonymous tradie to get to Work()!!

장점은 무엇입니까?

이점은 목수 등의 특정 직업 요구 사항이 변경되면 감독자가 코드를 변경할 필요가 없으므로 알거나 돌볼 필요가 없다는 것입니다. 중요한 것은 목수가 Work ()의 ​​의미를 알고 있다는 것입니다. 둘째, 새로운 유형의 건설 노동자가 구직 사이트에 올 경우, 감독관은 거래에 대해 아무것도 알 필요가 없습니다. 모든 감독관은 건설 노동자 (예 : 용접기, Glazier, Tiler 등)가 Work ()를 완료하십시오.


설명 된 문제 및 해결책 (인터페이스 유무) :

인터페이스 없음 (예 1) :

예 1 : 인터페이스없이

인터페이스 없음 (예 2) :

예 2 : 인터페이스없이

인터페이스 :

예 3 : 인터페이스 사용의 이점

요약

인터페이스를 사용하면 자신이 누구인지 또는 무엇을 할 수 있는지에 대한 구체적인 지식 없이도 자신이 할당 된 작업을 수행 할 수 있습니다. 이를 통해 기존 코드를 변경하지 않고도 새로운 유형의 거래를 쉽게 추가 할 수 있습니다 (기술적으로는 아주 조금만 변경하는 것이 좋습니다).

위의 내용을 이해하지 못하거나 명확하지 않은 경우 의견을 요청하면 더 나은 답변을 얻으려고 노력할 것입니다.


4
까마귀에서 일하게되는 사람을위한 플러스.
유샤

11

파이썬에서 사용할 수있는 오리 타이핑 이 없으면 C #은 인터페이스를 사용하여 추상화를 제공합니다. 클래스의 종속성이 모두 구체적 유형이면 다른 유형을 전달할 수 없습니다. 인터페이스를 사용하여 인터페이스를 구현하는 모든 유형을 전달할 수 있습니다.


3
+1 맞습니다. 오리 타이핑이 있다면 인터페이스가 필요하지 않습니다. 그러나 그들은 더 강력한 유형 안전을 시행합니다.
Groo

11

피자 예제는 주문을 처리하는 추상 클래스를 사용해야하기 때문에 좋지 않으며 피자는 피자 유형을 무시해야합니다.

공유 속성이있는 경우 인터페이스를 사용하지만 클래스는 다른 위치에서 상속 받거나 사용할 수있는 공통 코드가 없을 때 인터페이스를 사용합니다. 예를 들어, 이것은 폐기 될 수있는 것들이며 IDisposable, 폐기 될 것임을 알고, 폐기 될 때 어떤 일이 일어날 지 모릅니다.

인터페이스는 객체가 수행 할 수있는 작업, 매개 변수 및 반환 유형을 알려주는 계약입니다.


10

기본 클래스를 제어하거나 소유하지 않는 경우를 고려하십시오.

예를 들어 Winforms의 .NET에서 시각적 컨트롤을 사용하면 .NET 프레임 워크에 완전히 정의 된 기본 클래스 Control에서 상속됩니다.

사용자 지정 컨트롤을 만드는 데 있다고 가정합시다. 새 버튼, 텍스트 상자, 목록보기, 그리드 등을 만들고자하며 모든 컨트롤 세트에 고유 한 특정 기능을 갖기를 원합니다.

예를 들어, 테마를 처리하는 일반적인 방법이나 현지화를 처리하는 일반적인 방법을 원할 수 있습니다.

이 경우 "기본 클래스 만 생성"할 수 없습니다. 그렇게 하면 컨트롤과 관련된 모든 것을 다시 구현해야하기 때문 입니다.

대신 Button, TextBox, ListView, GridView 등에서 내려와 코드를 추가하십시오.

그러나 이것은 문제를 일으 킵니다. 이제 어떤 컨트롤이 "내 컨트롤"인지 식별하고 "내 폼에있는 모든 컨트롤에 대해 테마를 X로 설정"이라는 코드를 작성할 수 있습니다.

인터페이스를 입력하십시오.

인터페이스는 객체가 특정 계약을 준수하는지 확인하기 위해 객체를 보는 방법입니다.

"YourButton"을 작성하고 Button에서 내려 가서 필요한 모든 인터페이스에 대한 지원을 추가하십시오.

이를 통해 다음과 같은 코드를 작성할 수 있습니다.

foreach (Control ctrl in Controls)
{
    if (ctrl is IMyThemableControl)
        ((IMyThemableControl)ctrl).SetTheme(newTheme);
}

인터페이스 없이는 불가능하지만 대신 다음과 같은 코드를 작성해야합니다.

foreach (Control ctrl in Controls)
{
    if (ctrl is MyThemableButton)
        ((MyThemableButton)ctrl).SetTheme(newTheme);
    else if (ctrl is MyThemableTextBox)
        ((MyThemableTextBox)ctrl).SetTheme(newTheme);
    else if (ctrl is MyThemableGridView)
        ((MyThemableGridView)ctrl).SetTheme(newTheme);
    else ....
}

네, "is"를 사용하지 말고 캐스팅하면 안됩니다.
Lasse V. Karlsen

4
내가 한숨 을 알아, 그러나 그것은 부수적이다.
Lasse V. Karlsen

7

이 경우 피자 기본 클래스를 정의하고 상속 할 수 있습니다. 그러나 인터페이스를 통해 다른 방식으로는 달성 할 수없는 작업을 수행 할 수있는 두 가지 이유가 있습니다.

  1. 클래스는 여러 인터페이스를 구현할 수 있습니다. 클래스에 필요한 기능 만 정의합니다. 다양한 인터페이스를 구현한다는 것은 클래스가 다른 장소에서 여러 기능을 수행 할 수 있음을 의미합니다.

  2. 인터페이스는 클래스 또는 호출자보다 더 넓은 범위에서 정의 될 수 있습니다. 즉, 기능을 분리하고, 프로젝트 종속성을 분리하고, 하나의 프로젝트 또는 클래스에서 기능을 유지하고이를 다른 곳에서 구현할 수 있습니다.

2의 한 가지 의미는 사용중인 클래스를 변경할 수 있으며 적절한 인터페이스를 구현하기 만하면됩니다.




4

도형을 그리기 위해 API 작업을하는 경우 DirectX 또는 그래픽 호출 또는 OpenGL을 사용할 수 있습니다. 그래서 인터페이스를 만들어서 구현 한 것을 호출 한 것에서 추상화합니다.

따라서 팩토리 메소드를 호출하십시오 MyInterface i = MyGraphics.getInstance(). 그런 다음 계약이 있으므로에서 기대할 수있는 기능을 알 수 있습니다 MyInterface. 따라서 하나의 라이브러리를 다른 라이브러리로 교체하면 함수가 지원된다는 것을 호출 i.drawRectangle하거나 i.drawCube알 수 있습니다 .

Dependency Injection을 사용하는 경우 XML 파일에서 구현을 스왑 아웃 할 수 있으므로 더욱 중요합니다.

따라서 일반적인 용도로 내보낼 수있는 암호화 라이브러리와 미국 회사에만 판매되는 암호화 라이브러리가있을 수 있으며 차이점은 구성 파일을 변경하고 나머지 프로그램은 그렇지 않다는 것입니다 변경되었습니다.

예를 들어 List변수 를 사용해야 하고 ArrayList인지 LinkedList인지 걱정할 필요가 없으므로 .NET의 컬렉션에 많이 사용 됩니다.

인터페이스에 코드를 작성하는 한 개발자는 실제 구현을 변경할 수 있으며 나머지 프로그램은 변경되지 않습니다.

이것은 전체 인터페이스를 모방 할 수 있기 때문에 단위 테스트 할 때 유용하므로 데이터베이스로 이동할 필요가 없지만 정적 데이터를 반환하는 모의 구현으로 인해 걱정할 필요없이 메소드를 테스트 할 수 있습니다 유지 관리를 위해 데이터베이스가 다운되었는지 여부


4

인터페이스는 실제로 구현 클래스가 따라야하는 계약이며 실제로 내가 아는 거의 모든 디자인 패턴의 기본입니다.

귀하의 예에서 인터페이스는 피자 인터페이스를 구현하는 것을 의미 하는 피자라는 것이 구현되었으므로 보장됩니다.

public void Order();

언급 한 코드 후에 다음과 같은 것을 가질 수 있습니다.

public void orderMyPizza(IPizza myPizza) {
//This will always work, because everyone MUST implement order
      myPizza.order();
}

이 방법으로 다형성을 사용하고 신경 쓰는 것은 객체가 order ()에 응답하는 것입니다.


4

이 페이지에서 "composition"이라는 단어를 검색했는데 한 번 보지 못했습니다. 이 답변은 위에서 언급 한 답변 외에도 매우 많이 있습니다.

객체 지향 프로젝트에서 인터페이스를 사용하는 데있어 절대적으로 중요한 이유 중 하나는 상속보다 구성을 선호 할 수 있기 때문입니다. 인터페이스를 구현하면 적용중인 다양한 알고리즘에서 구현을 분리 할 수 ​​있습니다.

데릭 바 나스 (Derek Banas)의 훌륭한 "데코레이터 패턴 (Decorator Pattern)"튜토리얼은 재미있게도 피자를 예로 사용합니다.

https://www.youtube.com/watch?v=j40kRwSm4VE


2
나는 이것이 최선의 대답이 아니라는 사실에 정말 충격을 받았습니다. 모든 유용한 인터페이스는 구성에서 가장 많이 사용됩니다.
Maciej Sitko

3

인터페이스는 다른 클래스간에 연결을 적용하기위한 것입니다. 예를 들어, 자동차와 나무 수업이 있습니다.

public class Car { ... }

public class Tree { ... }

두 클래스 모두에 레코딩 가능한 기능을 추가하려고합니다. 그러나 각 수업에는 자신의 방법을 태울 수 있습니다. 그래서 당신은 단순히 만든다;

public class Car : IBurnable
{
public void Burn() { ... }
}

public class Tree : IBurnable
{
public void Burn() { ... }
}

2
이와 같은 예에서 나를 괴롭히는 질문은 다음과 같습니다. 왜 이것이 도움이됩니까? 이제 IBurnable 유형의 인수를 메소드에 전달할 수 있습니까? IBurnable 인터페이스가있는 모든 클래스를 처리 할 수 ​​있습니까? 내가 찾은 피자 예와 동일합니다. 이 작업을 수행 할 수있는 것이 좋지만 그와 같은 이점은 없습니다. 예를 들어 (현재는 너무 두껍기 때문에) 예제를 확장하거나 그와 함께 작동하지 않는 예를 제공 할 수 있습니까 (다시 말하면 실제로는 두껍게 느껴지기 때문입니다). 고마워
Nebelhom

1
동의하다. 인터페이스 = "수행 가능". 클래스 / 추상 Calss = "Is A"
Peter.Wang

3

인터페이스가 필요할 때 필요할 것입니다. :) 예제를 공부할 수는 있지만 Aha가 필요합니다! 실제로 얻을 수있는 효과.

인터페이스가 무엇인지 알았으니 인터페이스없이 코드 만 작성하면됩니다. 조만간 인터페이스 사용이 가장 자연스러운 문제가되는 문제가 발생합니다.


3

인터페이스의 가장 중요한 이유 중 하나 인 디자인 패턴을 포함하는 게시물이 많지 않은 것에 놀랐습니다 . 계약을 사용하는 것에 대한 더 큰 그림이며 기계 코드의 구문 장식이지만 (정직하게 말하면 컴파일러는 무시할 수 있습니다) 추상화 및 인터페이스는 OOP, 인간 이해 및 복잡한 시스템 아키텍처에 중추적입니다.

본격적인 3 코스 식사로 피자 비유를 확장 해 봅시다. 우리는 여전히 Prepare()모든 음식 카테고리에 대한 핵심 인터페이스를 갖지만 코스 선택 (스타터, 메인, 디저트) 및 음식 유형 (사 보리 / 달콤함, 채식 / 비 채식, 글루텐 프리 등).

이러한 사양을 기반으로 전체 프로세스를 개념화 하기 위해 Abstract Factory 패턴을 구현할 수 있지만 인터페이스 만 사용하여 기초 만 구체적으로 만들 수 있습니다. 다른 모든 것들은 융통성이 있거나 다형성을 장려 할 수 있지만 Course, ICourse인터페이스 를 구현하는 다른 클래스들 사이에서 캡슐화를 유지 합니다 .

더 많은 시간이 있다면, 이것의 완전한 예를 그려보고 싶거나 누군가 나를 위해 이것을 확장 할 수는 있지만 C # 인터페이스는이 유형의 시스템을 설계하는 데 가장 좋은 도구가 될 것입니다.


1
이 답변에는 더 많은 포인트가 필요합니다! 디자인 된 패턴 (예 : 상태 패턴)에 사용될 때 인터페이스가 빛납니다. 자세한 내용은 plus.google.com/+ZoranHorvat-Programming 을 참조하십시오
Alex Nolasco

3

직사각형 모양의 객체에 대한 인터페이스는 다음과 같습니다.

interface IRectangular
{
    Int32 Width();
    Int32 Height();
}

필요한 것은 객체의 너비와 높이에 액세스하는 방법을 구현하는 것입니다.

이제 다음과 같은 객체에서 작동하는 메소드를 정의 해 보겠습니다 IRectangular.

static class Utils
{
    public static Int32 Area(IRectangular rect)
    {
        return rect.Width() * rect.Height();
    }
}

직사각형 객체의 영역을 반환합니다.

SwimmingPool직사각형 클래스 를 구현해 봅시다 :

class SwimmingPool : IRectangular
{
    int width;
    int height;

    public SwimmingPool(int w, int h)
    { width = w; height = h; }

    public int Width() { return width; }
    public int Height() { return height; }
}

그리고 House직사각형 인 또 다른 클래스 :

class House : IRectangular
{
    int width;
    int height;

    public House(int w, int h)
    { width = w; height = h; }

    public int Width() { return width; }
    public int Height() { return height; }
}

이를 감안할 때 Area주택이나 수영장 에서 메소드를 호출 할 수 있습니다.

var house = new House(2, 3);

var pool = new SwimmingPool(3, 4);

Console.WriteLine(Utils.Area(house));
Console.WriteLine(Utils.Area(pool));

이런 식으로 클래스는 인터페이스의 수를 "상속"(정적 방법) 할 수 있습니다.


3

인터페이스는 특정 기능의 공급자와 해당 소비자 간의 계약을 정의합니다. 계약 (인터페이스)에서 구현을 분리합니다. 객체 지향 아키텍처와 디자인을 살펴 봐야합니다. wikipedia로 시작할 수 있습니다 : http://en.wikipedia.org/wiki/Interface_(computing)


3

뭐 ?

인터페이스는 기본적으로 인터페이스를 구현 하는 모든 클래스 가 따라야 하는 계약입니다 . 클래스처럼 보이지만 구현이 없습니다.

에서 C#인터페이스 관례 이름은 당신이 인터페이스라는 형태를 갖고 싶어 경우는 'I'그래서, 당신이로 선언 것이다 앞에 덧붙이에 의해 정의된다IShapes

이제 왜?

Improves code re-usability

당신이 그리는하고 싶은 말은하자 Circle, Triangle. 당신은 그룹화 할 수 있습니다 그들이 함께하고 그들에게 전화 Shapes하고 그리는 방법이 Circle그리고 Triangle 내일이 더 2를 결정할 수 있기 때문에 그러나 구체적인 구현을 갖는 것이 나쁜 생각을 Shapes Rectangle& Square. 이제 그것들을 추가하면 코드의 다른 부분을 깰 수도 있습니다.

인터페이스를 사용하면 계약과 다른 구현을 분리 할 수 ​​있습니다


라이브 시나리오 1 일차

원과 삼각형을 그리는 앱을 만들라는 요청을 받았습니다.

interface IShapes
{
   void DrawShape();
   
 }

class Circle : IShapes
{
    
    public void DrawShape()
    {
        Console.WriteLine("Implementation to Draw a Circle");
    }
}

Class Triangle: IShapes
{
     public void DrawShape()
    {
        Console.WriteLine("Implementation to draw a Triangle");
    }
}
static void Main()
{
     List <IShapes> shapes = new List<IShapes>();
        shapes.Add(new Circle());
        shapes.Add(new Triangle());

        foreach(var shape in shapes)
        {
            shape.DrawShape();
        }
}

라이브 시나리오 2 일차

추가 Square및 추가를 요청받은 경우 Rectangle,이를 위해 목록 class Square: IShapesMain추가를 위해 함축을 작성하기 만하면 됩니다.shapes.Add(new Square());


왜 수백 번의 반대 답변을 수백 건의 답변으로 6 년 된 질문에 대한 답변을 추가합니까? 여기에 아직 언급되지 않은 것은 없습니다.
Jonathon Reinhart

@ JonathonReinhart, 예, 그렇게 생각했지만이 예와 그 설명 방법이 다른 신체보다 신체에 더 잘 도움이 될 것이라고 생각했습니다.
클린트

2

여기에는 많은 좋은 답변이 있지만 약간 다른 관점에서 시도하고 싶습니다.

객체 지향 설계의 SOLID 원리에 익숙 할 것입니다. 요약해서 말하자면:

S-단일 책임 원칙 O-개방 / 폐쇄 원칙 L-Liskov 대체 원칙 I-인터페이스 분리 원칙 D-종속성 반전 원리

SOLID 원칙을 준수하면 깨끗하고, 팩토링되고, 응집력 있고 느슨하게 결합 된 코드를 생성 할 수 있습니다. 을 고려하면:

"종속성 관리는 모든 규모의 소프트웨어에서 핵심 과제"(Donald Knuth)

의존성 관리에 도움이되는 것은 큰 승리입니다. 인터페이스와 의존성 역전 원리는 실제로 구체적인 클래스에 대한 의존성에서 코드를 분리하는 데 도움이되므로 구현보다는 동작 측면에서 코드를 작성하고 추론 할 수 있습니다 . 이렇게하면 코드를 컴파일 타임이 아닌 런타임에 구성 할 수있는 구성 요소로 나누는 데 도움이되며, 나머지 코드를 변경하지 않고도 해당 구성 요소를 쉽게 꽂거나 뺄 수 있습니다.

인터페이스는 특히 Dependency Inversion Principle에 도움이됩니다. 여기서 코드는 서비스 모음으로 구성 될 수 있으며 각 서비스는 인터페이스로 설명됩니다. 그런 다음 서비스를 생성자 매개 변수로 전달하여 서비스를 런타임에 클래스에 "주입"할 수 있습니다. 이 기술은 단위 테스트를 작성하고 테스트 중심 개발을 사용하는 경우 매우 중요합니다. 시도 해봐! 인터페이스를 통해 코드를 개별적으로 테스트 할 수있는 관리 가능한 청크로 분리하는 방법을 빠르게 이해할 수 있습니다.


1

인터페이스의 주요 목적은 사용자와 코드를 분리하고 확장 성을 허용하는 해당 인터페이스를 구현하는 다른 클래스간에 계약을 체결하는 것입니다.


1

거기에는 정말 좋은 예가 있습니다.

다른 하나는 switch 문의 경우 특정 방식으로 작업을 수행 할 때마다 더 이상 유지 관리 및 전환이 필요하지 않습니다.

피자 예제에서 피자를 만들고 싶다면 인터페이스 만 있으면됩니다. 각 피자는 자체 논리를 처리합니다.

이는 커플 링 및 순환 복잡성을 줄이는 데 도움이됩니다. 여전히 논리를 구현해야하지만 더 넓은 범위에서 추적해야 할 부분이 줄어 듭니다.

각 피자에 대해 해당 피자와 관련된 정보를 추적 할 수 있습니다. 다른 피자 만 알면되기 때문에 다른 피자는 중요하지 않습니다.


1

인터페이스에 대해 생각하는 가장 간단한 방법은 상속이 무엇을 의미하는지 인식하는 것입니다. CC 클래스가 C 클래스를 상속하면 다음 두 가지를 모두 의미합니다.

  1. 클래스 CC는 클래스 C의 공용 또는 보호 멤버를 자신의 것처럼 사용할 수 있으므로 부모 클래스에 존재하지 않는 것만 구현하면됩니다.
  2. CC에 대한 참조는 C에 대한 참조를 기대하는 루틴 또는 변수에 전달되거나 지정 될 수 있습니다.

상속의 두 기능은 어떤 의미에서 독립적입니다. 상속은 두 가지를 동시에 적용하지만 첫 번째없이 두 번째를 적용 할 수도 있습니다. 객체가 두 개 이상의 관련되지 않은 클래스에서 멤버를 상속하도록 허용하는 것이 한 유형의 항목을 여러 유형으로 대체 할 수있게하는 것보다 훨씬 복잡하기 때문에 유용합니다.

인터페이스는 다소 추상적 인 기본 클래스와 비슷하지만 중요한 차이점이 있습니다. 기본 클래스를 상속받는 객체는 다른 클래스를 상속 할 수 없습니다. 반대로, 객체는 원하는 클래스를 상속하거나 다른 인터페이스를 구현하는 능력에 영향을 미치지 않으면 서 인터페이스를 구현할 수 있습니다.

이것의 좋은 특징 중 하나는 .net 프레임 워크 (IMHO에서 사용되지 않음)는 객체가 할 수있는 일을 선언적으로 표시 할 수 있다는 것입니다. 예를 들어, 일부 객체는 (목록에서 가능한 것처럼) 인덱스로 항목을 검색 할 수있는 데이터 소스 객체를 원하지만 거기에 아무것도 저장할 필요가 없습니다. 다른 루틴에는 Collection.Add와 같이 인덱스가 아닌 것을 저장할 수있는 데이터 저장소 객체가 필요하지만 아무것도 읽을 필요가 없습니다. 일부 데이터 유형은 인덱스로 액세스를 허용하지만 쓰기는 허용하지 않습니다. 다른 사람은 쓰기를 허용하지만 색인으로 액세스 할 수는 없습니다. 물론 일부는 둘 다 허용합니다.

ReadableByIndex와 Appendable이 관련이없는 기본 클래스라면 ReadableByIndex를 기대하는 것과 Appendable을 기대하는 것에 모두 전달 될 수있는 타입을 정의하는 것은 불가능합니다. ReadableByIndex 또는 Appendable이 다른 것에서 파생되도록하여이를 완화하려고 시도 할 수 있습니다. 파생 클래스는 두 가지 목적을 위해 공개 멤버를 제공해야하지만 일부 공개 멤버는 실제로 작동하지 않을 수 있음을 경고합니다. Microsoft의 클래스와 인터페이스 중 일부는 그렇게하지만 다소 불안정합니다. 보다 깔끔한 접근 방식은 다양한 목적을위한 인터페이스를 보유한 다음 객체가 실제로 수행 할 수있는 작업을위한 인터페이스를 구현하는 것입니다. 인터페이스 IReadableByIndex와 인터페이스 IAppendable이있는 경우,


1

인터페이스를 데이지 체인으로 연결하여 또 다른 인터페이스를 만들 수도 있습니다. 여러 인터페이스를 구현할 수있는이 기능을 통해 개발자는 현재 클래스 기능을 변경하지 않고도 클래스에 기능을 추가 할 수 있습니다 (SOLID 원칙).

O = "확장을 위해 클래스를 열어야하지만 수정을 위해 클래스를 닫아야합니다"


1

나에게 인터페이스의 장점 / 장점은 추상 클래스보다 더 유연하다는 것입니다. 추상 클래스는 1 개만 상속 할 수 있지만 여러 인터페이스를 구현할 수 있으므로 여러 위치에서 추상 클래스를 상속하는 시스템을 변경하면 문제가 발생합니다. 100 개소에서 상속되면 100 개로 변경해야합니다. 그러나 인터페이스를 사용하면 새 인터페이스에 새 변경 사항을 배치하고 필요한 경우 해당 인터페이스를 사용할 수 있습니다 (SOLID의 Interface Seq.). 또한 인터페이스 예제의 객체가 인터페이스를 구현하는 위치 수에 관계없이 메모리에서 한 번만 사용되므로 메모리 사용량이 적은 것처럼 보입니다.


1

인터페이스는 느슨하게 결합 된 방식으로 일관성을 유도하는 데 사용되며, 이는 밀접하게 결합 된 추상 클래스와 다릅니다. 따라서 이것이 일반적으로 계약으로 정의되는 이유입니다. 인터페이스를 구현하는 클래스는 "규칙 / 구문"을 따릅니다. 인터페이스에 의해 정의되며 그 안에 구체적인 요소가 없습니다.

아래 그래픽으로 지원되는 예를 보여 드리겠습니다.

공장에는 3 종류의 기계가 있다고 상상해보십시오. 사각형 기계, 삼각형 기계 및 다각형 기계. 시간이 경쟁력이 있고 운전자 훈련을 간소화하고 싶습니다. 기계를 시작하고 멈추는 한 가지 방법으로 훈련시키고 싶습니다. 이제 녹색 시작 버튼과 빨간색 중지 버튼이 있습니다. 이제 3 개의 다른 머신에서 3 개의 다른 유형의 머신을 시작하고 중지하는 일관된 방법이 있습니다. 이러한 머신은 클래스이며 클래스에 시작 및 중지 방법이 필요하다고 생각하십시오. 매우 다른 클래스들 사이에서 일관성을 추구 할 것입니까? 인터페이스가 답입니다.

여기에 이미지 설명을 입력하십시오

당신이 시각화하는 데 도움이되는 간단한 예, 추상 클래스를 사용하지 않는 이유는 무엇입니까? 인터페이스를 사용하면 개체를 직접 관련 시키거나 상속 할 필요가 없으며 여러 클래스간에 일관성을 유지할 수 있습니다.

public interface IMachine
{
    bool Start();
    bool Stop();
}

public class Car : IMachine
{
    public bool Start()
    {
        Console.WriteLine("Car started");
        return true;
    }

    public bool Stop()
    {
        Console.WriteLine("Car stopped");
        return false;
    }
}

public class Tank : IMachine
{
    public bool Start()
    {
        Console.WriteLine("Tank started");
        return true;
    }

    public bool Stop()
    {
        Console.WriteLine("Tank stopped");
        return false;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var car = new Car();
        car.Start();
        car.Stop();

        var tank = new Tank();
        tank.Start();
        tank.Stop();

    }
}

1
class Program {
    static void Main(string[] args) {
        IMachine machine = new Machine();
        machine.Run();
        Console.ReadKey();
    }

}

class Machine : IMachine {
    private void Run() {
        Console.WriteLine("Running...");
    }
    void IMachine.Run() => Run();
}

interface IMachine
{
    void Run();
}

이것을 다른 관점에서 설명하겠습니다. 위에서 보여준 예제에 따라 스토리를 만들어 봅시다.

프로그램, 기계 및 IMachine은 우리 이야기의 배우입니다. 프로그램이 실행되기를 원하지만 그 능력이 없으며 Machine은 실행 방법을 알고 있습니다. 기계와 IMachine은 가장 친한 친구이지만 프로그램은 기계와 용어를 사용하지 않습니다. 따라서 Program과 IMachine은 거래를하고 IMachine은 기계를 리플렉터와 같은 기계를 통해 실행하는 방법을 Program에 알릴 것이라고 결정했습니다.

그리고 프로그램은 IMachine의 도움으로 실행하는 방법을 배웁니다.

인터페이스는 커뮤니케이션을 제공하고 느슨하게 결합 된 프로젝트를 개발합니다.

추신 : 나는 구체적인 수업의 방법을 개인으로했습니다. 여기서의 목표는 구체적인 클래스 속성 및 메소드에 대한 액세스를 막고 느슨하게 결합되고 인터페이스를 통해 도달 할 수있는 방법을 남겨 두는 것입니다. (따라서 인터페이스의 메소드를 명시 적으로 정의했습니다).


1

나는 내가 매우 늦었다는 것을 알고있다. (거의 9 년) 누군가가 작은 설명을 원한다면 다음과 같이 할 수있다.

간단히 말해서, 객체가 무엇을 할 수 있는지 또는 객체에 어떤 기능을 구현할 것인지 알 때 인터페이스를 사용합니다. 예제 삽입, 업데이트 및 삭제.

interface ICRUD{
      void InsertData(); // will insert data
      void UpdateData(); // will update data
      void DeleteData(); // will delete data
}

중요 사항 : 인터페이스는 항상 공개입니다.

도움이 되었기를 바랍니다.

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