Как работает эта функция «Программирование на интерфейсах»? - PullRequest
4 голосов
/ 11 мая 2009

Мне нравится идея "программирования для интерфейсов" и избегания использования ключевого слова "new".

Тем не менее, что мне делать, когда у меня есть два класса, которые имеют одинаковый интерфейс, но принципиально отличаются в настройке. Не вдаваясь в подробности о моем конкретном коде, у меня есть интерфейс с методом DoStuff. Два класса реализуют этот интерфейс. Один из них очень прост и не требует инициализации. Другой имеет пять различных переменных, которые необходимо настроить. В сочетании они позволяют буквально миллионам способов работы класса при вызове DoStuff.

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

Ответы [ 5 ]

6 голосов
/ 11 мая 2009

Я думаю, вы можете неправильно понимать концепцию программирования для интерфейсов. Вы всегда должны использовать ключевое слово new в объектно-ориентированных языках для создания новых экземпляров объектов. То, что вы программируете на интерфейсы, не снимает это требование.

Программирование на интерфейсе просто означает, что все ваши конкретные классы имеют свое поведение, определенное в интерфейсе, а не в самом конкретном классе. Поэтому, когда вы определяете тип переменной, вы определяете ее как интерфейс вместо конкретного типа.

В вашем случае просто внедрите DoStuff в ваши конкретные классы, поскольку каждый класс нуждается в его реализации (будь то просто или с 10 другими инициализированными объектами и настройкой). Например, если у вас есть интерфейс IInterface и класс SomeClass, который реализует IInterface. Вы можете объявить экземпляр SomeClass так:

IInterface myInstance = new SomeClass();

Это позволяет передавать этот экземпляр другим функциям без необходимости беспокоиться о деталях реализации класса этого экземпляра.

2 голосов
/ 11 мая 2009

Ну, у вас действительно есть 3 варианта. Используйте новый, используйте фабрику или используйте контейнер DI. С DI-контейнером ваши пять переменных, скорее всего, должны быть в конфигурационном файле некоторых сортов.

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

1 голос
/ 11 мая 2009

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

Как это сделать, зависит от того, чего вы хотите достичь, и от семантики этих классов.

Возьмите класс, который вы упомянули с этими полями.

Можно ли прочитать эти поля откуда-нибудь? Файл конфигурации, как пример? Если это так, возможно, все, что вам нужно, это просто конструктор по умолчанию, который инициализирует эти поля из такого файла конфигурации.

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

Возможно, вам стоит взглянуть на контейнер IoC и внедрение зависимостей?

0 голосов
/ 20 августа 2009

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

Вы должны использовать конструктор, который получает InitializationParameter. Оба класса должны получить это. Один с простым интерфейсом, который не нужно ничего получать от него. Другой, который нуждается в параметрах и получит их от него.

Если вас беспокоит инициализация, вы можете использовать factory, просто спросите его о каком-либо интерфейсе, обеспечивающем этот параметр init, и factory создаст, инициализирует и вернет вам объект в соответствии с указанными вами значениями.

Если что-то не понятно - спросите.

0 голосов
/ 11 мая 2009

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

Избегание нового ключевого слова может быть полезным, поскольку оно создает зависимость от реализующего класса. Лучшим решением было бы использовать Dependancy Injection.

например

public interface IDoStuff
{
    void DoStuff();
}

public class DoStuffService
{
    private IDoStuff doer;

    public DoStuffService()
    {
        //Class is now dependant on DoLotsOfStuff
        doer = new DoLotsOfStuff(1,true, "config string");
    }
}

public class DoStuffBetterService
{
    private IDoStuff doer;

    //inject dependancy - no longer dependant on DoLotsOfStuff
    public DoStuffBetterService(IDoStuff doer)
    {
        this.doer = doer;
    }
}

Очевидно, вам все еще нужно создать объект IDoStuff, передаваемый куда-нибудь. Контейнер Inversion of Control (IoC) - хороший инструмент для реализации этого. Вот хороший учебник для Castle Windsor Container , если вы заинтересованы в получении дополнительной информации. (Есть много других контейнеров IoC, я просто использую этот.)

Пример в вашем вопросе был очень абстрактным, поэтому я надеюсь, что этот ответ будет полезным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...