【问题标题】:Is using var + basic dependency factory more loosely coupled than C# interfaces?使用 var + 基本依赖工厂是否比 C# 接口更松散耦合?
【发布时间】:2012-09-19 21:06:19
【问题描述】:

这是一个通用的设计问题。我们经常使用接口来解耦组件,写入接口而不是实现等。有时接口使用基本的注入技术,例如,

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

这样我仍然只需要更改一次工厂,只要它返回的任何实例支持需要使用的方法,它就可以工作。然而,优点是生产和消费对象甚至不需要知道任何显式接口,不存在。它还可以干净地支持具有多个方法的接口(防止臃肿的接口声明)。

一个明显的缺点是,每次您想从“接口”使用方法时,工厂可能必须重新实例化该类,除非缓存了单个实例(如上)或使用了一些记忆技术。

这种方法的优缺点?这是一种常见的做法吗?

【问题讨论】:

  • 这不仅仅是关于 var 关键字,而是它与工厂结合使用的方式。
  • // shorthand way, could also omit var, too -- 怎么样?
  • IMyInterfaceFactory.GetInstance().DoSomething();
  • “被消费,它会工作”,你的意思是:“它会编译”,对吧?我的印象是vardynamic 在这里混淆了。
  • 是的,我的意思是它会编译,好点。但这甚至适用于传统的 C# 接口——它只是确保只要类型实现(注意:语法上)它们继承的接口的方法,事情就会编译。我已经从标题中删除了动态以避免这种混淆。

标签: c# oop dynamic interface var


【解决方案1】:

var 关键字没有任何动态或松散性。它在编译时触发静态类型推断。

您的第二段代码的行为与

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

工厂函数仍然是强类型的。事实上,通过移除接口,您可以使消费者代码更加紧密地耦合。

【讨论】:

  • 你知道你可以把 MyInterfaceInstance 改成任何其他类型,只要它支持 DoSomething(),对吧?这就是重点,重点不在于工厂类型松散。
【解决方案2】:

Var 关键字在技术上仍然是强类型的,因此您的代码确实知道它是什么类/接口。如果您打算将其转储到一个对象中,那么我们是说您的其余代码不知道该工厂的内容是什么。我不建议这样做,因为这会导致您强制转换该对象以利用其中的任何内容。

我不确定您要使用“防止臃肿的接口声明”去哪里,但您也可以通过扩展基类或抽象类来实现多态性。这样一来,子类之间的任何通用代码都可以单独保留,并且每个子类的任何特定代码(方法或属性)都可以被覆盖。

如果您希望一起更改接口,则需要在接口中实现一个接口,see this post。所以你基本上只有接口 A 只有方法 DoStuff() 和其他从这个接口继承的接口可以像你描述的那样多态地使用。

interface A
{

DoStuff();
}

interface B : A
{
DoSomethingElse();
}

class C : B
{

DoStuff(){}
DoSomethingElse(){}
}

顺便说一句,您上面的“单个实例缓存”代码接近于所谓的单例pattern

【讨论】:

  • 你必须明白这是一个语法/语义的事情——使用 'var' 意味着消费类不需要知道支持 DoSomething() 的类型的名称——是的他们会推断类型,但没关系,只要该类型具有 DoSomething() 方法。您可以将该类移动到工厂提供不同实现的任何地方,或者如果您想更改所有使用它的类的行为,则只需更改该工厂。
  • @SeanThoman Var 不允许消费类不知道实例的名称。当你声明 var 时,你必须用一个名字来实例化一些东西,所以你给它一个名字,即使它是其他地方的一个实例,你仍然给它一个名字。此外,您仍然需要一些基本类型(接口/类)来实现多态性。如果您想摆脱所有代码,只需调用 FactoryClass.GetInstance().SomeMethod();。相反,我认为您正在寻找一个从工厂实例化对象的包装类,因此您调用 Wrapper.DoSomething() 就是这样。
  • @MortalitySX,多态性是一个单独的问题,但是是的,你是对的,我可以为此使用接口或基类。但这篇文章的重点还不是研究多态性。在这种情况下,消费者知道的唯一名称是“IMyInterfaceFactory.GetInstance()”,这是一个静态方法名称,而不是类型名称。如果我想在这里使用多态性,我会选择接口。但是使用 var 仍然可以为消费者带来好处,甚至不必知道该接口的名称。
  • 唯一减轻的知识要求是开发人员的知识要求 - 编译器只需将var 替换为推断的类型名称。从不同开发人员编写的代码的角度来看,这将显式合约(接口类型)替换为隐式合约(类型必须公开方法DoSomething,否则我的代码会崩溃)。从类相互引用的角度来看,使用var 与使用MyInterfaceInstance 没有区别。
  • @DanJ,没错,这是开发人员的知识,但在您想将项目的一部分用于另一个项目或彻底改变程序集的实现时,它也会发挥重要作用一个项目正在引用。对我来说主要的缺点是当你使用 var 时,你从 GetInstance() 检索到的实例的引用不能保存在任何地方(不在堆上),因为 var 只存在于函数范围内。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-15
  • 2013-07-29
  • 2012-02-18
  • 2014-05-29
  • 1970-01-01
  • 2020-10-26
  • 1970-01-01
相关资源
最近更新 更多