Кружок про ООП и дизайн / archived / read-only

 
  • Зачем нужен дизайн
    • Нам надо бежать быстрее и у нас динамично меняющиеся требования, нам некогда наводить красоту
    • Добавлять новое, чем ещё не пользуются - относительно легко
    • Изменять трудно
    • Изменять чужой код ещё труднее
      • |75%
    • Кажется, что проще выкинуть и написать заново, если потребуется
      • что делать с зависимым компонентами?
      • где гарантии, что в этот раз вы сделает правильно
    • Делать сразу хорошо дешевле (на длинной дистанции) чем наводить порядок потом
      • © Rich Hickey: SimpleMadeEasy

      • Как исправить?
        • What kind of runner can run as fast as they possibly can from the very start of a race? Right, only somebody who runs really short races, okay? [Audience reply: Sprinter] But of course, we are programmers, and we are smarter than runners, apparently, because we know how to fix that problem, right? We just fire the starting pistol every hundred yards and call it a new sprint.

    • Дизайн это не "наведение красоты" и не попытка предусмотреть все возможные сценарии в будущем
    • Цель дизайна
      • Снижение стоимости внесения изменений
  • Хороший дизайн (что даёт)
    • Предсказуемый код
      • Последствия изменений должны быть очевидны
    • Рациональный (Reasonable)
      • Стоимость изменений пропорциональна пользе от их внесения
    • Переиспользуемый код
      • Существующий код можно переиспользовать в новом неожиданном контексте
    • Показательный. Самовоспроизводящийся код
      • Выставляет напоказ принципы, заложенные в нём, делает их очевидными
    • Понятный, читаемый, красивый, хорошо пахнущий и тд...
  • ООП
    • Чем быстрее вы забудете ООП, тем лучше для вас и ваших программ
      • Да-да. Если ООП для вас это только инкапсуляция, полиморфизм и наследование
    • Процедурный подход
      • У нас есть структуры данных и операции, которые над ними можно выполнять
      • java // я говорю как я хочу выполнить действие while (file.getPos() < file.size) { byte[] chunk = file.read(Hdd.BLOCK_SIZE); hdd.head.setPosition(calculateNextPostion(...)); hdd.getCurrentBlock().setValue(chunk); file.setPos(file.getPos() + Hdd.BLOCK_SIZE); }
    • Объектный подход
      • У нас есть объекты с поведением, которые обмениваются сообщениями
      • ```java storage.write(file); // я говорю, что я хочу

        class Hdd implements Storage { void write(File file) { while (file.hasNext()) { write(file.readNext(BLOCK_SIZE)); } }

        void write(byte[] bytes) {
            // assertBlockSize
            head.setPosition(...);
            currentBlock.write(bytes);
        }
        

        }

        class File { boolean hasNext(); byte[] readNext(int numOfBytes); }

        ```

    • Попроси меня сделать что ты хочешь А не говори мне как ты хочешь это что-то сделать
    • Думать сценариями, а не объектами
      • Из докладов Rich Hickey (автор языка clojure): Молния бьёт в дерево. Кто кого вызывает?
      • В первую очередь надо думать о том, что мы хотим делать, а не как
      • Я хочу отправить сообщение, кто за него должен отвечать?
    • ООП позволяет
      • уменьшить зависимость между компонентами
      • лучше переиспользовать код
  • Зависимости
    • Управление зависимостями
      • Схема
      • Хорошо, когда часто измененяемые компоненты зависят от более стабильных
        • От стандартной библиотеки зависеть можно
        • От фреймворка тоже, если очень уверены, что не соберётесь его поменять
      • Зависеть от абстракций (они стабильнее), а не от конкретных реализаций
      • Используйте инверсию зависимости
      • Изолируйте зависимости
        • Сделайте обёртку со стабильным интерфейсом
    • Виды зависимостей
      • Зависимость от конкретного класса
      • Зависимость от конкретного метода
      • Зависимость от аргументов метода
    • Пример
      • ```java class FraudConfirmationService { private SmartcallsClient smartcalls;

        void confirmOrder(Long orderId) {
            Order order = getOrder(orderId);
        
            SmartcallsResult res = smartcalls.appendToCampaign(PhoneNormalizer.normalizeWithoutPlus(order.phone));
            if (res.getAttempt().isSuccess()) {
            }
        }
        

        } ```

    • Исправление
      • Скажи, что ты хочешь, а не как
      • Наше желание спросить клиента делал ли он заказ - стабильнее чем способ, которым мы это делаем
        • ```java class FraudConfirmationService { private ClientConfirmationService clientConfirmationService;

          void confirmOrder(Long orderId) {
              if (clientConfirmationService.askConfirmation(new Phone(order.phone))) {
              }
              orderService.confirm();
              ticket.resolve();
          }
          

          }

          interface ClientConfirmationService { boolean askConfirmation(Phone phone); }

          class SmartcallsCallService implements ClientConfirmationService { boolean askConfirmation(Phone phone) { return smartcalls.appendToCampaign(phone.withoutPlus()).getAttempt().isSuccess(); } } ```

    • Закон Деметры: каждый должен обращаться только к непосредственным «друзьям»
      • Цепочка вызовов. this.some.foo().bar().baz() намертво привязывает класс к контексту this не просто зависит от some, а some от bar и т.д.
  • Принцип единой ответственности
    • Р. Мартин: Компонент имеет только одну причину для изменения
      • Есть компонент, который подтверждает заказ и закрывает тикет Есть две причины его возможного изменения: 1. Изменение в заказах 2. Изменение в тикетах
    • Объект имеет обязанности связанные только со своим назначением
      • Пример нарушения
        • ```java class BackupService { private MdsService mds;

          public String backup(List<String> filenames) {
              String zipPath = zipFiles(filenames);
              return mds.write(zipPath);
          }
          
          private String zipFiles(List<String> filenames) {
              File tempFile = File.createTempFile("blabla");
              var zip = new Zip(tempFile);
              for (var f : filenames) {
                  zip.add("/", f);
              }
              return tempFile.getName();
          }
          

          } ```

  • Заключение
    • Незнание или несоблюдение принципов, небрежное отношение к зависимостям приводит к тому, что компоненты становятся похожими на узлы, в которые нужно будет вплетать новые связи. В какой-то момент это становится слишком трудным и кажется, что проще узал отрезать и заменить, но что тогда делать с зависимыми?
    • |75%
    • Изучайте принципы проектирования и дизайна, учитесь делать код проще. Будьте счастливы.
  • Практика
    • TASK-001
      • У нас есть List<Long> id - это идентификаторы топиков, нам надо пойти в ручку "/topic/:id", она возвращает String c названием. Надо получить список топиков Topic{id, название}.
    • TASK-002
      • Ручка "/topic/:id" стала возвращать json {id: 22, title: "Бракованный товар", ..."и ещё какие-то поля не нужные нам"...}
    • TASK-003
      • У нас есть список List<Long> id - это идентификаторы пользователей, нам надо сходить в ручку "/user/:id" и получить список User{id, fname, lname} И кажется будут ещё такие же списки для других сущностей
    • TASK-004
      • Мы сделали ещё 10 таких
    • TASK-005
      • Сервис "/user/:id" сообщает, что он перестал справляться с запросами, теперь нужно ходить за пользователями в ручку "/user", она возвращает список всех пользователей в CSV{id, fname, lname}. У нас есть подозрение, что несколько других ручек тоже перейдут в такой режим
Loading page ...

Print options

Expand or collapse list branches

Want checkboxes? Change the list style

List style:

Display or hide list attributes

x

Expand & collapse ec

Import im

Word count wc

Current selection
Words: #{js-wc-sel}
Characters with spaces: #{js-cc-space-sel}
Characters without spaces: #{js-cc-sel}
The whole list
Words: #{js-wc}
Characters with spaces: #{js-cc-space}
Characters without spaces: #{js-cc}

List view options oo

Any email, forwarded to this address, will appear in beginning of this list.

Send an email to yourself and add the sender to Contacts for future use.

  • The email subject becomes the list item's text.
  • The email body becomes the list item's note.
  • All attachments from the email are attached to the list item (PRO only).
  • In the subject, you can also add #tags, ^due dates, and @assignees with Checkvist's smart syntax.

You can also set up voice integration on mobile devices