В настоящее время я читаю книгу Марка Симана и Стивена ван Дерсена «Внедрение зависимостей» (второе издание, но тот же пример находится в первом издании).
В главе 9.2.2 «Сообщение об исключениях с использованием шаблона декоратора» он предлагает декоратор обработки исключений, который украшает абстракцию перехватом определенных исключений и открытием окна предупреждения вместо того, чтобы выдавать исключение потребителю.
Он заявляет, что придерживается SRP и OCP, которым я могу следовать.
Однако, на мой взгляд, это нарушает Принцип подстановки Лисков.
Исключения, создаваемые абстракцией, относятся к контракту. Если они перехвачены и преобразованы в предупреждающее сообщение в декораторе, потребитель явно не знает об этом. Потребитель либо должен неявно знать, что декоратор был применен для обработки исключений, что было бы странно, на мой взгляд, либо он не знает этого, и в этом случае он должен сам заботиться об обработке исключений, чтобы придерживаться контракта. и LSP, что сделало бы декоратор бесполезным.
На мой взгляд, потребитель не должен знать, что абстракция была оформлена, он должен придерживаться контракта, который обеспечивает абстракция.
Я бы предложил новый интерфейс IExceptionHandlingAbstraction
, реализованный адаптером, который перехватывает исключения IAbstraction
и преобразует их в окна предупреждений. Таким образом, потребитель может полагаться на контракт IExceptionHandlingAbstraction
и явно знает, что ему не нужно самостоятельно обрабатывать исключения.
Поскольку я очень доверяю сообщениям, ответам и книгам Марка, я не совсем уверен, что я что-то здесь упускаю.
the consumer must not know that an abstraction was decorated, he should adhere to the contract the abstraction provides.
Это верно. Чтобы придерживаться LSP, обе реализации абстракции должны придерживаться одного и того же контракта. Но теперь возникает вопрос: что такое контракт? Чего ждать потребителю.
В отличие от Java, .NET не использует исключения как часть определения метода. И я думаю, что это нормально. Вы не можете и не должны ограничивать исключения, выбрасываемые абстракцией. Это, конечно, меняется, когда вы выбрасываете исключения, которые, как ожидается, будет обрабатывать клиент. В этом случае тип исключения должен быть частью контракта.
Большая проблема в этом конкретном примере декоратора, IMO, заключается в том, что исключение не всплывает. Он полностью проглочен. Это, безусловно, можно считать нарушением (неявного) контракта, поскольку разработчик ожидает, что вызов будет успешным, если не будет выдано исключение. Так что я согласен, что пример немного наивен и действительно нарушает LSP.
Этот пример, однако, не предназначен для того, чтобы быть полным доказательством, 100% SOLID решением, а скорее как пример, демонстрирующий силу перехвата с использованием декораторов. Также обратите внимание, что в главе 10 мы объясняем, что быть на 100% SOLID невозможно и нежелательно. Все дело в компромиссах, и, возможно, этот пример декоратора хорошо работает в конкретном клиентском приложении, которое вы создаете, хотя я ожидаю, что потребитель (форма) должен знать, была ли операция выполнена успешно, потому что вы обычно хотите закрыть форму после ее завершения. . Но в этом случае я ожидаю, что дизайн, описанный в главе 10, будет лучшим решением.
My proposal would be a new interface
IExceptionHandlingAbstraction
that is implemented by an adapter that catches the exceptions ofIAbstraction
and transforms it to alert boxes
Создание новой абстракции, позволяющей потребителю получать уведомления или информацию о сбоях, является хорошим решением проблемы нарушения LSP.
Возврат логического значения может быть немного сложным, потому что это код ошибки, а потребители обычно забывают проверять коды ошибок. Вместо этого решение, которое я использовал в прошлом, состоит в том, чтобы передать продолжение (делегату), которое будет вызываться либо в случае успеха, либо в случае неудачи (в зависимости от того, что наиболее полезно, но я использовал его в случае успеха).
Да, конечно, это более функциональный подход, я также стараюсь использовать его все чаще и чаще для обеспечения безопасности.
Спасибо за указание на то, что измененное «поведение возврата» является фактическим нарушением. В моем
IExceptionHandlingAbstraction
я использовал логическое значение в качестве возвращаемого значения, чтобы указать на успех.