Это изображение взято из применения доменного дизайна и шаблонов: с примерами в C # и .NET
Это диаграмма классов для шаблона состояний, в котором в SalesOrder
течение срока службы может быть несколько состояний. Между разными состояниями разрешены только определенные переходы.
Теперь OrderState
класс является abstract
классом, и все его методы наследуются его подклассам. Если мы рассмотрим подкласс, Cancelled
который является конечным состоянием, которое не допускает никаких переходов в какие-либо другие состояния, нам придется использовать override
все его методы в этом классе, чтобы генерировать исключения.
Разве это не нарушает принцип подстановки Лискова, поскольку подкласс не должен изменять поведение родителя? Исправляет ли это изменение абстрактного класса в интерфейс?
Как это можно исправить?
cancel()
меняет состояние на отмененное.Ответы:
Это конкретная реализация, да. Если вы сделаете состояния конкретными классами, а не абстрактными разработчиками, тогда вы избежите этого.
Однако шаблон состояния, на который вы ссылаетесь, который, по сути, является проектом конечного автомата, в общем, я не согласен с тем, как я его вижу. Я думаю, что есть достаточные основания для того, чтобы осудить это как нарушение принципа единой ответственности, потому что эти шаблоны управления состоянием в конечном итоге становятся центральными хранилищами для знания текущего состояния многих других частей системы. Этот централизованный элемент управления состоянием чаще всего требует бизнес-правил, относящихся ко многим различным частям системы, для их разумной координации.
Представьте себе, что все части системы, которые заботятся о состоянии, находятся в разных сервисах, в разных процессах на разных машинах, в центральном диспетчере состояния, который детализирует статус для каждого из этих мест, эффективно являются узкими местами всей этой распределенной системы, и я думаю, что узкое место является признаком нарушения SRP, а также вообще плохой дизайн.
Напротив, я бы предложил сделать объекты более интеллектуальными, как в объекте Model в паттерне MVC, где модель знает, как обращаться с собой, ей не нужен внешний оркестратор для управления своими внутренними действиями или причинами для этого.
Даже помещая такой шаблон состояния внутри объекта, чтобы он управлял только собой, создается впечатление, что вы сделали бы этот объект слишком большим. Я бы сказал, что рабочие процессы должны осуществляться через композицию различных самодостаточных объектов, а не с помощью одного организованного состояния, которое управляет потоком других объектов или потоком интеллекта внутри себя.
Но в этот момент это больше искусство, чем разработка, и поэтому определенно субъективно вы подходите к некоторым из этих вещей, в которых говорится, что принципы являются хорошим руководством, и да, реализация, которую вы перечислите, является нарушением LSP, но ее можно исправить, чтобы не допустить. Просто будьте очень осторожны с SRP при использовании любого шаблона такого рода, и вы, вероятно, будете в безопасности.
источник
Это распространенное неправильное толкование LSP. Подкласс может изменить поведение родителя, если он остается верным для родительского типа.
Есть хорошее длинное объяснение Википедии , предлагающее вещи, которые нарушат LSP:
Лично мне проще запомнить это: если я смотрю на параметр в методе с типом A, может ли кто-нибудь передать подтип B вызвать какие-либо сюрпризы? Если они будут, то есть нарушение LSP.
Является ли исключение сюрпризом? На самом деле, нет. Это может произойти в любое время, независимо от того, вызываю ли я метод Ship в OrderState или Granted или Shipped. Поэтому я должен учитывать это, и это не является нарушением LSP.
Тем не менее , я думаю, что есть лучшие способы справиться с этой ситуацией. Если бы я писал это на C #, я бы использовал интерфейсы и проверил бы реализацию интерфейса перед вызовом метода. Например, если текущий OrderState не реализует IShippable, не вызывайте метод Ship для него.
Но тогда я бы тоже не использовал шаблон State для этой конкретной ситуации. Шаблон State гораздо больше подходит для состояния приложения, чем для состояния объекта домена, подобного этому.
Итак, в двух словах, это плохо продуманный пример Государственного паттерна и не очень хороший способ обработки состояния заказа. Но это, возможно, не нарушает LSP. И государственная структура, сама по себе, конечно , нет.
источник
CircleWithFixedCenterButMutableRadius
в качестве вероятного нарушения LSP, если в потребительском договореImmutablePoint
указано, что еслиX.equals(Y)
потребители могут свободно заменить X на Y, и наоборот, и, следовательно, должны воздерживаться от использования класса таким образом, чтобы такая замена вызывала проблемы. Тип может быть в состоянии законно определить такequals
, что все экземпляры будут сравниваться как отдельные, но такое поведение, вероятно, ограничит его полезность.(Это написано с точки зрения C #, поэтому нет проверенных исключений.)
Согласно статье Википедии о LSP , одним из условий LSP является:
Как вы должны это понимать? Что именно являются «исключениями, создаваемыми методами супертипа», когда методы супертипа являются абстрактными? Я думаю, что это исключения, которые задокументированы как исключения, которые могут генерироваться методами супертипа.
Это означает, что если
OrderState.Ship()
задокументировано что-то вроде «throws,InvalidOperationException
если эта операция не поддерживается текущим состоянием», то я думаю, что этот дизайн не нарушает LSP. С другой стороны, если методы супертипа не документированы таким образом, LSP нарушается.Но это не означает, что это хороший дизайн, вы не должны использовать исключения для нормального потока управления, к которому это кажется очень близким. Кроме того, в реальном приложении вы, возможно, захотите узнать, можно ли выполнить операцию, прежде чем пытаться это сделать, например, чтобы отключить кнопку «Отправить» в пользовательском интерфейсе.
источник