Лучший способ разделить проблемы чтения и записи с помощью интерфейсов? - PullRequest
8 голосов
/ 30 ноября 2010

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

Эта практика привела меня к созданию объектов, доступных только для чтения, которым предоставляются значения во время создания / создания, а затем к доступу только для публичных получателей для внешних вызывающих абонентов.получить доступ к свойствам с.Защищенные, внутренние и частные сеттеры позволяют поддерживать внутренний контроль над записью в объектную модель.

При создании интерфейсов при создании API поверх моей объектной модели я начал рассматривать те же проблемы с неизменяемостью.Например, предоставляя только открытые методы получения на моих интерфейсах, и предоставляя разработчикам самим решать, какие методы установки и как обрабатывать этот аспект.

Пример интерфейса «только для чтения» для реализации, который я 'Я говорю о том, является ли это ценным предметом (просто для демонстрации):

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 { //...

или

и т. Д. *
Что еще может помочь мне наполнить написание моей неизменной модели программирования и сохранить ее умеренно гибкой или, по крайней мере, разделить проблемы для лучшего контроля над ней?

Ответы [ 5 ]

10 голосов
/ 30 ноября 2010

Я думаю, я мог бы использовать вариант ваших идей, примерно такой:

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

Таким образом, ваш изменяемый интерфейс имеет полные методы получения и установки (я не думаю, что имеет смысл иметь интерфейс, который имеет методы установки, но не имеет методов получения), но любой объект, который его реализует, также будет иметь неизменную версию интерфейса Вы можете использовать для любого чисто функционального кода.

4 голосов
/ 02 декабря 2010

У вас должны быть отдельные интерфейсы для ReadableFoo, ImmutableFoo и MutableFoo. Последние два должны наследоваться от первого. ReadableFoo должен содержать метод «AsImmutable», который будет возвращать Foo, который гарантированно будет неизменным (неизменяемый экземпляр должен возвращать себя; изменяемый экземпляр должен возвращать новый неизменяемый экземпляр, который содержит его данные) и, возможно, член «AsNewMutable» который создаст новый изменяемый экземпляр, содержащий те же данные, независимо от того, был ли оригинал изменяемым или нет).

Ни один класс не должен реализовывать как ImmutableFoo, так и MutableFoo.

3 голосов
/ 30 ноября 2010

Если ваши объекты должны быть неизменяемыми (и вы разрабатываете свое приложение на основе концепции неизменяемых данных), тогда объекты действительно ДОЛЖНЫ оставаться неизменными.

Канонический метод изменения данных в неизменяемых сценариях состоит в создании новых объектовтак что я бы предложил что-то вроде этого:

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);
2 голосов
/ 30 ноября 2010

Рассмотрите возможность использования шаблона компоновщика: объекты компоновщика создают неизменяемые экземпляры базового объекта. Строки .NET похожи на это - строковый объект является неизменным, и есть класс StringBuilder для эффективного построения строковых объектов. (строка + строка + строка гораздо менее эффективна, чем использование StringBuilder для того же)

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

Стоит усилий, чтобы ваша система работала на неизменяемых объектах, так как неизменность смывает много головных болей в сценариях многопоточности / параллелизма / параллельного выполнения, а также в сценариях кэширования данных / управления версиями.

1 голос
/ 30 ноября 2010

Я полагаю, что объединение вашего 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
}

Это чисто и позволяет конкретным классам решать, какой тип изменчивости требуется раскрыть.во внешний мир.

...