Лучший способ разделить проблемы чтения и записи с помощью интерфейсов?
в последнее время я осознал преимущество (некоторые утверждают, что злоупотребляют) неизменяемыми объектами, чтобы резко сократить проблемы зависимости от чтения и записи в моей объектной модели и их результирующие условия и побочные эффекты, чтобы в конечном итоге упростить управление кодом (вид функционального программирования).
эта практика привела меня к созданию объектов только для чтения, которые предоставляются значения во время создания/строительства, а затем сделать доступными только общедоступные геттеры для внешних вызывающие для доступа к свойствам. Защищенные, внутренние и частные сеттеры позволяют поддерживать внутренний контроль над записью в объектную модель.
при создании интерфейсы при создании 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
}
это чисто, и это позволяет конкретным классам решать, какой вид изменчивости требуется подвергнуть внешнему миру.