Является ли использование фабрики зависимостей var + basic более слабо связанной, чем интерфейсы С#?

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

interface IMyInterface
{
    void DoSomething();
}

static class IMyInterfaceFactory
{
    public static IMyInterface GetInstance()
    {
        return new MyInterfaceInstance();
    }
}

class IMyInterfaceConsumer
{
    IMyInterface mInterface;

    public IMyInterfaceConsumer()
    {
        this.mInterface = IMyInterfaceFactory.GetInstance();
    }

    public void UseTheInterface()
    {
        this.mInterface.DoSomething();
    }
}

Мой вопрос касается использования ключевого слова var вместо этого. Даже не используя настоящий интерфейс C #, но все же создавая «интерфейс» в смысле дизайна,

static class IMyInterfaceFactory
{
    // of course, this doesnt need to be a single instance
    static MyInterfaceInstance mSingleInstance; 

    // no longer programming to the interface, just returning the instance
    public static MyInterfaceInstance GetInstance()
    {
        // null coalesce
        return mSingleInstance ?? (mSingleInstance = new MyInterfaceInstance());
    }
}

class IMyInterfaceConsumer
{
    public void UseTheInterface()
    {
        // shorthand way, could also omit var, too 
        var myInterface = IMyInterfaceFactory.GetInstance();
        myInterface.DoSomething();
    }
}

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

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

Плюсы/минусы этого подхода? Это обычная практика?


person Sean Thoman    schedule 19.09.2012    source источник
comment
Дело не только в ключевом слове var, но и в том, как оно используется вместе с factory.   -  person Sean Thoman    schedule 20.09.2012
comment
// shorthand way, could also omit var, too -- как?   -  person Austin Salonen    schedule 20.09.2012
comment
IMyInterfaceFactory.GetInstance().DoSomething();   -  person Sean Thoman    schedule 20.09.2012
comment
Под потреблением он будет работать, вы имеете в виду: он будет компилироваться, верно? У меня сложилось впечатление, что здесь есть путаница между var и dynamic.   -  person Austin Salonen    schedule 20.09.2012
comment
Да, я имею в виду, что он скомпилируется, хороший момент. Но это верно даже для традиционного интерфейса C# — он просто гарантирует, что все будет компилироваться, пока типы реализуют (примечание: синтаксически) методы интерфейса, которые они наследуют. Я удалил динамическое из названия, чтобы избежать путаницы.   -  person Sean Thoman    schedule 20.09.2012


Ответы (2)


Ключевое слово Var по-прежнему технически строго типизировано, поэтому ваш код знает, что это за класс/интерфейс. Если вы планировали сбросить его в объект, то мы говорим, что остальная часть вашего кода понятия не имеет, что выходит из этой фабрики. Я бы не советовал этого, поскольку это заставляет вас использовать этот объект, чтобы использовать что-либо в нем.

Я не уверен, куда вы пытаетесь пойти с «предотвратить раздутые объявления интерфейса», но вы также можете сделать полиморфизм, расширив базовый класс или абстрактный класс. Это сделало бы так, что любой код, общий для дочерних классов, можно было бы оставить в покое, а любой конкретный код (методы или свойства) для каждого дочернего класса можно было бы переопределить.

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

interface A
{

DoStuff();
}

interface B : A
{
DoSomethingElse();
}

class C : B
{

DoStuff(){}
DoSomethingElse(){}
}

Кстати, приведенный выше код с «кэшированием одного экземпляра» близок к тому, что называется синглтоном шаблон.

person iMortalitySX    schedule 19.09.2012
comment
Вы должны понимать, что это вопрос синтаксиса/семантики — использование «var» означает, что потребляющим классам не нужно знать имя типа, который поддерживает DoSomething() — да, они выведут тип, но он не имеет значения, пока этот тип имеет метод DoSomething(). Вы можете переместить этот класс в любое место, где фабрика предоставляет другую реализацию, или просто изменить фабрику, если вы хотите изменить поведение всех потребляющих ее классов. - person Sean Thoman; 20.09.2012
comment
@SeanThoman Var не позволяет потребляющим классам не знать имя экземпляра. Когда вы объявляете var, вы должны создавать экземпляр чего-то с именем, поэтому вы даете ему имя, даже если это экземпляр где-то еще, вы все равно даете ему имя. Кроме того, вам все равно понадобится какой-то базовый тип (интерфейс/класс) для полиморфизма. Если вы хотите избавиться от всего своего кода, просто вызовите FactoryClass.GetInstance().SomeMethod();. Вместо этого я думаю, что вы ищете класс-оболочку, который создает экземпляры объектов из фабрик, поэтому вы вызываете Wrapper.DoSomething() и все. - person iMortalitySX; 20.09.2012
comment
@MortalitySX, полиморфизм - это отдельная проблема, но да, вы правы, я мог бы использовать для этого интерфейс или базовый класс. Но смысл этого поста еще не в том, чтобы посмотреть на полиморфизм. В этом случае единственное имя, о котором знает потребитель, — это IMyInterfaceFactory.GetInstance(), и это имя статического метода, а не имя типа. Если бы я хотел использовать здесь полиморфизм, я бы выбрал интерфейс. Но использование var по-прежнему дает преимущество потребителю, которому даже не нужно знать имя этого интерфейса. - person Sean Thoman; 20.09.2012
comment
Единственное облегченное требование к знаниям - это требование к разработчику - компилятор просто заменит var именем предполагаемого типа. С точки зрения кода, написанного разными разработчиками, это заменяет явный контракт (тип интерфейса) неявным (тип должен предоставлять метод DoSomething, иначе мой код выйдет из строя). С точки зрения классов, ссылающихся друг на друга, использование var неотличимо от использования MyInterfaceInstance. - person Dan J; 20.09.2012
comment
@DanJ, ​​это правильно, это знания разработчика, но они также будут играть важную роль в любое время, когда вы хотите выпотрошить часть проекта для другого проекта или радикально изменить реализацию сборки, на которую ссылается проект. Основным недостатком для меня является то, что когда вы используете var, ссылка на экземпляр, который вы извлекаете из GetInstance(), не может храниться нигде (не в куче), поскольку var живет только в области действия функции. - person Sean Thoman; 20.09.2012

В ключевом слове var нет ничего динамичного или свободного. Он запускает определение статического типа во время компиляции.

Ваш второй фрагмент кода ведет себя идентично

public void UseTheInterface()
{
    // shorthand way, could also omit var, too 
    MyInterfaceInstance myInterface = IMyInterfaceFactory.GetInstance();
    myInterface.DoSomething();
}

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

person Ben Voigt    schedule 19.09.2012
comment
Вы понимаете, что можете изменить MyInterfaceInstance на любой другой тип, если он поддерживает DoSomething(), верно? В том-то и дело, что дело не в том, чтобы фабрика была слабо типизирована. - person Sean Thoman; 20.09.2012