Вопрос

Один наткнулся на эту фразу при чтении о рисунках дизайна.

Но я этого не понимаю, может кто-то объяснить это для меня?

Это было полезно?

Решение

Интерфейсы - это просто контракты или подписи, и они ничего не знают о реализациях.

Кодирование против интерфейса означает, что клиентский код всегда содержит интерфейсный объект, который поставляется на заводе. Любой экземпляр, возвращаемый заводом, будет иметь интерфейс типа, который должен был реализовать любой класс кандидата завода. Таким образом, клиентская программа не беспокоит о реализации, и подпись интерфейса определяет, что все операции могут быть сделаны. Это может быть использовано для изменения поведения программы во время выполнения. Это также поможет вам написать гораздо лучшие программы с точки зрения обслуживания.

Вот основной пример для вас.

public enum Language
{
    English, German, Spanish
}

public class SpeakerFactory
{
    public static ISpeaker CreateSpeaker(Language language)
    {
        switch (language)
        {
            case Language.English:
                return new EnglishSpeaker();
            case Language.German:
                return new GermanSpeaker();
            case Language.Spanish:
                return new SpanishSpeaker();
            default:
                throw new ApplicationException("No speaker can speak such language");
        }
    }
}

[STAThread]
static void Main()
{
    //This is your client code.
    ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
    speaker.Speak();
    Console.ReadLine();
}

public interface ISpeaker
{
    void Speak();
}

public class EnglishSpeaker : ISpeaker
{
    public EnglishSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak English.");
    }

    #endregion
}

public class GermanSpeaker : ISpeaker
{
    public GermanSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak German.");
    }

    #endregion
}

public class SpanishSpeaker : ISpeaker
{
    public SpanishSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak Spanish.");
    }

    #endregion
}

alt text

Это просто основной пример, а фактическое объяснение принципа выходит за рамки этого ответа.

РЕДАКТИРОВАТЬ

Я обновил пример выше и добавил абстрактный базовый класс динамика. В этом обновлении я добавил функцию всем пропаркам на «Сайэлло». Все говорящий говорят «Hello World». Так что это общая особенность с аналогичной функцией. Обратитесь к диаграмме классов, и вы обнаружите, что динамик абстрактный класс реализует интерфейс ispeaker и отмечает Speak () как абстрактное, что означает, что каждая реализация докладчика отвечает за реализацию метода Speal, поскольку он варьируется от динамика до динамика. Но весь спикер говорит «привет» единогласно. Таким образом, в абстрактном классе динамика мы определяем способ, который говорит «Hello World», и каждая реализация докладчика выйдет метод Sayhello.

Рассмотрим случай, когда испанский спекулер не может поздороваться, так что в этом случае вы можете переопределить метод SAYHELLO для динамика испанского языка и поднять правильное исключение.

Обратите внимание, что мы не внесли никаких изменений в интерфейс iSpeaker. И клиентский код и сантехника также остаются неизменными без изменений. И это то, что мы достигаем Программирование к интерфейсу.

И мы могли бы достичь этого поведения, просто добавив аннотацию базового аннотация класса и некоторую незначительную модификацию в каждой реализации, что оставив исходную программу без изменений. Это желаемая особенность любого приложения, и это позволяет вашему приложению легко поддерживаться.

public enum Language
{
    English, German, Spanish
}

public class SpeakerFactory
{
    public static ISpeaker CreateSpeaker(Language language)
    {
        switch (language)
        {
            case Language.English:
                return new EnglishSpeaker();
            case Language.German:
                return new GermanSpeaker();
            case Language.Spanish:
                return new SpanishSpeaker();
            default:
                throw new ApplicationException("No speaker can speak such language");
        }
    }
}

class Program
{
    [STAThread]
    static void Main()
    {
        //This is your client code.
        ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
        speaker.Speak();
        Console.ReadLine();
    }
}

public interface ISpeaker
{
    void Speak();
}

public abstract class Speaker : ISpeaker
{

    #region ISpeaker Members

    public abstract void Speak();

    public virtual void SayHello()
    {
        Console.WriteLine("Hello world.");
    }

    #endregion
}

public class EnglishSpeaker : Speaker
{
    public EnglishSpeaker() { }

    #region ISpeaker Members

    public override void Speak()
    {
        this.SayHello();
        Console.WriteLine("I speak English.");
    }

    #endregion
}

public class GermanSpeaker : Speaker
{
    public GermanSpeaker() { }

    #region ISpeaker Members

    public override void Speak()
    {
        Console.WriteLine("I speak German.");
        this.SayHello();
    }

    #endregion
}

public class SpanishSpeaker : Speaker
{
    public SpanishSpeaker() { }

    #region ISpeaker Members

    public override void Speak()
    {
        Console.WriteLine("I speak Spanish.");
    }

    public override void SayHello()
    {
        throw new ApplicationException("I cannot say Hello World.");
    }

    #endregion
}

alt text

Другие советы

Подумайте о интерфейсе как договор между объектом и его клиентами. То есть интерфейс определяет то, что может сделать объект, и подписи для доступа к этим вещам.

Реализации являются фактическим поведением. Скажем, например, у вас есть метод сортировки (). Вы можете реализовать Quicksort или Mergeort. Это должно не иметь значения для клиентского кода, вызывающего сорт, пока интерфейс не меняется.

Библиотеки, такие как Java API, а также .NET Framework, используют интерфейсы, поскольку миллионы программистов используют предоставленные объекты. Создатели этих библиотек должны быть очень осторожны, что они не изменяют интерфейс на классы в этих библиотеках, потому что это повлияет на все программисты, используя библиотеку. С другой стороны, они могут изменить реализацию столько, сколько им нравится.

Если, как программист, вы код против реализации, то как только он меняет ваш код, останавливает работу. Так что думайте о преимуществах интерфейса этого способа:

  1. Он скрывает то, что вам не нужно знать, что делает объект проще в использовании.
  2. Это обеспечивает договор о том, как объект будет вести себя, чтобы вы могли зависеть от этого

Это означает, что вы должны попытаться написать свой код, чтобы он использовал абстракцию (абстрактный класс или интерфейс) вместо реализации напрямую.

Обычно реализация вводится в ваш код через конструктор или вызов метода. Итак, ваш код знает о интерфейсе или абстрактном классе и может назвать все, что определяется по этому договору. Используется фактический объект (реализация интерфейса / абстрактного класса), вызовы работают на объекте.

Это подмножество Liskov Substitution Principle (Lsp), л SOLID принципы.

Пример в .NET будет кодировать с IList вместо List или Dictionary, чтобы вы могли использовать любой класс, который реализует IList взаимозаменяемо в вашем коде:

// myList can be _any_ object that implements IList
public int GetListCount(IList myList)
{
    // Do anything that IList supports
    return myList.Count();
}

Другим примером из библиотеки базового класса (BCL) является ProviderBase Абстрактный класс - это обеспечивает некоторую инфраструктуру, и, как главное означает, что все реализации провайдера могут быть использованы взаимозаменяемо, если вы помимо кода.

Если бы вы написали класс автомобиля в эпоху сгорания-автомобиля, то есть большой шанс, что вы бы внедрили Oilchange () как часть этого класса. Но, когда введены электромобили, вы будете в беде, поскольку для этих автомобилей не существует никаких нефтяных избавок, и никакой реализации.

Раствор задачи состоит в том, чтобы иметь интерфейс управления () в классе автомобиля и скрыть детали внутри подходящей реализации. Каждый тип автомобиля предоставит свою собственную реализацию для работы оказания (). Как владелец автомобиля, все, с которым вы должны иметь дело, является выработка оказания () и не беспокоиться о адаптации, когда есть изменение.

class MaintenanceSpecialist {
    public:
        virtual int performMaintenance() = 0;
};

class CombustionEnginedMaintenance : public MaintenanceSpecialist {
    int performMaintenance() { 
        printf("combustionEnginedMaintenance: We specialize in maintenance of Combustion engines \n");
        return 0;
    }
};

class ElectricMaintenance : public MaintenanceSpecialist {
    int performMaintenance() {
        printf("electricMaintenance: We specialize in maintenance of Electric Cars \n");
        return 0;
    }
};

class Car {
    public:
        MaintenanceSpecialist *mSpecialist;
        virtual int maintenance() {
            printf("Just wash the car \n");
            return 0;
        };
};

class GasolineCar : public Car {
    public: 
        GasolineCar() {
        mSpecialist = new CombustionEnginedMaintenance();
        }
        int maintenance() {
        mSpecialist->performMaintenance();
        return 0;
        }
};

class ElectricCar : public Car {
    public: 
        ElectricCar() {
             mSpecialist = new ElectricMaintenance();
        }

        int maintenance(){
            mSpecialist->performMaintenance();
            return 0;
        }
};

int _tmain(int argc, _TCHAR* argv[]) {

    Car *myCar; 

    myCar = new GasolineCar();
    myCar->maintenance(); /* I dont know what is involved in maintenance. But, I do know the maintenance has to be performed */


    myCar = new ElectricCar(); 
    myCar->maintenance(); 

    return 0;
}

Дополнительное объяснение: Вы являетесь владельцем автомобиля, владеющего несколькими автомобилями. Вы выделяете услугу, которую вы хотите на аутсорсинг. В нашем случае мы хотим передать техническое обслуживание всех автомобилей.

  1. Вы определяете договор (интерфейс), который добрутся добра для всех ваших автомобилей и поставщиков услуг.
  2. Поставщики услуг выходят с механизмом для предоставления услуг.
  3. Вы не хотите беспокоиться об ассоциировании типа автомобиля с поставщиком услуг. Вы просто указываете, когда хотите запланировать обслуживание и вызовов его. Соответствующая сервисная компания должна перейти и выполнять рабочую силу.

    Альтернативный подход.

  4. Вы определяете работу (можете быть новым интерфейсным интерфейсом), который хорошо держит для всех ваших автомобилей.
  5. Ты выйти с механизмом, чтобы обеспечить услугу. По сути, вы собираетесь предоставить реализацию.
  6. Вы вызываете работу и делаете это сами. Здесь вы собираетесь выполнить работу соответствующих работ по техническому обслуживанию.

    Какой недостаток 2-го подхода? Вы не можете быть экспертом по нахождению наилучшего способа для обслуживания. Ваша работа - водить машину и наслаждаться этим. Не быть в бизнесе поддержания его.

    Какая он нижняя часть первого подхода? Есть накладные расходы на поиск компании и т. Д. Если вы не представляете собой автомобильную компанию, она не стоит того, чтобы усилия.

Это утверждение о сцеплении. Одна потенциальная причина использования объектно-ориентированного программирования - повторное использование. Таким образом, например, вы можете разделить свой алгоритм среди двух совместных объектов A и B. Это может быть полезно для более позднего создания другого алгоритма, который может повторно использовать один или иной из двух объектов. Однако, когда эти объекты обмениваются (отправляют сообщения - методы вызова), они создают зависимости друг от друга. Но если вы хотите использовать один без другого, вам необходимо указать, что должно сделать какой-то другой объект C сделать для объекта A, если мы заменим B. Эти описания называются интерфейсами. Это позволяет объекту A взаимодействовать без изменения различного объекта, полагаясь на интерфейс. Упомянутое заявление говорит, что если вы планируете повторно использовать некоторую часть алгоритма (или в целом программы), вы должны создавать интерфейсы и полагаться на них, поэтому вы можете изменить бетонную реализацию в любое время без изменения других объектов, если вы используете объявленный интерфейс.

Как сказали, это означает, что ваш вызов должен знать только об абстрактном родиве, а не фактическим классом реализации, который будет выполнять работу.

Что помогает понять, что это то, почему вы всегда должны программировать интерфейс. Есть много причин, но два из самых простых, чтобы объяснить

1) тестирование.

Допустим, у меня есть весь код базы данных в одном классе. Если моя программа знает о конкретном классе, я могу только проверить свой код, действительно бегая его против этого класса. Я использую -> означает «переговоры».

WorkClass -> DalClass, однако, давайте добавим интерфейс в смесь.

РабочийКласс -> Идал -> Далкласс.

Таким образом, DalClass реализует Idal Interface, а класс рабочего звонит только через это.

Теперь, если мы хотим написать тесты для кода, мы можем вместо этого сделать простой класс, который просто действует как база данных.

Работница -> Идал -> Ifackaceal.

2) повторное использование

Следуя приведенным выше примеру, скажем, мы хотим перейти с SQL Server (который использует наш бетон DalClass) в Monogodb. Это примет серьезную работу, но не только если мы запрограммировали на интерфейс. В этом случае мы просто пишем новый класс БД, а также измените (через завод)

РабочийКласс -> Идал -> Dalclass

к

Работник -> Идал -> Монгодбкласс

Интерфейсы описывают возможности. При написании императивного кода, поговорим о возможностях, которые вы используете, а не конкретные типы или классы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top