Лучший способ разделить проблемы чтения и записи с помощью интерфейсов?

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

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

при создании интерфейсы при создании 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 { //...

или

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

5 ответов


Я думаю, что могу использовать вариант ваших идей, что-то вроде этого:

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

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


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

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


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

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

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

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

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

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


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

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