programing

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

showcode 2023. 5. 25. 22:16
반응형

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

인터페이스에 대한 이유는 정말로 저를 피합니다.제가 이해한 바로는, 그것은 C#에 존재하지 않는 존재하지 않는 다중 상속에 대한 일종의 해결책입니다.

일부 멤버와 함수를 미리 정의한 다음 클래스에서 다시 정의해야 합니다.따라서 인터페이스가 중복됩니다.그냥 통사적인 느낌이 들어요. 음, 제게는 쓰레기 같은 느낌이에요. (제발 기분 나쁘게 생각하지 마세요.쓸모없는 물건에 있는 쓰레기 같은 것.

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

쉬운 예(다른 스택 오버플로 기여도에서 추출)

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

아무도 인터페이스가 어떻게 유용한지 명확하게 설명하지 않았기 때문에, 저는 한번 시도해 보려고 합니다(그리고 샤밈의 대답에서 아이디어를 조금 훔칩니다).

피자 주문 서비스를 생각해 봅시다.여러 종류의 피자를 먹을 수 있으며 각 피자에 대한 일반적인 동작은 시스템에서 주문을 준비하는 것입니다.각각의 피자는 준비되어야 하지만 각각의 피자는 다르게 준비됩니다.예를 들어, 속이 꽉 찬 크러스트 피자를 주문할 때 시스템은 아마도 식당에서 특정 재료를 사용할 수 있는지 확인하고 깊은 요리 피자에 필요하지 않은 재료를 따로 두어야 할 것입니다.

이것을 코드로 쓸 때, 기술적으로 당신은 그냥 할 수 있습니다.

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

종류가, 이 의 부분은 피자를 다루고 있는지 쓸 , 피자를 것을 때문에, 각각의 은 비각록각의종피류다준라자도더비되르게가의어우부종코상다떤리이가피관하지아다니도않됩지는루를은분의자류의드▁call▁even▁to▁is▁and▁knows▁it다니됩▁doesn▁that도▁we상▁each아않▁code비록▁this코지다하관▁of▁it,▁different▁prepared▁part▁therefore▁type지는▁just루이피부어▁of 그것은 단지 그것이 피자를 위해 불려지고 있다는 것을 알고 있기 때문에 각각의 전화가 있습니다.Prepare에서는 컬렉션에 여러 종류의 피자가 있더라도 유형에 따라 각 피자를 자동으로 올바르게 준비합니다.

요점은 인터페이스가 계약을 나타낸다는 것입니다.모든 구현 클래스가 가져야 하는 공용 메서드 집합입니다.기술적으로, 인터페이스는 구문, 즉 어떤 방법이 있는지, 어떤 인수를 얻고 무엇을 반환하는지만 관리합니다.일반적으로 의미론도 캡슐화합니다. 문서화를 통해서만 가능합니다.

그런 다음 다양한 인터페이스 구현을 수행하고 원하는 대로 전환할 수 있습니다.당신의 예에서, 모든 피자 인스턴스는IPizza사용할 수 있습니다.IPizza알려지지 않은 피자 종류의 사례를 다루는 곳이라면 어디든.이 형이상모인스스턴에서 상속되는 IPizza그것은 그것이 있기 때문에 주문 가능하도록 보장됩니다.Order()방법.

Python은 정적으로 입력되지 않으므로 런타임에 유형이 유지되고 조회됩니다.그래서 당신은 전화를 걸어볼 수 있습니다.Order()모든 객체에 대한 방법.런타임은 객체가 이러한 메소드를 가지고 있는 한 행복하고 그렇지 않으면 그저 어깨를 으쓱하고 "Meh."라고 말할 수 있습니다.C#에서는 그렇지 않습니다.하며 임의의 .object컴파일러는 런타임 중에 인스턴스에 해당 메서드가 있는지 여부를 아직 알지 못합니다.컴파일러의 관점에서는 검증할 수 없기 때문에 무효입니다. (반성이나.dynamic키워드, 하지만 지금은 그것이 좀 지나치다고 생각합니다.)

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

시작할 때 코드를 더 쉽고 빠르게 작성할 수 있는 것으로 보지 않을 때만 이러한 점이 명확해졌습니다. 이것은 그들의 목적이 아닙니다.다양한 용도가 있습니다.

(이것은 피자의 유사성을 잃게 될 것입니다. 왜냐하면 이것의 사용을 시각화하는 것은 매우 쉽지 않기 때문입니다.)

화면에서 간단한 게임을 만들고 있다고 가정하면, 그것은 당신이 상호작용하는 생물체를 가지고 있을 것입니다.

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주 후에 마케팅 부서에서는 트위터에서 오크에 대해 읽었기 때문에 다음과 같은 작업을 수행해야 합니다.

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

이제 프런트 엔드는 인터페이스 IC 생성에만 관심이 있습니다. 트롤이나 오크의 내부 구현에는 관심이 없고 IC 생성을 구현한다는 사실에만 관심이 있습니다.

이 관점에서 이것을 볼 때 주의해야 할 중요한 점은 추상적인 생명체 클래스도 쉽게 사용할 수 있었다는 것이고, 이 관점에서 보면 이것도 같은 효과를 낼 수 있다는 것입니다.

그리고 당신은 공장에서 창조물을 추출할 수 있습니다.

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 = _factory.GetCreature(creatureType);

    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 프레임워크(예: Inject 또는 Autofac)를 사용하여 런타임에 생성자에 ICreatureFactory가 필요할 때마다 CreatureFactory 인스턴스가 생성되도록 설정할 수 있습니다. 이는 코드를 멋지고 단순하게 만듭니다.

또한 컨트롤러에 대한 단위 테스트를 작성할 때 조롱된 ICreatureFactory(예: 구체적인 구현에 DB 액세스가 필요한 경우 이에 종속되지 않도록 함)를 제공하고 컨트롤러에서 코드를 쉽게 테스트할 수 있습니다.

D: '레거시'의 이유로 A와 B 두 개의 프로젝트가 제대로 구성되지 않은 경우와 A가 B를 참조하는 경우 등 다른 용도가 있습니다.

그런 다음 B에서 이미 A에 있는 메서드를 호출해야 하는 기능을 찾습니다.순환 참조를 얻음에 따라 구체적인 구현을 사용하여 이 작업을 수행할 수 없습니다.

A의 클래스가 구현하는 인터페이스를 B로 선언할 수 있습니다.B의 당신의 메소드는 구체적인 객체가 A의 유형임에도 불구하고 인터페이스를 구현하는 클래스의 인스턴스를 문제없이 통과할 수 있습니다.

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

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):

예 1: 인터페이스 없음

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

예 2: 인터페이스 없음

인터페이스 포함:

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

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

유추:그래서 저는 건설 현장의 현장 책임자입니다.저는 어느 무역업자가 들어올지 모릅니다.하지만 저는 그들에게 무엇을 하라고 말합니다.

  1. 만약 그것이 목수라면 나는 말합니다.build wooden scaffolding.
  2. 배관공이라면 이렇게 말하죠Set up the pipes
  3. 만약 그것이 BJP 정부 관료들, 내 말은,three bags full of cash, sir.

위의 접근법의 문제는 제가 해야 한다는 것입니다. (저는) 누가 그 문을 열고 들어오는지 알고 있고, 그 문이 누구인지에 따라, 저는 그들에게 무엇을 해야 하는지 말해야 합니다.이로 인해 일반적으로 코드를 유지하기 어렵거나 오류가 발생하기 쉽습니다.

무엇을 해야 하는지 아는 것의 의미:

  • 다음과 같이 합니다: 이은목수코드다변의경미다우니합를는경되서음.BuildScaffolding()BuildScaffold() 변경)클래스도 (으, 약간이변경름즉즉것다).Forepersonclass)도 마찬가지입니다. 코드를 하나만 (수정)하는 대신 두 번 변경해야 합니다.다형성을 사용하는 경우 동일한 결과를 얻기 위해 (기본적으로) 한 가지만 변경하면 됩니다.

  • 둘째로, 당신은 계속해서 물어볼 필요가 없을 것입니다: 당신은 누구입니까?좋아, 이렇게 해봐요...당신은 누구시죠?좋아요. 그렇게 하세요. ...다형성 - 코드를 건조시키고 특정 상황에서 매우 효과적입니다.

  • 다형화를 사용하면 기존 코드를 변경하지 않고도 쉽게 추가 작업자 클래스를 추가할 수 있습니다.(즉, 솔리드 디자인 원칙 중 두 번째):개방-폐쇄 원칙).

해결책

상상해보세요. 누가 들어오든 간에 저는 이렇게 말할 수 있습니다. "일을 하세요." 그리고 그들은 그들이 전문으로 하는 존경하는 일을 합니다. 배관공은 파이프를 다루고, 전기 기술자는 전선을 다루고, 관료는 뇌물을 받고 다른 사람들을 위해 이중으로 일을 할 수 있습니다.

이 접근법의 이점은 다음과 같습니다. (i) 누가 그 문을 통해 들어오는지 정확히 알 필요는 없습니다. 제가 알아야 할 것은 그들이 일종의 무역일 것이고 그들이 일을 할 수 있다는 것이고, 둘째, 저는 그 특정 무역에 대해 아무것도 알 필요가 없다는 것입니다.전통이 그것을 처리할 것입니다.

그래서 이것 대신에:

if(electrician) then  electrician.FixCablesAndElectricity() 

if(plumber) then plumber.IncreaseWaterPressureAndFixLeaks() 

if(keralaCustoms) then keralaCustoms.askForBribes() 

다음과 같은 작업을 수행할 수 있습니다.

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()!!

어떤 혜택이 있습니까?

이점은 목수 등의 구체적인 직업 요구 사항이 변경되면 포어맨이 코드를 변경할 필요가 없다는 것입니다. 그는 알거나 신경 쓸 필요가 없습니다.중요한 것은 목수가 작업()의 의미를 알고 있다는 것입니다.두 번째로, 새로운 유형의 건설 노동자가 현장에 오면 현장 책임자는 업계에 대해 아무것도 알 필요가 없습니다. 현장 책임자의 관심사는 건설 노동자(예: Welder, Glazier, Tiler 등)가 작업()을 완료할 수 있는지 여부입니다.

요약

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

위의 내용 중 하나라도 이해가 안 되거나 명확하지 않은 경우 댓글로 문의하시면 답변을 더 좋게 하도록 노력하겠습니다.

다음은 다시 설명한 예입니다.

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

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

피자 예제는 순서를 처리하는 추상 클래스를 사용해야 하고, 예를 들어 피자는 피자 유형을 무시해야 하기 때문에 좋지 않습니다.

공유 속성이 있지만 클래스가 다른 위치에서 상속되거나 사용할 수 있는 공통 코드가 없는 경우 인터페이스를 사용합니다.를 들어,할 수 것, 처분할 수 있는 입니다.IDisposable당신은 그것이 처분될 것이라는 것을 알고 있고, 그것이 처분될 때 무슨 일이 일어날지 모를 뿐입니다.

인터페이스는 객체가 수행할 수 있는 몇 가지 작업, 어떤 매개 변수 및 어떤 반환 유형을 예상하는지 알려주는 계약일 뿐입니다.

기본 클래스를 제어하거나 소유하지 않는 경우를 고려합니다.

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

사용자 지정 컨트롤을 생성하는 업무를 수행하고 있다고 가정합니다.새 단추, 텍스트 상자, 목록 보기, 그리드 등을 작성하고 컨트롤 집합에 고유한 특정 기능을 모두 사용할 수 있습니다.

예를 들어, 일반적인 방법으로 Ming을 처리하거나 일반적인 방법으로 현지화를 처리할 수 있습니다.

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

대신 단추, 텍스트 상자, 목록 보기, 그리드 보기 등에서 아래로 내려와서 코드를 추가합니다.

하지만 이것은 문제를 제기합니다. 이제 어떤 컨트롤이 "당신의 것"인지 어떻게 식별할 수 있는지, "내 것인 폼의 모든 컨트롤에 대해 테마를 X로 설정하십시오."라고 말하는 코드를 어떻게 만들 수 있는지 말입니다.

인터페이스를 입력합니다.

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

당신은 "당신의 버튼"을 만들고, 버튼에서 내려와서, 당신이 필요로 하는 모든 인터페이스에 대한 지원을 추가할 것입니다.

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

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#에서 다중 상속을 사용할 수 없다고 생각하고 질문을 다시 살펴봅니다.

저는 이 페이지에서 "구성"이라는 단어를 검색해 봤는데 한 번도 보지 못했습니다.이 답변은 앞서 언급한 답변 외에도 매우 많은 것입니다.

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

Derek Banas의 훌륭한 "데코레이터 패턴" 튜토리얼 (재미있게도 피자를 예로 사용)은 가치 있는 삽화입니다.

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

인터페이스 = 계약, 느슨한 커플링에 사용됩니다(GRAPH 참조).

API로 도형을 그리는 작업을 하는 경우 DirectX나 그래픽 콜 또는 OpenGL을 사용하고 싶을 수 있습니다.GL을 사용하고 싶을 것입니다.그래서, 저는 당신이 부르는 것으로부터 제 구현을 추상화하는 인터페이스를 만들 것입니다.

공장법이라고 : 공장방사용니다합을식다니▁so사.MyInterface i = MyGraphics.getInstance()여러분은 때문에 은 그면러, 계이있때어문에서 어떤 할 수 알 수.MyInterface그럼 전화해 보세요.i.drawRectangle또는i.drawCube한 라이브러리를 다른 라이브러리와 교환하면 해당 기능이 지원된다는 점을 알아야 합니다.

종속성 주입을 사용하는 경우에는 XML 파일에서 구현을 스왑 아웃할 수 있으므로 이 작업이 더욱 중요합니다.

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

NET에서는 .NET 컬에사다니와 이에서 매우 많이 됩니다. 예를 들어 다음과 같이 사용해야 합니다.List변수를 사용할 수 있으며, 배열 목록인지 연결 목록인지 여부에 관계없이 걱정할 필요할 경우에 따라 다릅니다.

당신이 인터페이스에 코드화하는 한 개발자는 실제 구현을 변경할 수 있고 프로그램의 나머지 부분은 변경되지 않은 상태로 유지됩니다.

전체 인터페이스를 모의 분석할 수 있기 때문에 유닛 테스트 시에도 유용합니다. 따라서 데이터베이스에 갈 필요가 없고 정적 데이터만 반환하는 모의 구현에 사용할 수 있으므로 데이터베이스가 유지보수를 위해 다운되었는지 여부를 걱정하지 않고 방법을 테스트할 수 있습니다.

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

public class Car { ... }

public class Tree { ... }

두 클래스 모두에 굽기 가능한 기능을 추가하려고 합니다.하지만 각 학급마다 불태우는 방법이 있습니다.그래서 당신은 간단하게;

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

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

필요할 때 인터페이스를 얻을 수 있습니다 :) 예를 연구할 수는 있지만, 실제로 인터페이스를 얻으려면 아하! 효과가 필요합니다.

이제 인터페이스가 무엇인지 알았으니, 인터페이스 없이 코드만 입력하십시오.조만간 인터페이스 사용이 가장 자연스러운 문제에 직면하게 될 것입니다.

인터페이스는 실제로 구현 클래스가 따라야 하는 계약이며, 실제로는 제가 알고 있는 거의 모든 설계 패턴의 기반입니다.

당신의 예에서, 인터페이스는 피자 인터페이스를 구현하는 것을 의미하는 ISA Pizza의 모든 것이 구현되도록 보장되기 때문에 만들어집니다.

public void Order();

언급한 코드 다음에는 다음과 같은 것이 있을 수 있습니다.

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

이런 식으로 다형성을 사용하면 개체가 순서()에 반응하는 것이 중요합니다.

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

피자 비유를 확장해서 풀 윙 3 코스 요리를 말해보겠습니다.우리는 여전히 핵을 가지고 있을 것입니다.Prepare()모든 음식 범주에 대한 인터페이스를 제공하지만 코스 선택(채소, 메인, 디저트)과 음식 유형에 대한 다양한 특성(맛있는/달콤한, 채식주의자/비채식주의자, 글루텐 프리 등)에 대한 추상적인 선언도 제공합니다.

이러한 사양을 바탕으로 추상 팩토리 패턴을 구현하여 전체 프로세스를 개념화할 수 있지만, 인터페이스를 사용하여 기반만 구체화할 수 있습니다.다른 모든 것들은 유연해지거나 다형성을 장려할 수 있지만, 다른 클래스들 사이에서 캡슐화를 유지할 수 있습니다.Course것을구현을 것.ICourse인터페이스

시간이 좀 더 있다면, 이에 대한 전체 예를 작성하고 싶거나 누군가가 이를 확장해 줄 수 있습니다. 요약하자면, C# 인터페이스가 이러한 유형의 시스템을 설계하는 데 가장 좋은 도구가 될 것입니다.

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

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

이러한 방식으로 클래스는 임의의 수의 인터페이스에서 동작(정적 메서드)을 "상속"할 수 있습니다.

뭐라고?

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

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: IShapes 그고리로.Mainshapes.Add(new Square());

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

여기에 좋은 답변들이 많지만 조금 다른 관점에서 시도해보고 싶습니다.

객체 지향 설계의 견고한 원칙에 익숙할 수 있습니다.요약:

S - 단일 책임 원칙 O - 개방/폐쇄 원칙 L - 리스코프 대체 원칙 I - 계면 분리 원칙 D - 종속성 반전 원칙

SOLID 원칙을 따르는 것은 깨끗하고 요소가 잘 포함되며 응집력이 있으며 느슨하게 결합된 코드를 생성하는 데 도움이 됩니다.다음과 같은 경우:

"의존성 관리는 모든 규모의 소프트웨어에서 핵심 과제입니다."(Donald Knuth)

의존성 관리에 도움이 되는 모든 것이 큰 승리입니다.인터페이스와 의존성 반전 원리는 코드와 구체적인 클래스의 의존성을 분리하는 데 실제로 도움이 되므로 코드는 구현보다는 행동 측면에서 작성되고 추론될 수 있습니다.이것은 코드를 컴파일 시간이 아닌 런타임에 구성할 수 있는 구성 요소로 분할하는 데 도움이 되며, 코드의 나머지 부분을 변경하지 않고도 해당 구성 요소를 쉽게 연결하고 분리할 수 있음을 의미합니다.

인터페이스는 특히 종속성 반전 원리를 사용하여 코드를 서비스 모음으로 구성하고 각 서비스를 인터페이스로 설명할 수 있습니다.그런 다음 서비스를 생성자 매개 변수로 전달하여 런타임에 클래스에 "주입"할 수 있습니다.이 기술은 단위 테스트를 작성하기 시작하고 테스트 기반 개발을 사용할 경우 매우 중요합니다.해보세요!인터페이스가 코드를 개별적으로 테스트할 수 있는 관리 가능한 청크로 분할하는 데 어떻게 도움이 되는지 빠르게 이해할 수 있습니다.

답이 많네요.최선을 다하는 중.헤헤.

먼저, 예, 여기서 구체적인 기초와 파생된 클래스를 사용할 수 있습니다.이 경우 기본 클래스에서 Prepare 메서드에 대해 비어 있거나 쓸모 없는 구현을 수행해야 합니다. 또한 이 메서드를 만듭니다.virtual파생된 클래스는 자체적으로 이 Prepare 메서드를 재정의합니다.이 경우 기본 클래스에서 준비를 구현해도 소용이 없습니다.

Interface구현이 아닌 계약을 정의해야 했기 때문입니다.

이 .IPizza를 입력하면 준비 기능이 제공합니다.이것은 계약입니다.그것이 어떻게 준비되는지는 구현이며 당신의 전망이 아닙니다.그것은 다양한 사람들의 책임입니다.Pizza실행.interface 는또.abstract추상화, 즉 준비 방법을 만들어야 했기 때문에 여기서는 구체적인 기본 클래스보다 클래스를 선호합니다.구체적인 기본 클래스에는 추상 메서드를 만들 수 없습니다.

이제 여러분은 이렇게 말할 수 있습니다. 추상적인 수업을 사용하면 어떨까요?

를 해야 할% 추상화를 .Interface그러나 구체적인 구현과 함께 추상화가 필요할 때는 다음과 같이 하십시오.abstract클래스. 의미합니다.

예: 모든 피자에 베이스가 있을 것이고 베이스 준비는 동일한 과정이 될 것입니다.하지만, 모든 피자의 종류와 토핑은 다양할 것입니다.이 경우 추상 메서드 Prepare와 구체적인 메서드 PreparePizzaBase를 사용하여 추상 클래스를 만들 수 있습니다.

public abstract class Pizza{
    // concrete method which is common to all pizzas.
    public PizzaBase PreparePizzaBase(){
        // code for pizza base preparation.
    }
    public abstract void Prepare();
}

public class DeluxePizza: Pizza{
    public void Prepare(){
        var base=PreparePizzaBase();
        // prepare deluxe pizza on pizza base.
    }
}

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

정말 좋은 예들이 있습니다.

또 다른 예로, switch 문의 경우에는 작업을 특정 방식으로 수행하기를 원할 때마다 더 이상 유지보수 및 전환할 필요가 없습니다.

당신의 피자 예에서, 만약 피자를 만들고 싶다면, 인터페이스는 당신이 필요로 하는 모든 것입니다. 거기서부터 각각의 피자는 그것의 논리를 처리합니다.

이는 커플링 및 사이클로매틱 복잡성을 줄이는 데 도움이 됩니다.당신은 여전히 논리를 구현해야 하지만 더 넓은 시야에서 추적해야 할 것은 더 적을 것입니다.

각 피자에 대해 해당 피자에 대한 정보를 추적할 수 있습니다.다른 피자들만 알면 되기 때문에 다른 피자들이 무엇을 가지고 있는지는 중요하지 않습니다.

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

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

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

인터페이스는 추상 기본 클래스와 비슷하지만 기본 클래스를 상속하는 개체는 다른 클래스를 상속할 수 없습니다.반대로 개체는 원하는 클래스를 상속하거나 다른 인터페이스를 구현하는 기능에 영향을 주지 않고 인터페이스를 구현할 수 있습니다.

이것의 한 가지 좋은 특징은 (IMHO라는 .net 프레임워크에서 충분히 활용되지 않음) 객체가 할 수 있는 것을 선언적으로 나타낼 수 있게 한다는 것입니다.예를 들어 일부 개체는 인덱스를 통해 검색할 수 있는 데이터 소스 개체를 원하지만(목록을 사용할 경우 가능), 여기에 아무것도 저장할 필요가 없습니다.다른 루틴에는 데이터 저장소 개체가 필요합니다. 여기서 인덱스(Collection과 같이)가 아닌 항목을 저장할 수 있습니다.추가), 그러나 그들은 아무것도 읽을 필요가 없습니다.일부 데이터 유형은 인덱스로 액세스할 수 있지만 쓰기는 허용하지 않습니다. 다른 데이터 유형은 쓰기를 허용하지만 인덱스로 액세스할 수 없습니다.물론 일부는 두 가지 모두를 허용할 것입니다.

다음 사용자가 읽을 경우Index와 Appendable은 관련이 없는 기본 클래스이므로 ReadableBy가 예상되는 항목에 모두 전달될 수 있는 형식을 정의할 수 없습니다.색인 및 추가 가능한 항목.ReadableBy를 사용하여 이 문제를 완화할 수 있습니다.인덱스 또는 추가 가능은 다른 클래스에서 파생됩니다. 파생 클래스는 두 가지 목적 모두에 대해 공용 멤버를 사용할 수 있도록 만들어야 하지만 일부 공용 멤버가 실제로 작동하지 않을 수 있음을 경고합니다.마이크로소프트의 일부 클래스와 인터페이스는 그렇게 하지만, 그것은 꽤 까다롭습니다.더 깨끗한 접근법은 다양한 목적을 위한 인터페이스를 가진 다음 객체가 실제로 할 수 있는 것들을 위한 인터페이스를 구현하도록 하는 것입니다.I 판독 가능한 인터페이스가 있는 경우색인과 다른 인터페이스를 추가할 수 있는 클래스는 자신이 수행할 수 있는 작업에 적합한 인터페이스를 구현할 수 있습니다.

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

O = "클래스는 확장을 위해 열리지만 수정을 위해 닫아야 합니다."

인터페이스의 장점/장점은 추상 클래스보다 유연하다는 것입니다.추상 클래스를 하나만 상속할 수 있지만 여러 인터페이스를 구현할 수 있기 때문에 추상 클래스를 상속하는 시스템에 대한 변경은 여러 곳에서 문제가 됩니다.100개의 위치에서 상속되는 경우 변경하려면 100개 모두를 변경해야 합니다.그러나 인터페이스를 사용하면 새 인터페이스에 새 변경사항을 배치하고 필요한 곳에 해당 인터페이스를 사용할 수 있습니다(SOLID의 인터페이스 시퀀스).또한 인터페이스를 구현하는 곳의 수에도 불구하고 인터페이스 예제의 개체는 메모리에서 한 번만 사용되기 때문에 메모리 사용량이 인터페이스에서 더 적을 것으로 보입니다.

인터페이스는 엄격하게 결합된 추상 클래스와는 다르게 느슨하게 결합된 방식으로 일관성을 유지하는 데 사용됩니다.그것이 또한 일반적으로 계약으로 정의되는 이유입니다.인터페이스를 구현하는 클래스는 인터페이스에 의해 정의된 "규칙/ 구문"을 준수하며 인터페이스 내에는 구체적인 요소가 없습니다.

아래 그림에서 지원하는 예를 들어보겠습니다.

공장에 세 종류의 기계가 있다고 상상해 보세요.직사각형 기계, 삼각형 기계, 다각형 기계.시간은 경쟁력이 있기 때문에 운영자 교육을 간소화해야 합니다.녹색 시작 버튼과 빨간색 중지 버튼을 사용할 수 있도록 기계를 시작 및 중지하는 한 가지 방법으로 교육하고 싶을 뿐입니다.이제 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();

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

다른 관점에서 설명하겠습니다.위에서 보여준 예에 따라 이야기를 만들어 보겠습니다.

프로그램, 기계, 아이머신은 우리 이야기의 배우들입니다.프로그램이 실행을 원하지만 해당 기능이 없으며 기계가 실행 방법을 알고 있습니다.기계와 아이머신은 친한 친구이지만 프로그램은 기계와 대화하는 사이가 아닙니다.그래서 Program과 Imachine은 거래를 했고, Imachine은 Program에 머신(반사경처럼)을 보고 실행하는 방법을 알려주기로 결정했습니다.

그리고 프로그램은 아이머신의 도움으로 실행하는 법을 배웁니다.

인터페이스는 의사소통을 제공하고 느슨하게 결합된 프로젝트를 개발합니다.

PS: 저는 개인적으로 구체적인 수업 방법을 가지고 있습니다.여기서 제 목표는 구체적인 클래스 속성과 방법에 액세스하는 것을 방지하고 인터페이스를 통해 액세스할 수 있는 방법만 남겨둠으로써 느슨하게 결합되는 것입니다.(Soi는 인터페이스의 방법을 명시적으로 정의했습니다.)

언급URL : https://stackoverflow.com/questions/6802573/interfaces-whats-the-point

반응형