IT story

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

hot-time 2020. 4. 7. 08:34
반응형

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


인터페이스의 이유는 저를 정말로 피합니다. 내가 이해 한 바에 따르면 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
    }
}

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

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

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

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


아무도 인터페이스가 어떻게 유용한 지에 대한 명확한 용어를 실제로 설명하지 않았으므로, 나는 그것을 쏘게 될 것입니다 (그리고 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는 자동으로 각 피자를 올바르게 준비합니다 컬렉션에 여러 유형의 피자가 있더라도 유형에 따라 다릅니다.


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

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

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

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의 메소드에는 문제없이 인터페이스를 구현하는 클래스의 인스턴스가 전달 될 수 있습니다.


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

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
    }
}

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

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();
    }
}

유추에 대한 간단한 설명

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

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

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

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

위의 접근 방식의 문제점은 내가해야한다는 것입니다. (i) 누가 그 문을 걷고 있는지 알고 있으며 그것이 누구인지에 따라 그들에게 무엇을해야하는지 알려야합니다. 그것은 특정 거래에 대한 모든 것을 알아야한다는 것을 의미합니다. 이 방법과 관련된 비용 / 혜택이 있습니다.

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

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

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

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

해결책

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

이 접근법의 장점은 다음과 같습니다. (i) 누가 그 문을 통과하고 있는지 정확히 알 필요가 없습니다. 내가 알아야 할 것은 그들이 일종의 비극이되고 일을 할 수 있다는 것입니다. , (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 : 인터페이스 사용의 이점

요약

인터페이스를 사용하면 자신이 누구인지 또는 무엇을 할 수 있는지에 대한 구체적인 지식 없이도 자신이 할당 된 작업을 수행 할 수 있습니다. 이를 통해 기존 코드를 변경하지 않고도 새로운 유형의 거래를 쉽게 추가 할 수 있습니다 (기술적으로는 아주 조금만 변경해도 됨). OOP 접근 방식과 기능적 프로그래밍 방법론의 실질적인 이점입니다.

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


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


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

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

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


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

예를 들어 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 ....
}

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

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

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

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


C #에서 다중 상속을 사용할 수 없다고 가정하고 질문을 다시 살펴보십시오.


인터페이스 = 수축, 느슨한 결합에 사용됩니다 ( GRASP 참조 ).


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

So you call a factory method: MyInterface i = MyGraphics.getInstance(). Then, you have a contract, so you know what functions you can expect in MyInterface. So, you can call i.drawRectangle or i.drawCube and know that if you swap one library out for another, that the functions are supported.

This becomes more important if you are using Dependency Injection, as then you can, in an XML file, swap implementations out.

So, you may have one crypto library that can be exported that is for general use, and another that is for sale only to American companies, and the difference is in that you change a config file, and the rest of the program isn't changed.

This is used a great deal with collections in .NET, as you should just use, for example, List variables, and don't worry whether it was an ArrayList or LinkedList.

As long as you code to the interface then the developer can change the actual implementation and the rest of the program is left unchanged.

This is also useful when unit testing, as you can mock out entire interfaces, so, I don't have to go to a database, but to a mocked out implementation that just returns static data, so I can test my method without worrying if the database is down for maintenance or not.


An interface is really a contract that the implementing classes must follow, it is in fact the base for pretty much every design pattern I know.

In your example, the interface is created because then anything that IS A Pizza, which means implements the Pizza interface, is guaranteed to have implemented

public void Order();

After your mentioned code you could have something like this:

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

This way you are using polymorphism and all you care is that your objects respond to order().


I did a search for the word "composition" on this page and didn't see it once. This answer is very much in addition to the answers aforementioned.

One of the absolutely crucial reasons for using interfaces in an Object Oriented Project is that they allow you to favour composition over inheritance. By implementing interfaces you can decouple your implementations from the various algorithms you are applying to them.

This superb "Decorator Pattern" tutorial by Derek Banas (which - funnily enough - also uses pizza as an example) is a worthwhile illustration:

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


Interfaces are for applying connection between different classes. for example, you have a class for car and a tree;

public class Car { ... }

public class Tree { ... }

you want to add a burnable functionality for both classes. But each class have their own ways to burn. so you simply make;

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

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

You will get interfaces, when you will need them :) You can study examples, but you need the Aha! effect to really get them.

Now that you know what interfaces are, just code without them. Sooner or later you will run into a problem, where the use of interfaces will be the most natural thing to do.


I'm surprised that not many posts contain the one most important reason for an interface: Design Patterns. It's the bigger picture into using contracts, and although it's a syntax decoration to machine code (to be honest, the compiler probably just ignores them), abstraction and interfaces are pivotal for OOP, human understanding, and complex system architectures.

Let's expand the pizza analogy to say a full fledge 3 course meal. We'll still have the core Prepare() interface for all our food categories, but we'd also have abstract declarations for course selections (starter, main, dessert), and differing properties for food types (savoury/sweet, vegetarian/non-vegetarian, gluten free etc).

Based on these specifications, we could implement the Abstract Factory pattern to conceptualise the whole process, but use interfaces to ensure that only the foundations were concrete. Everything else could become flexible or encourage polymorphism, yet maintain encapsulation between the different classes of Course that implement the ICourse interface.

If I had more time, I'd like to draw up a full example of this, or someone can extend this for me, but in summary, a C# interface would be the best tool in designing this type of system.


Here's an interface for objects that have a rectangular shape:

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

All it demands is that you implement ways to access the width and height of the object.

Now let's define a method that will work on any object that is IRectangular:

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

That will return the area of any rectangular object.

Let's implement a class SwimmingPool that is rectangular:

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; }
}

And another class House that is also rectangular:

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; }
}

Given that, you can call the Area method on houses or swimming-pools:

var house = new House(2, 3);

var pool = new SwimmingPool(3, 4);

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

In this way, your classes can "inherit" behavior (static-methods) from any number of interfaces.


An interface defines a contract between the provider of a certain functionality and the correspondig consumers. It decouples the implementation from the contract (interface). You should have a look at object oriented architecture and design. You may want to start with wikipedia: http://en.wikipedia.org/wiki/Interface_(computing)


There are a lot of good answers here but I would like to try from a slightlt different perspective.

You may be familiar with the SOLID principles of object oriented design. In summary:

S - Single Responsibility Principle O - Open/Closed Principle L - Liskov Substitution Principle I - Interface Segregation Principle D - Dependency Inversion Principle

Following the SOLID principles helps to produce code that is clean, well factored, cohesive and loosely coupled. Given that:

"Dependency management is the key challenge in software at every scale" (Donald Knuth)

then anything that helps with dependency management is a big win. Interfaces and the Dependency Inversion Principle really help to decouple code from dependencies on concrete classes, so code can be written and reasoned about in terms of behaviours rather than implementations. This helps to break the code into components which can be composed at runtime rather than compile time and also means those components can be quite easily plugged in and out without having to alter the rest of the code.

Interfaces help in particular with the Dependency Inversion Principle, where code can be componentized into a collection of services, with each service being described by an interface. Services can then be "injected" into classes at runtime by passing them in as a constructor parameter. This technique really becomes critical if you start to write unit tests and use test driven development. Try it! You will quickly understand how interfaces help to break apart the code into manageable chunks that can be individually tested in isolation.


The main purpose of the interfaces is that it makes a contract between you and any other class that implement that interface which makes your code decoupled and allows expandability.


Therese are ask really great examples.

Another, in the case of a switch statement, you no longer have the need to maintain and switch every time you want rio perform a task in a specific way.

In your pizza example, if want to make a pizza, the interface is all you need, from there each pizza takes care of it's own logic.

This helps to reduce coupling and cyclomatic complexity. You have to still implement the logic but there will be less you have to keep track of in the broader picture.

For each pizza you can then keep track of information specific to that pizza. What other pizzas have doesn't matter because only the other pizzas need to know.


The simplest way to think about interfaces is to recognize what inheritance means. If class CC inherits class C, it means both that:

  1. Class CC can use any public or protected members of class C as though they were its own, and thus only needs to implement things which do not exist in the parent class.
  2. A reference to a CC can be passed or assigned to a routine or variable that expects a reference to a C.

Those two function of inheritance are in some sense independent; although inheritance applies both simultaneously, it is also possible to apply the second without the first. This is useful because allowing an object to inherit members from two or more unrelated classes is much more complicated than allowing one type of thing to be substitutable for multiple types.

An interface is somewhat like an abstract base class, but with a key difference: an object which inherits a base class cannot inherit any other class. By contrast, an object may implement an interface without affecting its ability to inherit any desired class or implement any other interfaces.

One nice feature of this (underutilized in the .net framework, IMHO) is that they make it possible to indicate declaratively the things an object can do. Some objects, for example, will want data-source object from which they can retrieve things by index (as is possible with a List), but they won't need to store anything there. Other routines will need a data-depository object where they can store things not by index (as with Collection.Add), but they won't need to read anything back. Some data types will allow access by index, but won't allow writing; others will allow writing, but won't allow access by index. Some, of course, will allow both.

If ReadableByIndex and Appendable were unrelated base classes, it would be impossible to define a type which could be passed both to things expecting a ReadableByIndex and things expecting an Appendable. One could try to mitigate this by having ReadableByIndex or Appendable derive from the other; the derived class would have to make available public members for both purposes, but warn that some public members might not actually work. Some of Microsoft's classes and interfaces do that, but that's rather icky. A cleaner approach is to have interfaces for the different purposes, and then have objects implement interfaces for the things they can actually do. If one had an interface IReadableByIndex and another interface IAppendable, classes which could do one or the other could implement the appropriate interfaces for the things they can do.


Interfaces can also be daisy chained to create yet another interface. This ability to implement multiple Interfaces give the developer the advantage of adding functionality to their classes without having to change current class functionality (SOLID Principles)

O = "Classes should be open for extension but closed for modification"


To me an advantage/benefit of an interface is that it is more flexible than an abstract class. Since you can only inherit 1 abstract class but you can implement multiple interfaces, changes to a system that inherits an abstract class in many places becomes problematic. If it is inherited in 100 places, a change requires changes to all 100. But, with the interface, you can place the new change in a new interface and just use that interface where its needed (Interface Seq. from SOLID). Additionally, the memory usage seems like it would be less with the interface as an object in the interface example is used just once in memory despite how many places implement the interface.


Am I correct then to take another look at Interfaces, The graphical interface (winforms / WPF) is like the Interface. It displays only what the end user will interact with. The end user will then not have to know what went into the design of the application, but would know what they can do with it, based on available options on the form. In OOP view then the idea is to create a structured interface that informs other users of your e.g DLL libraries what is available to use and that it's like an guarantee/contract that the field, methods and properties will be available to use (inherit in your classes).


I share your sense that Interfaces are not necessary. Here is a quote from Cwalina pg 80 Framework Design Guidelines "I often here people saying that interfaces specify contracts. I believe this a dangerous myth. Interfaces by themselves do not specify much. ..." He and co-author Abrams managed 3 releases of .Net for Microsoft. He goes on to say that the 'contract' is "expressed" in an implementation of the class. IMHO watching this for decades, there were many people warning Microsoft that taking the engineering paradigm to the max in OLE/COM might seem good but its usefulness is more directly to hardware. Especially in a big way in the 80s and 90s getting interoperating standards codified. In our TCP/IP Internet world there is little appreciation of the hardware and software gymnastics we would jump through to get solutions 'wired up' between and among mainframes, minicomputers, and microprocessors of which PCs were just a small minority. So coding to interfaces and their protocols made computing work. And interfaces ruled. But what does solving making X.25 work with your application have in common with posting recipes for the holidays? I have been coding C++ and C# for many years and I never created one once.

참고 URL : https://stackoverflow.com/questions/6802573/interfaces-whats-the-point

반응형