使用接口分离读写问题的最佳方法?

最近我一直在意识到(一些人会过度使用)不可变对象的好处,以大大减少我的对象模型中的读写依赖性问题及其产生的条件和副作用,最终使代码更易于管理(一种function性编程式的。

这种做法使我创建了只读对象,这些对象在创建/构建时提供了值,然后只为外部调用者提供公共getter来访问属性。 受保护的内部和私有设置器允许在写入对象模型时保持内部控制。

在通过我的对象模型创建API时创建接口时,我已经开始考虑有关不变性的相同问题。 例如,通过在我的接口上仅提供公共getter,并将其留给实现者来决定setter以及如何处理该方面。

我正在讨论的用于实现的“只读”接口的一个例子是这个有价值的项目(仅用于演示):

public interface IValuableItem { decimal Amount {get;} string Currency {get;} } 

但是我想知道我应该如何提供一个允许写入的伴随接口(如果我应该) ,而不是在同一个接口中组合这些操作,以免“污染”它的不变性。

我想到了以下几个想法,就在我的脑海中。 如果没有提供我认为对每个人的利弊, 你认为最好的方法是什么? 有没有一种行业中常用的编码方法来管理这个概念?

 // companion writer public interface IValuableModifier { decimal Amount {set;} string Currency {set;} } 

要么

 // explicit methods to enforce importance of or deviance in the programming public interface IValuableModifier { void SetAmount(decimal val); void SetCurrency(string cur); } 

要么

 // companion writer that inherits the original interface public interface IValuableModifier : IValuableItem { //... 

要么

 // Let a concrete class choose one and/or the other. class Concrete : IValuableModifer, IValuableItem { //... 

要么

等等…
还有什么可以帮助我灌输我不可改变的编程模型,并保持适度的灵活性,或者至少将问题分开以便更好地控制它?

我想我可能会使用你的想法的变体,如下所示:

 public interface IValuableItem { decimal Amount { get; } string Currency { get; } } public interface IMutableValuable : IValuableItem { new decimal Amount { set; get; } new string Currency { set; get; } } class Item : IMutableValuable { public decimal Amount { get; set; } public string Currency { get; set; } } 

这种方式你的可变接口有完整的getter和setter(我觉得有一个有setter但没有getter的接口是没有意义的),但实现它的任何对象也会有一个你可以使用的接口的不可变版本任何纯function代码。

你应该为ReadableFoo,ImmutableFoo和MutableFoo提供单独的接口。 后两者应该从第一个inheritance。 ReadableFoo应该包含一个“AsImmutable”方法,它将返回一个保证不可变的Foo(一个不可变的实例应该自己返回;一个可变实例应该返回一个包含其数据的新的不可变实例),并且可能是一个“AsNewMutable”成员(这将创建一个包含相同数据的新的可变实例,无论原始是否可变)。

没有类应该实现ImmutableFoo和MutableFoo。

如果你的对象是不可变的(并且你围绕不可变数据的概念设计应用程序)那么对象实际上必须保持不变。

在不可变场景中修改数据的规范方法是创建新对象,所以我建议这样的事情:

 public interface IValuableItem { decimal Amount { get; } string Currency { get; } T CreateCopy(decimal amount, string currency); } public class SomeImmutableObject : IValuableItem { public decimal Amount { get; private set; } public string Currency { get; private set; } public SomeImmutableObject(decimal amount, string currency) { Amount = amount; Currency = currency; } public SomeImmutableObject CreateCopy(decimal amount, string currency) { return new SomeImmutableObject(amount, currency); } } SomeImmutableObject obj = new SomeImmutableObject(123.33m, "GBP"); SomeImmutableObject newObj = obj.CreateCopy(120m, obj.Currency); 

考虑使用构建器模式:构建器对象构造核心对象的不可变实例。 .NET字符串就像这样 – 字符串对象是不可变的,并且有一个StringBuilder类用于有效构造字符串对象。 (string + string + string比使用StringBuilder做同样的效率低得多)

另请注意,构建器对象仅用于构建目标对象 – 构建器不是目标对象的实例/不自己实现目标接口。

值得努力使您的系统在不可变对象上运行,因为不可变性在线程/并发/并行执行方案以及数据缓存/数据版本控制方案中消除了许多令人头疼的问题。

我相信结合你的第3和第4选择是实现可变和不可变类型的更好方法。

 Public interface ImmutableItem { decimal Amount {get;} string Currency {get;} } Public interface MutableItem: ImmutableItem { decimal Amount {set;} string Currency {set;} } class Concrete : ImmutableItem { //Only getters } class Concrete : MutableItem { //Both getters & setters } 

这是干净的,它让具体的类决定想要暴露给外部世界的哪种可变性。