• Какие стоит создавать интерфейсы?

    @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 комментария
  • Какие стоит создавать интерфейсы?

    Alexandroppolus
    @Alexandroppolus
    кодир
    4 принцип SOLID предписывает создавать настолько маленькие, насколько возможно. При этом реализация может имплементировать несколько интерфейсов.

    Но смущает одно, IMove::move() для персонажа игры, скорее всего, будет принимать x,y координаты, а для файла - конечный путь.

    да. Это совершенно разные интерфейсы. Всё правильно.
    Person заимплементит IMove с координатами, а File - с путем.
    Ответ написан
    Комментировать
  • Что такое абстракция?

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

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

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

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

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

    @Akela_wolf
    Extreme Programmer
    Нотификации - в реальном мире это очень сложная тема, с большим количеством подводных граблей и регулярно меняющимися требованиями. Давайте посмотрим какие сущности у нас тут есть с точки зрения принципа единственной ответственности (SRP):

    1. Канал уведомления (NotificationChannel). Вот это то самое: Email, SMS, Telegram, Slack, VK и т.д. Его ответственностью является отсылка сообщения определенным способом (определенный текст в определенный адрес). Я бы написал его интерфейс так:
    interface NotificationChannel {
      void send(String to, String text);
    }


    2. Уведомлятель (Notifier). Его ответственностью является формирование уведомления. И тут тоже возможны варианты, поскольку разных пользователей нужно уведомлять о разных событиях и разным образом. Его ответственостью является сформировать текст сообщения и отправить через определенный канал. Интерфейс выглядит примерно так:
    interface Notifier {
      void notify(User user, Event event);
    }


    3. Дальше возможны варианты. В частности формирование текста сообщения можно выделить в отдельную ответственность, создав класс (или интерфейс) NotificationFormatter. Если предусматривается настройка каналов уведомления для каждого пользователя, то может потребоваться какой-нибудь NotificationChannelManager и т.д. Погружаться в эту кроличью нору можно очень глубоко. Все зависит от того насколько детально вы хотите разложить подсистему на объекты.

    Лично я бы начал как минимум с интерфейса Notifier, который позволяет "закрыть" подробности реализации подсистемы уведомлений от вызывающего кода. Эта граница, на мой взгляд, лишней не будет. А затем уже, по желанию или при необходимости, раскладывал его реализацию на отдельные, несвязанные классы.
    Ответ написан
    3 комментария