Если безопасность не является проблемой, является ли неизменный интерфейс хорошим шаблоном? - PullRequest
2 голосов
/ 24 сентября 2011

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

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

В таком контексте существуютв некоторых контекстах нужен читаемый объект DrawnFancyText, но ему все равно, является ли он изменяемым или нет, но есть другие, где нужен неизменный производный от DrawnText или DrawnFancyText, но это не важно.Достижение первого потребует, чтобы EditableDrawnFancyText и ImmutableDrawnFancyText имели общую базу;для достижения последнего потребуется, чтобы ImmutableDrawnText и ImmutableDrawnFancyText имели общую базу.К сожалению, такой шаблон не может быть достигнут без множественного наследования, поскольку ImmutableDrawnText не имеет отношения к EditableDrawnFancyText.К счастью, интерфейсы допускают множественное наследование, хотя классы этого не делают.

Казалось бы, лучший способ добиться правильного отношения наследования - это определить интерфейсы:

  1. IDrawnText
  2. IDrawnFancyText: IDrawnText
  3. IEditableDrawnText: IDrawnText
  4. IEditableDrawnFancyText: IEditableDrawnText, IDrawnFancyText
  5. IImmutableDrawnText: IDrawnText * 1016 даже не могут использовать даже 1015классы достигли бы всех надлежащих объектных отношений.С другой стороны, предоставление интерфейсов будет означать, что потребители должны будут верить, что никто не реализует так называемый «неизменяемый» интерфейс с объектом, который допускает внешнюю мутацию.

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

    В идеале, было бы возможно выставить открытый интерфейс достаточно хорошо, чтобы можно было передавать внешние экземпляры,без необходимости позволять внешнему коду определять свои собственные реализации.Если бы это было выполнимо, это казалось бы оптимальным подходом.К сожалению, хотя можно открывать публичные абстрактные классы с помощью «внутренних» квалифицированных конструкторов, я не знаю ни одной такой возможности с интерфейсами.Тем не менее, я не уверен, что возможность того, чтобы кто-либо реализовал «IImmutableDrawnText» с объектом, который допускает внешнюю мутацию, обязательно является реальной проблемой.но в его документации будет прямо указано, что объекты, реализующие IDrawnText, могут изменяться или не изменяться другими способами;IImmutableDrawnText предоставит те же члены, что и IDrawnText, но в документации будет прямо указано, что классам, допускающим мутацию, запрещено реализовывать интерфейс.Ничто не помешает непостоянным классам реализовать IImmutableDrawnText в нарушение договора, но любой и все такие классы будут нарушением реализации этого интерфейса.

Ответы [ 3 ]

6 голосов
/ 24 сентября 2011

Не существует такого понятия, как «неизменный интерфейс». Есть интерфейс, в котором не объявляет методы, которые мутируют объект, но нет способа для запретить мутацию. А разрешение мутации разрешает все проблемы с безопасностью потоков и «безопасностью», которые сопровождают ее.

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

Кстати, неизменность не для безопасности. Язык / фреймворк, который допускает отражение (например, C # /. Net?), Может использоваться для виртуальной модификации объекта по желанию, игнорируя все, что сделал объект, чтобы предотвратить его. Неизменность в основном просто делает это трудно случайно.

4 голосов
/ 24 сентября 2011

Интерфейс не определяет изменчивость / неизменность; скорее это служит только для ограничения того, что может потребитель . Тот факт, что интерфейс не предоставляет средства мутации, не означает, что вы должны предполагать, что объект неизменен. Например, IEnumerable ничего не говорит об неизменности коллекции, но это не изменяемый интерфейс.

В основном: вам нужно определить это в другом месте - возможно, в документации.

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

Также: если тип не является sealed, он не может претендовать на неизменность, поскольку сам (базовый) тип даже не знает , что возможно. Опять же, это может или не может быть реальной проблемой.

2 голосов
/ 24 сентября 2011

Вы можете взглянуть на класс WPF Freezable для вдохновения, если хотите реализовать этот шаблон.

Он описывается следующим образом:

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

...