Как менять код соблюдая второй принцип SOLID?

Ссылка скопирована
1 ответ

Покажу простой rest api псевдо код:

class UserController:     public function all(): array         return $this->userService->all()->toArray();             public function single($id): array         return $this->userService->single($id)->toArray();                   class UserService:     public function all(): UserServiceDTOCollection         $users = $this->repo->all();         # convert UserRepositoryDTOCollection -> UserServiceDTOCollection         $users->convert(UserServiceDTOCollection::class);                  return $users;              public function single($id): UserServiceDTO         $user = $this->repo->single($id);        # convert UserRepositoryDTO-> UserServiceDTO         $user->convert(UserServiceDTO::class);                  return $user                   class UserRepository:     public function all(): UserRepositoryDTOCollection         ...              public function single($id): UserRepositoryDTO        ...

class UserController: public function all(): array return $this->userService->all()->toArray(); public function single($id): array return $this->userService->single($id)->toArray(); class UserService: public function all(): UserServiceDTOCollection $users = $this->repo->all(); # convert UserRepositoryDTOCollection -> UserServiceDTOCollection $users->convert(UserServiceDTOCollection::class); return $users; public function single($id): UserServiceDTO $user = $this->repo->single($id); # convert UserRepositoryDTO-> UserServiceDTO $user->convert(UserServiceDTO::class); return $user class UserRepository: public function all(): UserRepositoryDTOCollection ... public function single($id): UserRepositoryDTO ...

Вот мой код, если кратно тут 3 слоя: UserController, UserService, UserRepository, все отделены DTO'шками.

1. Как по принципам солид добавлять/удалять/менять контроллеры?

Как вижу я:

Добавлять/изменять - UserController2 extends UserController и DI:container(UserControllerInterface::class, new UserController2)

Удалять - тут уже в route.php просто удалять путь

2. Если поступила задача юзеру добавить новое поле, например Avatar, то как это сделать?

Как я вижу:

1. UserResitoryWithAvatar extends UserRepository
2. UserRepositoryAvatarDTO extends UserRepositoryDTO
3. UserRepositoryAvatarDTOCollection extends UserRepositoryDTOCollection,
4. UserService доваляют новый метод allWithAvatar():UserServiceWithAvatarDTOCollection
5. UserService доваляют новый метод singleWithAvatar():UserServiceWithAvatarDTO
6. DI:container(UserRepositoryWithAvatarInterface::class, new UserRepositoryWithAvatar)

Ни как не могу понять как все это менять, если есть кто толковый был бы рад списаться и обсудить в каком то чате (whatsapp, discord, ...). Все читаю, читаю, ... и не понимаю как использовать

Дополнительно:

А если потом потребуется добавить еще поле, а спустя время еще одно, и еще?
Будет что-то вроде: UserRepositoryWithAvatarAndCompanyAndSiteAndTelegram ?
Не являюсь экспертом в solid, но не понимаю, почему нельзя расширить новым свойством имеющиеся слои? Кажется, вы пытаетесь только переусложнить.

  • WitER, я согласен с вами, но второй принцип солид гласит мы не можем менять существующий код, только дополнять. Вот и не понятно, как его менять
  • И я показал очень простой пример чтобы разобратся по факту там может быть много использований классов UserService, UserRepository другими классами, и если мы поменяем что то, то надо будет править и другие места. а второе правило обеспечивает нам возможность добавлять и не ломать старый функционал
  • Принцип open/closed не говорит о том что добавляя автар вы обязаны создавать новые классы. Он говорит что вы должны стремиться к тому что бы новый функционал появлялся через добавление нового кода - в том числе и новых методов, новых атрибутов класса. То есть если в UserRepositoryDTO допишите новый setter и getter setAvatar, getAvatar и новый атрибут avatar это будет вполне в рамках этого принципа.
  • O в SOLID вообще относится к базовым классам, которые переиспользуются прикладным кодом. Выдрючивать по этому принципу конечные классы, от которых больше никакой код не зависит - это карго-культ в чистом виде.
  • Дмитрий, но как это может быть в рамках этого принципа, когда выходные данные с этого репозитория другие, представьте что UserRepository используют 10 классов, и часть из них выдают данные пользователям, тогда мало того что данные уже будут скриптам передоватся не стандартные там будет доп поле, так еще и на выходет в апи будут поля которые не хотелось бы показывать. Это уже называется сайд эффектами. Или я не прав в чем то ?
  • Adamos, ну это выдрючивание уже идет с чистой архитектуры, это обеспечивает изляцию по уровням и всегда знаеш что должно приходить. я наверно усложнил код... Извиняюсь, учусь только
  • systemaworking, ну, так добавляете еще одно поле в DTO - оно и будет приходить.
    Зачем вокруг этого марлезонский балет? Ради какой цели?
  • systemaworking, выходные из UserRepository у вас как был UserDTO так и остался. Обьекты которые работаю с этим ДТО и не знают что у него появился новый аттрибут и новые сеттеры и геттеры - так и продолжат работать, вы ж старый функционал не трогаете. Что с ними случится? В этом то и заключается смысл всей этой движухи - добавлять функционал и тогда обьекты которые не знают или им не надо знать о новом функционал - будут работать попрежнему, потому что старый вы не затронули. А если вы начнете свой движ - с созданием новых классов ДТО под каждый чих то вообщем то такая интепретация этого принципа принесет вам ровно те проблемы от которых этот принцип должен избавлять.
  • Дмитрий, отнюдь. Проблемы с зоопарком классов, предназначенных примерно для одного и того же, будут куда интереснее и разнообразнее, чем банальный говнокод ;)
  • systemaworking, если говорить проще. Принцип Open/Close работает на то что бы уменьшить количество времени которое потребуется программисту что бы понять где у него чего упадет в случае добавления/изменения/удаления функционала который у него был, с учетом того что рядом может сидеть его коллега который пилит задачу связанную с вашими обьектами но не фига не знающий о тех изменениях что вы вносите. В случае с ДТО - это вполне решиться доп атрибутом и сеттером и геттером. В каких то случаях возможно придется добавлять новый класс. Но ваш вариант когда у вас приходит новый человек ему дает задачу там создавать пользователя через очередь - и он начиная писать UserDTO в IDE и получает подсказки UserDTO, UserWithAvatarDTO, UserWithAvatarWithPenisLengthDTO, UserWithAvatarWithPenisLengthWithBallsCountDTO - ну мягко говоря еще хуже, даже не затрагивая тему а нахрена вам все предыдущие DTO - их не должно быть в системе ибо их использование приведет к некорректной работе.
  • Adamos, тут не так просто же, ну добавил я поле в UserRepositoryDto, и UserService уже принемает данные не те к которым он был написан, может он там их в array загоняит и т.д. Я к тому что в UserSErvice данные уже идут нового формата, и значит мне и юзер сервис надо править, чтобы правильно обрабатывал и все классы что использую этот юзер сервис. А SOLID создан для быстрого изменения а не правки кучи файлов. Вот а тут и туплю, не могу понять. Нужен человек кто уже это все применил и прокачал в себе.
  • Дмитрий, ну вы тоже по своему правы. Но чето я не догоняю зачем тогда это солид правила, когда можно тупо править классы и не парится. Я отталкивался от такого момента. Вот написал я метод который выдает определенную структуру данных, и начили другие классы это использовать, и пока эта структура не изменится все будет хорошо работат, как только она изменится добавлением нового атрибута, есть вероятность ошибки
  • systemaworking, ну да, вам придется править все, что затрагивается новым функционалом.
    Соблюдение принципов позволит только не перепахивать по этому поводу половину кода.
    Бережно сохранять при этом старые классы, которые никем извне не будут использоваться - оверинжиниринг.
  • systemaworking, это не законы за нарушение которые следует расстрел. это принципы к которым надо стремиться. Ну возьмем ваш случай, и добавим ограничение что avatar является обязательным атрибутом. И? Окей вы создали новое DTO. Новый сервис, новый контроллер, все новое. Старые теперь надо удалить - они не будут работать. Удалили. Ну и по факту вы изменили старый класс ну и только еще переименовали его. Ну придет кто нибудь посмотрит а нахрен нам UserAvatarDTO - назовем проще UserDTO. Ну и теперь можно сказать что вы просто сделали 2 лишних шага но ровно нарушили принцип.
  • systemaworking, тут еще такой нюанс.
    Есть паттерны программирования - вот они про взаимодействие классов, как одни с другими общаются и какие ограничения друг на друга накладывают.
    А принципы KISS, SOLID и DRY - они не про архитектуру, они про то, как писать сами классы.
    О, например, побуждает не выносить в интерфейс класса детали его реализации, скрывать их за обобщениями, чтобы класс не нужно было всерьез переписывать при обновлениях, но можно было дополнять, сохраняя интерфейс в целом неизменным. Необязательно при наследовании, при правках - тоже.
    У вас это все уже соблюдается....
  • Как менять код соблюдая второй принцип SOLID?

    Цей принцип не про недоторканість коду, як ви його розумієте. Наявний файл з кодом можна правити і потрібно, але робити це потрібно так, щоб наявний функціонал не зазнавав змін.

  • Дмитрий, если это в одном месте, то я поступлю как вы советуете, так как зачем мне переменование. Но если куча файлов, тогда мой лучше выходит... Но опят такие... Пятый принцим и нужен чтобы расширать классы.. ох
  • systemaworking, Ваш вариант в случае с вашим примером с аватаром не выйдет лучше никак. Еще раз принцип open/close не говорит что изменение кода должно быть ОБЯЗАТЕЛЬНО через добавление нового класса. Новый метод вполне катит, как и новый атрибут.
  • Ответы:

    для начала научись грамотно формулировать мысли и приводить работоспособные примеры
    1. все строится на интерфейсах которые у тебя нихрена не описаны - откуда-то появляется UserControllerInterface
    2. для проброса объектов использовать либо контейнеры, либо фабрики
    3. не стоит пытаться создать holy controller подходящий для решения все задач
    4. к OCP твой вопрос никак не относится - суть данного принципа в том, что свойства классов приватные, а геттеры/сеттеры - публичные, это позволит расширять класс при этом не модифицируя напрямую родителя

    • 4 пункт не понял. Как править если надо добавить новое поле? Или добавлять дополнительный функционал?
    • systemaworking,

      Как править если надо добавить новое поле?

      через публичные/защищенные сеттеры. смысл в том, что только объект может иметь доступ к его свойствам. В приведенном коде ничего подобного нет (программные сущности … должны быть открыты для расширения, но закрыты для модификации)

    Нужно решить такую задачу?

    Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

    Заказать помощь
    Лучший ответ
    1
    Никита Орлов Ответ

    Второй принцип SOLID - принцип открытости/закрытости (Open/Closed Principle) - гласит, что программные сущности должны быть открыты для расширения, но закрыты для модификации. Это означает, что код должен быть легко расширяемым без необходимости изменения его базовой структуры.

    Чтобы соблюдать второй принцип SOLID при изменении кода, следует придерживаться следующих рекомендаций:

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

    2. Используйте паттерны проектирования, такие как стратегия, декоратор или адаптер, чтобы добавлять новую функциональность к существующему коду, не изменяя его.

    3. Разделяйте код на модули и компоненты, чтобы изолировать изменения и уменьшить зависимости между различными частями программы.

    4. Используйте инверсию управления (Inversion of Control) и внедрение зависимостей (Dependency Injection) для разделения создания объектов от их использования. Это позволит легко заменять объекты на более специализированные или расширенные версии.

    5. Пишите модульные тесты для проверки функциональности кода перед его изменением. Тесты помогут избежать случайных ошибок и обеспечат стабильность кода при его модификации.

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

    Другие ответы (0)

    Пока нет других ответов. Будьте первым, кто поможет автору.

    Ответить на вопрос

    комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *

    Вам также может быть интересно