【问题标题】:What's the best way expose a mutable interface over an immutable one?在不可变接口上公开可变接口的最佳方法是什么?
【发布时间】:2011-05-04 16:08:39
【问题描述】:

我想知道 C# 中关于可变/不可变接口的最佳实践是什么。

我喜欢只处理接口而不是真实对象;删除依赖项并允许更轻松的测试。

我通常还公开只读接口,从而降低错误。 但是,当我需要更改对象实例的内容时,这会在实际代码中产生问题。

这就是我想要做的事情

public interface ISomething
{
    string Name { get; }   
}

public interface IMutableSomething : ISomething
{
    string Name { get; set; }   
}

...

public class ConsumerClass
{
   //Note that I'm working against the interface, not the implementation
   public void DoSomethingOnName(ISomething o)
   {
       var mutableO = (IMutableSomething) o;
       mutableO.Name = "blah";
   }
}

以这种方式工作让我可以轻松测试 ConsumerClass 并打破 ISomething 与其实现之间的任何依赖关系

我知道我可以将接口转换为实现,但这会引入对实际实现的依赖。

我可以做类似下面的事情,但我觉得它丑陋和烦人

public interface IMutableSomething : ISomething
{
    void SetName(string newName)   
}

or

public interface IMutableSomething // No inheritance, implementation impl. 2 interfaces
{
    string Name { get; set; }
}

谢谢,

埃里克 G.

【问题讨论】:

  • 这对我来说似乎有点好笑 - 你正在传递具有特定接口的东西(我将其解读为“正常工作的最低要求”),然后将其转换为具有进一步要求的对象就可以了(现在必须允许设置)。这有点像设置一个球门柱,然后当有人到达它时说,哦,是的,顺便说一句,这不是球门柱,它只是一个半路标记
  • 另外,为了接口而接口并不是真的那么有用。

标签: c# inheritance interface properties readonly


【解决方案1】:

我认为你的接口很好,但在你的消费者代码中应该是这样的:

public class ConsumerClass{   
  // Just take IMutableSomething
  public void DoSomethingOnName(IMutableSomething o)   {       
    o.Name = "blah"; 
  }
}

方法调用是一个契约,正如其他人所说,您需要指定您的ConsumerClass 可以实际使用的最通用类型。您可能想了解 Liskov 替换原则:http://en.wikipedia.org/wiki/Liskov_substitution_principle

在这种情况下,虽然 IMutableSomething 可以替代 ISomething,但反之则不成立。

【讨论】:

  • 我想要做的是类似于var something = Repository.Get(...) 的东西,它会返回一个 ISomething 然后调用 ConsumerClass.DoSomething(someThing) 而不必在调用之前强制转换为 IMutableSomething ...这样,我不能不小心改了名字
  • 这不是一个好主意。如果你需要一个 IMutableSomething,你应该让你的存储库能够返回一些 IMutableSomething。您尝试执行的操作只会导致用户出现运行时异常。如果您不想无意中更改 ISomething,请在任何地方获取 ISomething,除非您确实需要更改名称。
  • 感谢您的意见。这基本上就是我想要做的事情,而不必在调用之前强制转换参数。
【解决方案2】:

这并不是真正正确使用界面;接口使您不必关心实现是什么,您只需使用定义的属性和方法。如果您需要“设置”具有仅获取接口的东西,则不应将该接口作为参数传递。

在这种情况下,如果您必须使用接口,请在您的接口上定义 set 方法(或以不同的方式实现属性)

public interface ISomething
{
    string Name { get; set;}
    void SetName(string newValue);
}

// Choose one of these methods to implement; both is overkill
public class SomethingElse : ISomething
{
     protected string _internalThing = string.Empty;

     public string Name
     {
         get { return _internalThing; }
         set { throw new InvalidOperationException(); }
     }

     public void SetName(string newValue)
     {
         throw new InvalidOperationException();
     }
}

然后简单地让不可变接口对值不做任何事情(或抛出异常)。

【讨论】:

  • 我不确定你建议的代码的目的是什么;有一个在调用时总是抛出的 setter 似乎是糟糕的设计,只是有一个 Set 方法来进行设置无论如何。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-25
  • 2018-06-20
  • 1970-01-01
相关资源
最近更新 更多