Search
Write a publication
Pull to refresh
29
0
Паршиков Павел @parshikov_pavel

Software engineer

Send message

Конечно, наследование никто не отменяет. И поэтому в статье и не утверждается, что надо безоговорочно отказаться от наследования в пользу агрегации. Ведь это отдельные инструменты, и каждый может оказаться более удобным в конкретной задаче. И если задача как та, которую Вы описали, — один класс тесно связан с другим, будет изменяться вместе с ним и разделяет с ним большую часть реализации, — то берите наследование.


В статье лишь утверждается, что нужно "по умолчанию" ограничить возможность наследования с помощью final. А уж если Вы пошли по пути создания дочерних классов, то старайтесь уменьшить зацепление на детали реализации настолько, насколько это возможно. Например, используя описанные в статье техники — abstract классы и final методы. А уж если реализация может быть переопределена в дочернем классе, то задокументируйте в PHPDoc все существенные нюансы.

"Связность" и "зацепление" — несколько неустоявшиеся переводы английских cohesion и coupling. Ориентировался при использовании терминов на википедию. Хотя разные авторы и переводчики используют для coupling как зацепление, так и связность.
По поводу "инкапсуляции" и "сокрытия", тоже существуют разные мнения. В википедии написано, что они часто используются взаимозаменяемо:


The term encapsulation is often used interchangeably with information hiding.

Но если исходить из смыслового определения инкапсуляции, как сочетания данных с методами, и сокрытия, как ограничения доступа. То в статье речь идет как раз о сокрытии. Есть подробная статья по этому поводу.

Финальный класс без контракта требуемый в качестве аргумента, это запрещение использования данного принципа. Это запрещение разработчику который хочет воспользоваться вашим кодом, расширения его частей

Конечно, как и любая ограничивающая конструкция, final снижает гибкость и "отключает" некоторые возможности ООП.
Зато с помощью final Вы можете взять в свои руки управление гибкостью использования своих классов. И если Вы не предусмотрели интерфейс для финального класса, который передается в качестве параметра в некоторый метод, то значит данный метод и разрабатывался для работы с этим конкретным классом, stable зависимостью. Если вы просто уберете final и разрешите создавать наследников для некоторой зависимости, то обязаны, по сути, гарантировать, что метод сможет с ними взаимодействовать. При том что у Вас будет отсутствовать явный контракт для этих дочерних классов.
Если формально подходить к LSP, то его реализация и невозможна без контракта.


It is a semantic rather than merely syntactic relation, because it intends to guarantee semantic interoperability of types in a hierarchy.
In addition to the signature requirements, the subtype must meet a number of behavioural conditions.

Нет интерфейса и контракта — нет никаких гарантий того, что в результате наследования будет получен поведенческий подтип (behavioural subtyping) и поэтому нет LSP.

Тут стоит учитывать, что есть два типа зависимостей: стабильные (stable) и изменчивые (volatile) зависимости. И в зависимости от типа зависимости нужно решать — выделять интерфейс или нет.


Logger — классическая volatile зависимость. Он может иметь несколько конкретных реализаций. А значит для него необходимо выделить интерфейс и организовать зацепление через интерфейс. В соответствии с принципом инверсии зависимостей (dependency inversion principle, DIP).


И если SomeService зависит от конкретной реализации зависимости Logger, то тут явное нарушение DIP. Клиент должен зависеть от абстракции и не зависеть от конкретных деталей реализации.


В случае же стабильных (stable) зависимостей — объектов-данных (Data Object), сущностей предметной области (типа Post, Comment), — нет смысла выделять интерфейс и использовать DIP. Т.к. они не являются абстракциями, а объекты вполне конкретного типа. Использование интерфейсов для них только усложняет код.

Information

Rating
Does not participate
Location
Россия
Date of birth
Registered
Activity