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

发布于 2024-10-05 06:42:13 字数 1368 浏览 4 评论 0原文

最近,我意识到(有些人认为过度使用)不可变对象的好处是可以大大减少对象模型中的读写依赖问题及其产生的条件和副作用,最终使代码更易于管理(类似函数式编程)。

这种做法使我创建了只读对象,这些对象在创建/构造时提供值,然后仅提供公共 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 { //...

等等...
还有什么可以帮助我在原本不可变的编程模型上进行写作并保持其适度的灵活性或至少分离关注点以更好地控制它?

Lately I've been realizing the benefit of (some would argue overuse of) immutable objects to cut down dramatically on read-write dependency issues in my object model and their resulting conditions and side-effects, to ultimately make the code simpler to manage (kind of functional-programming-esque).

This practice has led me to create read-only objects that are provided values at creation/construction time and then to make available only public getters for external callers to access the properties with. Protected, internal and private setters allow internal control to be maintained over writing to the object model.

When creating interfaces while making an API over my object model, I've started considering the same issues about immutability. For example, by providing only public getters on my interfaces, and leaving it up to implementors to decide upon setters and how to handle that aspect.

An example of a "read-only" interface for implementation that I'm talking about is this Valuable Item (just for demonstration):

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

However I got to wondering how I should provide a companion interface that allows for writing (and if I should), and not combine those operations within the same interface as to not "taint" its immutability.

The following ideas have come to mind, just off the top of my head. Without providing what I think are pros and cons to each, what do you think the best approach is? Is there a coding methodology common in the industry for managing this concept?

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

or

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

or

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

or

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

or

etc...
What else can help me imbue writing on my otherwise immutable programming model and keep it moderately flexible or at least to separate the concerns for better control over it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

何处潇湘 2024-10-12 06:42:13

我想我可能会使用你的想法的变体,像这样:

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的接口是没有意义的),但是任何对象实现它还将有一个不可变版本的接口,您可以将其用于任何纯功能代码。

I think I might use a variant of your ideas, something like this:

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

This way your mutable interface has full getters and setters (I don't think it makes sense to have an interface that has setters but no getters), but any object that implements it will also have an immutable version of the interface that you can use for any pure-functional code.

烈酒灼喉 2024-10-12 06:42:13

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

任何类都不应该同时实现 ImmutableFoo 和 MutableFoo。

You should have separate interfaces for ReadableFoo, ImmutableFoo, and MutableFoo. The latter two should inherit from the first. ReadableFoo should contain an "AsImmutable" method which will return a Foo that is guaranteed to be immutable (a immutable instance should return itself; a mutable instances should return a new immutable instance which contains its data), and probably an "AsNewMutable" member (which will create a new mutable instance containing the same data, whether the original was mutable or not).

No class should implement both ImmutableFoo and MutableFoo.

北方的韩爷 2024-10-12 06:42:13

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

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

public interface IValuableItem<T>
{
    decimal Amount { get; }
    string Currency { get; }
    T CreateCopy(decimal amount, string currency);
}

public class SomeImmutableObject : IValuableItem<SomeImmutableObject>
{
    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);

If your objects are to be immutable (and you design your application around the concept of immutable data) then objects really MUST remain immutable.

The canonical method for modifying data in immutable scenarios is to create new objects, so I would suggest something like this:

public interface IValuableItem<T>
{
    decimal Amount { get; }
    string Currency { get; }
    T CreateCopy(decimal amount, string currency);
}

public class SomeImmutableObject : IValuableItem<SomeImmutableObject>
{
    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);
岁月打碎记忆 2024-10-12 06:42:13

考虑使用构建器模式:构建器对象构造核心对象的不可变实例。 .NET 字符串是这样的——字符串对象是不可变的,并且有一个 StringBuilder 类用于高效构造字符串对象。 (字符串 + 字符串 + 字符串比使用 StringBuilder 执行相同操作的效率要低得多)

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

让您的系统在不可变对象上运行是值得的,因为不变性消除了线程/并发/并行执行场景以及数据缓存/数据版本控制场景中的许多麻烦。

Consider using a builder pattern: Builder objects construct immutable instances of the core object. .NET Strings are like this - the string object is immutable, and there is a StringBuilder class for efficient construction of string objects. (string + string + string is much less efficient than using a StringBuilder to do the same)

Note also that builder objects exist solely for building the target object - builders are not instances of the target object / do not implement the target interface themselves.

It's worth the effort to make your system run on immutable objects, as immutability washes away a lot of headaches in threading / concurrency / parallel execution scenarios, as well as data caching / data versioning scenarios.

隱形的亼 2024-10-12 06:42:13

我相信结合您的第三个和第四个选择是实现可变和可变的更好方法。不可变类型。

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
}

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

I believe combining your 3rd and 4th choice is a better way to implement mutable & immutable types.

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
}

This is clean and it let the concrete classes to decide which kind of mutability is wanted to expose to outer world.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文