Обычно предлагается, чтобы неизменяемые классы были запечатаны, чтобы обеспечить обещание потребителям, что наблюдаемые свойства класса останутся неизменными.Конечно, это может показаться хорошей практикой для классов, которые будут использоваться в контексте безопасности.С другой стороны, в ряде случаев может быть полезно иметь несколько неизменяемых классов с общими базовыми функциями, а также иметь редактируемые версии таких классов.
Например, графическая программа можетиметь объект DrawnText, который содержит местоположение, шрифт и строку, а также производную строку DrawnFancyText, которая добавляет параметры к тексту кривой вокруг фигуры.В некоторых контекстах может быть полезно иметь неизменные версии этих объектов (например, для таких вещей, как буферы отмены), но в других контекстах может быть более полезно иметь изменяемые версии.
В таком контексте существуютв некоторых контекстах нужен читаемый объект DrawnFancyText, но ему все равно, является ли он изменяемым или нет, но есть другие, где нужен неизменный производный от DrawnText или DrawnFancyText, но это не важно.Достижение первого потребует, чтобы EditableDrawnFancyText и ImmutableDrawnFancyText имели общую базу;для достижения последнего потребуется, чтобы ImmutableDrawnText и ImmutableDrawnFancyText имели общую базу.К сожалению, такой шаблон не может быть достигнут без множественного наследования, поскольку ImmutableDrawnText не имеет отношения к EditableDrawnFancyText.К счастью, интерфейсы допускают множественное наследование, хотя классы этого не делают.
Казалось бы, лучший способ добиться правильного отношения наследования - это определить интерфейсы:
- IDrawnText
- IDrawnFancyText: IDrawnText
- IEditableDrawnText: IDrawnText
- IEditableDrawnFancyText: IEditableDrawnText, IDrawnFancyText
- IImmutableDrawnText: IDrawnText * 1016 даже не могут использовать даже 1015классы достигли бы всех надлежащих объектных отношений.С другой стороны, предоставление интерфейсов будет означать, что потребители должны будут верить, что никто не реализует так называемый «неизменяемый» интерфейс с объектом, который допускает внешнюю мутацию.
Для информации, не связанной с безопасностью, будет ли этохорошо ли использовать интерфейсы, чтобы разрешить надлежащие отношения наследования и полагаться на то, что исполнители не нарушают контракты?
В идеале, было бы возможно выставить открытый интерфейс достаточно хорошо, чтобы можно было передавать внешние экземпляры,без необходимости позволять внешнему коду определять свои собственные реализации.Если бы это было выполнимо, это казалось бы оптимальным подходом.К сожалению, хотя можно открывать публичные абстрактные классы с помощью «внутренних» квалифицированных конструкторов, я не знаю ни одной такой возможности с интерфейсами.Тем не менее, я не уверен, что возможность того, чтобы кто-либо реализовал «IImmutableDrawnText» с объектом, который допускает внешнюю мутацию, обязательно является реальной проблемой.но в его документации будет прямо указано, что объекты, реализующие IDrawnText, могут изменяться или не изменяться другими способами;IImmutableDrawnText предоставит те же члены, что и IDrawnText, но в документации будет прямо указано, что классам, допускающим мутацию, запрещено реализовывать интерфейс.Ничто не помешает непостоянным классам реализовать IImmutableDrawnText в нарушение договора, но любой и все такие классы будут нарушением реализации этого интерфейса.