Ответы пользователя по тегу ООП
  • Как нормально реализовать классы в PHP?

    @Akela_wolf
    Extreme Programmer
    Начну с самого главного: создав такую иерархию наследования вы написали Language является SQLite3
    Really? Собственно из этого решения в дальнейшем будут торчать все возможные проблемы, парочка невозможных и одна невероятная.

    Не злоупотребляйте наследованием. В данном случае правильное решение - композиция.
    Ответ написан
    7 комментариев
  • Насколько хорошая практика передавать в метод имя класса, который его вызывает?

    @Akela_wolf
    Extreme Programmer
    Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете.
    С. Макконнелл


    Первый вариант понятен. Вы поиском сможете найти откуда каждый метод вызывается. Имя метода четко указывает что он делает.

    Второй вариант - непонятен. Во-первых, имя метода ни о чем не говорит. Чтобы понять что он делает - придется читать сам метод. Во-вторых при поиске мест вызова нужно будет постоянно проверять контекст - что за параметр передан в этот метод? А если он будет вызываться из класса-наследника одного из контроллеров? А если будут использовать трейты? А если код будет вынесен в делегат? Очень много сложностей для решения настолько простой задачи.
    Ответ написан
    1 комментарий
  • Какие стоит создавать интерфейсы?

    @Akela_wolf
    Extreme Programmer
    А если бы, допустим, в у нас был отдельный интерфейс IMove, то мы смогли бы реализовать его в каком угодно модуле/компоненте, например в условном File, ведь мы можем переместить файл. Так же и с ISleep, мало ли что может уснуть, начиная от операционной системы или потока выполнения, заканчивая персонажем в тамагочи.


    Неверно. IMove (точнее будет назвать Movable) имеет смысл в контексте какой-то предметной области. И перемещение файла, перемещение спрайта, перемещение трехмерного объекта, перемещение руки робота - все это разные перемещения. Соответственно и интерфейсы будут разные (расположенные в разных модулях, даже если будут называться одинаково), например:
    filesystem.Movable
    graphics.2d.Movable
    graphics.3d.Movable
    servo.Movable


    Поэтому ответ - нет, не нужно придумывать что-то общее у разнородных предметов и явлений. Только если это что-то общее позволяет вам как-то лучше моделировать предметную область.

    Вообще главный принцип тут такой: интерфейс должен иметь смысл, описывать некоторую единицу функциональности именно с точки зрения предметной области. Скажем, если в вашей предметной области операция move имеет смысл без операций eat и sleep - её можно выделить в отдельный интерфейс (а можно и не выделять, это зависит от других факторов). Если же операции eat и sleep всегда должны ходить парой и каждый потребитель этого интерфейса нуждается в обеих операциях - они должны быть в одном интерфейсе.
    Ответ написан
    4 комментария
  • Что такое абстракция?

    @Akela_wolf
    Extreme Programmer
    Не претендую на истину в последней инстанции, но в моей голове это выглядит так:

    Абстракция - это некоторое обобщение. Например, есть кошка, собака и попугай. Мы можем ввести абстракцию "домашние животные". Добавив в эту конструкцию волка и рысь мы можем ввести абстракцию "дикие животные". А затем ввести абстракцию "животные". А затем "живые существа" и т.д.

    В объектно-ориентированном программировании абстракции принимают форму абстрактных классов и интерфейсов.
    В обобщенном программировании абстракции принимают форму шаблонных классов и функций.
    В функциональном программировании абстракции принимают форму функций высшего порядка.

    "Чистая выдумка" - с моей точки зрения это не абстракция, это лишь способ выделения ответственности в отдельный класс. Но эта самая ответственность вполне конкретна.
    Классы - тем более не абстракция. При любом моделировании мы переносим в модель существенные черты моделируемого явления (системы) и отбрасываем несущественные. То о чем вы говорите - неизбежная потеря деталей при построении модели.
    Ответ написан
    2 комментария
  • Нарушает ли переопределение конструктора принцип подстановки Лисков в частных случаях?

    @Akela_wolf
    Extreme Programmer
    Нет, не нарушает. Принцип подстановки Лисков говорит о том, что клиент не должен видеть разницы между базовым объектом (в вашем случае интерфейсом) и его потомком. То есть потомки должны соблюдать контракт, определенный предком (могут делать его строже, но не слабее)

    Конструктор не является частью интерфейса, поэтому переопределение конструктора никак принцип подстановки Лисков нарушать не будет.
    Ответ написан
    3 комментария
  • Стоит ли вносить константы в класс?

    @Akela_wolf
    Extreme Programmer
    Нормальная практика. Суть константы в том числе в избежании дублирования значения и повышения читаемости кода
    Ответ написан
    Комментировать
  • Как провести соответствие между строкой и классом с точки зрения SOLID?

    @Akela_wolf
    Extreme Programmer
    Если уж совсем идеально, то:
    interface Voice {
      void say();
    }
    
    class Dog implements Voice { ... }
    class Cat implements Voice { ... }
    
    interface VoiceFactory {
      Voice getVoice(String voiceType);
    }
    
    class VoiceFactoryImpl implements VoiceFactory {
      private Map<String, Supplier<Voice>> suppliers = new HasMap<>();
      
      void addSupplier(String type, Supplier<Voice> supplier) { suppliers.put(type, supplier); }
    
      @Override
      Voice getVoice(String type) {
        final Supplier<Voice> supplier = suppliers.get(type);
        if (supplier != null) {
          return supplier.get();
        } else {
          throw new RuntimeException("No supplier for type: "+type);
        }
      }
    }


    Класс VoiceFactoryImpl выполняет принцип OCP - открыт для дополнений через метод addSupplier, в него можно добавлять новые сопоставления строка - животное и закрыт для изменений.
    Ответ написан
    9 комментариев
  • Как получить текущий класс в static методе?

    @Akela_wolf
    Extreme Programmer
    Никак. Рассматривайте статический метод как функцию, которая никак не связана с классом (кроме того что статический метод объявлен в его пределах). Просто в Java нет глобальных функций, все делается в пределах классов. А это оставляет для глобальных функций только статические методы. Статический метод вы можете перенести в другой класс - и все будет работать (когда ссылки поправите, разумеется). Также статический метод не может иметь никакого отношения к наследованию.

    Предполагаю, то что вам нужно называется шаблоном "фабрика" и обычно делается на базе синглтонов, а не статических методом.
    Ответ написан
    Комментировать
  • Как лучше вынести логику в общий класс?

    @Akela_wolf
    Extreme Programmer
    1. Сначала нужно обдумать что вы с этого будете иметь. Вы сможете переиспользовать эту логику? Вы сможете покрыть логику тестами? Иначе говоря, сформулировать цель такого рефакторинга.
    2. Собственно цель и определит какую логику вы будете выделять. Если задача переиспользовать - значит есть какой-то второй сценарий использования. Смотрите на него, выделяете общие с первым элементы. Выделяете различающиеся элементы - думаете как правильно оформить общие и различные элементы в коде (наследование, композиция, декораторы и т.п. приемы). Собственно на этом этапе у вас появляется план рефакторинга. Если же цель - облегчить тестирование, то начинаете с обдумывания теста. И рассматриваете тест как второй сценарий использования.
    3. Затем остается этот план рефакторинга претворить в жизнь.
    Ответ написан
    Комментировать
  • MVC (PHP): правильно ли понимаю слои?

    @Akela_wolf
    Extreme Programmer
    Модель - собственно данные с которыми работает программа. Они составляют основу программы и, вообще говоря, ничего не должны знать о контроллерах и прочем взаимодействии с пользователем. Теоретически к слою данных можно обратиться откуда угодно - хоть из скрипта, хоть через рест, хоть через вебсокеты, хоть через интерфейс десктопного приложения и т.д. Сервисы, в которых у вас бизнес-логика находятся рядом с моделью и составляют ядро приложения. Либо этих сервисов нет и вся бизнес-логика в модели (такое тоже может быть)

    Контроллер - собственно слой, отвечающий за взаимодействие с пользователем (причем конкретный способ взаимодействия - через рест или веб)
    Представление - тоже рядом, это преобразованные данных модели в то как они отдаются пользователю. Если мы говорим про рест - там может не быть представления как такового, могут отдаваться сразу модели. А может и быть - тут все зависит от конкретной задачи

    Хранение - слой, отвечающий за сохранение данных модели в БД/файлы/куда-нибудь еще. Модель, как чистая бизнес-логика, обычно не должна знать про то как и куда её данные сохраняются. Есть просто интерфейсы - прочитать данные, записать данные. А за ними реализация работы с БД и прочими хранилищами.

    Это я написал так, как полагается делать в теории, как описывает Роберт Мартин в книге "Чистая архитектура". Понятно что в реальности (а мы говорим про PHP), все это вряд ли будет настолько четко разделено в коде. Но в голове вот такое разделение на Контроллер(представление) - Модель - Хранение я держать советую.
    Ответ написан
  • Зачем в интерфейсах логика?

    @Akela_wolf
    Extreme Programmer
    1. Класс может иметь поля (состояние), интерфейс нет. Вот это первое и главное различие
    2. Интерфейс не может иметь протектед и приватные методы, класс - может.

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

    Еще посмотрите extension functions в Котлине - придуманы с той же идеей, хотя имеют свои преимущества (легко добавить к существующему интерфейсу/классу) и недостатки (нельзя перекрыть в производном классе)
    Ответ написан
    Комментировать