Наследование
Алмаз смерти
Наследование
Ключевое слово super
Ключевое слово super
Ключевое слово super
Полиморфизм
Переопределение методов
Перегрузка и переопределение
Переопределение методов
Переопределение методов
Запрет переопределения
Запрет наследования
String, StringBuilder, StringBuffer
String, StringBuilder, StringBuffer
String, StringBuilder, StringBuffer
Почему они объявлены как final?
Абстракция
Абстрактные классы
Модификатор abstract
Интерфейсы
Интерфейсы
Интерфейсы
Интерфейсы
Чем отличается абстрактный класс от интерфейса?
Когда и что выбирать?
Когда и что выбирать?
Бывают ли интерфейсы без методов?
Вложенные классы
Вложенные классы
Класс Object
Класс Object
Класс Object
Класс Object
Класс Object
Метод toString()
Метод toString()
Java Record
Ошибки компиляции и ошибки времени выполнения
Ошибки компиляции и ошибки времени выполнения
Ошибки компиляции и ошибки времени выполнения
Исключения
Исключения
Исключения
Исключения
Исключения
Исключения
Исключения
Исключения
Исключения
Создание своих собственных исключений
Создание своих собственных исключений
Создание своих собственных исключений
Создание своих собственных исключений
Паттерны проектирования
Зачем знать паттерны?
Зачем знать паттерны?
7.00M
Категория: ПрограммированиеПрограммирование

Тема 3

1.

Тема 3
Наследование и интерфейсы.
Обработка исключительных
ситуаций
Макейчик Е.Г.

2. Наследование

Наследование – механизм,
позволяющий одному классу
(подклассу) перенимать
свойства и методы другого
класса (суперкласса)
extends – используется для
создания подкласса. В Java
разрешено наследование
только от одного
родительского класса
(избегаем «алмаза смерти»)
Макейчик Е.Г.
2

3. Алмаз смерти

Макейчик Е.Г.
3

4. Наследование

Класс Person становится суперклассом для класса Employee,
т.е. Employee расширяет возможности Person
Макейчик Е.Г.
4

5. Ключевое слово super

В Java ключевое слово super – это ссылка на объект родительского класса. Оно
используется в трех основных случаях:
1. Вызов конструктора родителя. А вот и третий способ задействовать
конструктор!
Это самое частое использование. Если у родителя есть конструктор с
параметрами, подкласс обязан его вызвать с помощью super(...), так как
создание «ребенка» невозможно без инициализации «родителя». Важно:
Вызов super() должен быть самой первой строкой в конструкторе подкласса
Макейчик Е.Г.
5

6. Ключевое слово super

2. Вызов метода родителя
Если вы переопределили метод в подклассе (Override), но хотите внутри него
использовать логику из родительского метода, используется super.имяМетода()
3. Доступ к полю родителя
Используется редко (обычно если имена полей в родителе и потомке совпадают),
чтобы уточнить, что мы обращаемся именно к полю родительского класса
Макейчик Е.Г.
6

7. Ключевое слово super

Разница между this и super:
this – обращается к текущему объекту данного класса
super – обращается к части текущего объекта, которая относится к родительскому классу
Ошибка компиляции возникнет, если в классе Person есть конструктор, а в Student вы забуете
написать super(...). Это происходит из-за правила цепочки конструкторов (Constructor Chaining).
Почему возникает ошибка?
1. Неявный вызов: Если вы не пишете super() вручную, Java автоматически вставляет его первой
строкой в конструктор подкласса. Это выглядит как невидимый super(); без аргументов
2. Отсутствие конструктора по умолчанию: В вашем классе Person вы вручную создали
конструктор
public Person(String name, int born). Как только вы создаете любой свой конструктор, Java
перестает автоматически создавать пустой конструктор по умолчанию (Person())
3. Конфликт: Когда вы создаете наследника Student, Java пытается вызвать super(), но в классе
Person его не существует
Итог: Компилятор выдает ошибку: Implicit super constructor Person() is undefined
Макейчик Е.Г.
7

8. Полиморфизм

Полиморфизм – это «один интерфейс, много реализаций»!
Статический (Перегрузка):
Метод print(int) и print(String). Название одно, но Java еще при записи кода
знает, какую «версию» вызвать, глядя на то, что ты передал (число или текст).
Место действия только внутри одного класса
Динамический (Переопределение):
Механизм, посредством которого разрешение обращения к
переопределенному методу осуществляется во время выполнения, а не во
время компиляции. Динамическая диспетчеризация методов важна потому,
что именно с ее помощью Java реализует полиморфизм времени выполнения
Макейчик Е.Г.
8

9. Переопределение методов

Переопределение метода (overriding) – создание в подклассе метода,
совпадающего по сигнатуре и возвращаемым значением
Если переопределенный метод вызывается из подкласса, то получает
управление тот вариант метода, который был определен в подклассе.
Вариант метода, определенный в суперклассе, оказывается
замаскированным. Это полное изменение логики метода родительского
класса в классе-наследнике.
Где происходит: В разных классах (связанных наследованием)
Правило: Имя, аргументы и возвращаемый тип должны быть идентичны
Зачем нужно: Чтобы наследник выполнял ту же задачу, но по-своему
Макейчик Е.Г.
9

10. Перегрузка и переопределение

Можно ли менять модификатор доступа метода,
если да, то каким образом?
Макейчик Е.Г.
10

11. Переопределение методов

Строгое правило: нельзя сужать доступ, но можно расширять
Модификатор доступа в подклассе должен быть таким же или «более
открытым», чем в родительском классе
Если в родителе protected: в наследнике можно сделать protected
или public. Нельзя сделать private или default (package-private)
Если в родителе public: в наследнике обязан быть только public
Если в родителе default: в наследнике можно default, protected или
public
Макейчик Е.Г.
11

12. Переопределение методов

Зачем это нужно?
Это обеспечивает принцип подстановки Барбары Лисков (LSP): если
программа ожидает объект Person и вызывает его публичный метод,
она должна иметь возможность вызвать этот же метод и у Employee, не
столкнувшись с ошибкой доступа
Макейчик Е.Г.
12

13. Запрет переопределения

Для того чтобы предотвратить (запретить) переопределение
метода, надо при его объявлении указать модификатор final
Используется, когда нужно гарантировать, что определенная
логика останется неизменной во всех подклассах
Макейчик Е.Г.
13

14. Запрет наследования

Если класс помечен как final, от него нельзя создать наследника
(сделать extends)
Интересный факт: Класс String в Java является final, поэтому
никто не может создать свой «улучшенный» String
Макейчик Е.Г.
14

15. String, StringBuilder, StringBuffer

Эти три класса используются для работы со строками, но под
капотом они устроены совершенно по-разному
Макейчик Е.Г.
15

16. String, StringBuilder, StringBuffer

String – используй для обычного хранения текста. Любое
изменение (например, + "мир" ) создает новый объект в памяти, а
старый выбрасывается
StringBuilder – используй, если нужно много раз менять строку в
одном потоке (например, в цикле). Он меняет текст внутри того же
объекта
StringBuffer – то же самое, что и StringBuilder, но для работы в
многопоточных программах (чтобы данные не перемешались при
одновременном доступе)
Макейчик Е.Г.
16

17. String, StringBuilder, StringBuffer

Примерные результаты
(зависят от мощности ПК):
String (+):
~3000–5000 мс (несколько секунд)
StringBuilder:
~2–5 мс (почти мгновенно)
Макейчик Е.Г.
17

18. Почему они объявлены как final?

Все три класса помечены ключевым словом final, чтобы запретить наследование от них. Это
сделано по трем причинам:
1. Безопасность (Security)
Строки используются везде: в качестве ключей в HashMap, для хранения паролей, путей к
файлам или URL-адресов. Если бы можно было наследоваться от String, злоумышленник мог
бы создать свой класс-наследник, переопределить методы и подменить критически важные
данные (например, изменить путь к файлу уже после того, как программа его проверила)
2. Пул строк (String Pool)
Java экономит память, сохраняя одинаковые строки в специальном пуле. Если бы строки
можно было менять или наследовать, кэширование стало бы невозможным: изменив «одну»
строку, вы могли бы случайно изменить ее во всех частях программы, где используется такое
же слово
3. Потокобезопасность
Неизменяемые (final) объекты можно смело передавать между потоками, не боясь, что один
поток изменит данные, пока другой их читает
Макейчик Е.Г.
18

19. Абстракция

Абстракция – это один из четырех столпов объектно-ориентированного
программирования (ООП), который заключается в выделении значимых
характеристик объекта и отбрасывании второстепенных деталей
Простыми словами: абстракция отвечает на вопрос «что делает объект?», не
уточняя, «как именно он это делает»
В Java абстракция реализуется двумя основными способами:
Абстрактные классы (abstract class): позволяют создать частичный шаблон. Они могут
содержать как готовые методы с кодом, так и абстрактные методы без реализации
Интерфейсы (interface): представляют собой «чистую» абстракцию (контракт). Они
описывают только набор действий, которые класс обязан уметь выполнять, но не хранят
логику их выполнения
Макейчик Е.Г.
19

20. Абстрактные классы

В Java существуют абстрактные классы, которые можно использовать
только для наследования, но не для создания экземпляров (объектов)
Ключевые характеристики и правила
Запрет на создание объектов: Нельзя написать new MyAbstractClass(). Попытка
инстанцирования вызовет ошибку компиляции
Смешанное содержимое: В отличие от интерфейсов, абстрактный класс может
содержать как абстрактные методы (без тела), так и обычные методы с реализацией
Состояние (Поля): Абстрактные классы могут иметь любые поля (переменные
экземпляра), тогда как в интерфейсах допустимы только константы (public static final)
Конструкторы: У абстрактного класса может и должен быть конструктор. Он используется
для инициализации общих полей и вызывается из дочерних классов через super()
Обязательства наследников: Первый «конкретный» (неабстрактный) класс, наследующий
абстрактный, обязан реализовать все его абстрактные методы
Макейчик Е.Г.
20

21. Модификатор abstract

Макейчик Е.Г.
21

22. Интерфейсы

Интерфейсы – это особый вид классов, абстрактный тип,
который определяет «контракт» поведения
1.
Интерфейсы допускают множественное наследование
2.
Все методы – абстрактные (без модификатора abstract)
3.
Все переменные – static и final (без соотв.
модификаторов), необходима инициализация
4.
Все переменные и методы – public (без модификатора)
Макейчик Е.Г.
22

23. Интерфейсы

Особенности реализации интерфейсов:
1.
Методы, которые реализуют интерфейс, должны быть
объявлены как public. Сигнатура типа реализующего метода
должна точно соответствовать сигнатуре типа, указанной в
определении интерфейса
2.
Если класс включает интерфейс, но реализует не все его методы,
то такой класс должен быть объявлен как абстрактный
3.
Если класс реализует интерфейс, унаследованный от другого
интерфейса, класс должен реализовать все методы,
определенные в цепочке наследования интерфейсов
Макейчик Е.Г.
23

24. Интерфейсы

Класс реализующий интерфейс может содержать собственные методы
Макейчик Е.Г.
24

25. Интерфейсы

Интерфейсы можно использовать для импорта в различные классы
совместно используемых констант
Такой подход (Constant Interface Pattern) сейчас считается устаревшим.
В современном Java-коде для таких целей используют перечисления (enum)
Макейчик Е.Г.
25

26. Чем отличается абстрактный класс от интерфейса?

Макейчик Е.Г.
26

27. Когда и что выбирать?

Выбираем Абстрактный класс, если:
Есть тесная связь «является» (IS-A). Например: Собака – это
Животное
Нужно общее состояние (поля, которые меняются: age, weight)
Нужно реализовать часть логики, которую наследники будут
использовать «как есть», без изменений
Важно ограничить доступ к полям (protected или private)
Макейчик Е.Г.
27

28. Когда и что выбирать?

Выбираем Интерфейс, если:
Есть связь «умеет делать» (CAN-DO). Например: Смартфон и
Книга могут быть «Прочитаны», хотя они не родственники
Нужно обеспечить множественное наследование (объект
должен уметь и «летать», и «плавать»)
Ты создаешь «контракт»: тебе не важно, как это будет сделано,
важно, чтобы метод просто был
Макейчик Е.Г.
28

29. Бывают ли интерфейсы без методов?

Да, они называются Маркерными интерфейсами (Marker Interfaces)
Для чего они нужны:
Они не добавляют поведения, а просто «помечают» класс для JVM или библиотек,
сообщая им о наличии какого-то свойства
Популярные примеры в Java:
Serializable – говорит Java, что объект этого класса можно превратить в поток
байтов (записать в файл)
Cloneable – разрешает использовать метод clone() для создания копии объекта
Remote – указывает, что методы объекта могут быть вызваны удаленно
В современной Java маркерные интерфейсы часто заменяются аннотациями
(например, @Entity), но классические примеры до сих пор активно используются
Макейчик Е.Г.
29

30. Вложенные классы

Вложенные или внутренние классы (inner class) – это
классы, определенные внутри другого класса
Типы вложенных классов:
• Обычный внутренний класс (regular inner class or
member class)
• Локальный класс (method-local inner class)
• Статический вложенный класс (static nested class)
• Анонимный класс (anonymous inner class)
Макейчик Е.Г.
30

31. Вложенные классы

Макейчик Е.Г.
31

32. Класс Object

В Java «базовым классом» (прародителем) для всех без исключения
объектов является класс java.lang.Object. Даже если вы не пишете extends
Object, Java делает это за вас автоматически
Что здесь происходит:
Полиморфизм в действии: Переменная типа Object может хранить ссылку на объект
любого класса (в данном случае Cat), потому что любой класс в Java является
наследником Object
Ограничение доступа: Несмотря на то что в памяти лежит полноценный объект «Кот»,
через переменную obj вам будут доступны только методы класса Object (toString(),
hashCode(), equals() и т.д.). Вы не сможете вызвать метод obj.meow(), так как компилятор
видит эту переменную просто как абстрактный «Объект»
Зачем это нужно: Это позволяет писать универсальный код. Например, метод, который
принимает Object, сможет работать и с Котами, и с Кораблями, и со Строками
одновременно
Макейчик Е.Г.
32

33. Класс Object

Методы класса:
toString()
Зачем: Возвращает строковое представление объекта
По умолчанию: Выдает имя класса и хэш-код (например,
MyClass@15db9742)
Рекомендация: Почти всегда переопределяется (@Override), чтобы
выводить понятные данные (например, имя и возраст человека)
Макейчик Е.Г.
33

34. Класс Object

Методы класса:
equals(Object obj)
Зачем: Сравнивает объекты на логическое равенство
По умолчанию: Сравнивает ссылки (т.е. проверяет, один ли это и тот же
объект в памяти, аналогично оператору ==)
Рекомендация: Переопределяйте, если вам нужно, чтобы два разных
объекта с одинаковыми данными (например, два одинаковых ID)
считались равными
Макейчик Е.Г.
34

35. Класс Object

Методы класса:
hashCode()
Зачем: Возвращает целое число – «цифровой отпечаток» объекта
Правило: Если вы переопределили equals, вы обязаны
переопределить hashCode. Они работают в паре, особенно в
коллекциях типа HashMap
Макейчик Е.Г.
35

36. Класс Object

Методы класса:
getClass()
Зачем: Возвращает информацию о классе объекта во время выполнения (имя класса,
методы и т.д.)
Особенность: Его нельзя переопределить (final)
clone()
Зачем: Создает копию объекта
Особенность: Работает только если класс реализует интерфейс-маркер Cloneable
wait(), notify(), notifyAll()
Зачем: Используются для синхронизации потоков. Они позволяют одному потоку
«ждать» сигнала от другого
Макейчик Е.Г.
36

37. Метод toString()

toString()
важныйпозволяет
метод, возвращающий
значение
объекта
в виде
Метод toString()
получить текстовое
описание
любого
символьной
строки
объекта. Если
в классе не переопределен метод toString(), то будет
вызван метод toString() класса Object
Очень часто при использовании метода toString() для получения описания объекта
можно увидеть набор бессмысленных символов, например, [I@421199e8
Макейчик Е.Г.
37

38. Метод toString()

Если нужнаважный
соответствующая
информация об объекте
b, тогда
нужно в виде
toString()
метод, возвращающий
значение
объекта
переопределить
метод toString() класса Object в классе Best_Friend
символьной
строки
Макейчик Е.Г.
38

39. Java Record

Фишка Record
(появилась
в Java возвращающий
14+) в том, что она автоматически
создает поля,
Почему
это важный
круто?
toString()
метод,
значение объекта
в виде
конструктор,
геттеры
самое
главное,
правильные
Никакого
лишнего
кода:и,Тебе
не нужно
писать
this.name =методы
name, toString(), equals() и
символьной
строки
hashCode()
переопределять
toString() или equals() вручную. Java делает это «под капотом»
Неизменяемость: Поля в рекордах по умолчанию final. Это делает данные защищенными (их
Весь твой
код превращается
одну строку
нельзя
случайно
изменить послевсоздания)
Компактность: Идеально подходит для классов, которые просто хранят данные (DTO)
Макейчик Е.Г.
39

40. Ошибки компиляции и ошибки времени выполнения

Это фундаментальное различие в жизни
любого разработчика. Если вкратце: первые
не дают программе «родиться», вторые
«убивают» ее в процессе работы
Макейчик Е.Г.
40

41. Ошибки компиляции и ошибки времени выполнения

Ошибки компиляции (Compile-time errors)
Это ошибки, которые находит компилятор еще до того, как программа запустится. Если они
есть, JAR-файл или .class просто не создастся
Причина: Нарушение правил языка (синтаксиса)
Примеры:
Забыл точку с запятой ;
Перепутал имя переменной
Пытаешься положить String в int
Не реализовал метод интерфейса
Когда узнаешь: Сразу. Современные IDE (IntelliJ IDEA) подсвечивают их красным прямо во
время ввода кода
Макейчик Е.Г.
41

42. Ошибки компиляции и ошибки времени выполнения

Ошибки времени выполнения (Runtime errors / Exceptions)
Программа успешно скомпилировалась, запустилась, но в какой-то момент «упала» (crash)
Причина: Логические ошибки или непредвиденные ситуации с внешними данными
Примеры:
ArithmeticException: Деление на ноль
NullPointerException: Попытка вызвать метод у объекта, которого нет (null)
ArrayIndexOutOfBoundsException: Обращение к 10-му элементу массива, в котором их
всего 5
ClassCastException: Неудачная попытка превратить «Собаку» в «Кота» (тот самый кастинг)
Когда узнаешь: Только когда программа дойдет до «проблемного» места во время работы
Макейчик Е.Г.
42

43. Исключения

Исключение в Java – это объект некоторого класса, который
описывает исключительное состояние, возникшее в каком-либо
участке программного кода
При возникновении исключения исполняющая система Java
создает объект класса, связанного с данным исключением. Этот
объект хранит информацию о возникшей исключительной
ситуации (точка возникновения, описание и т.п.)
Возможна как автоматическая так и программная генерация
исключений
Макейчик Е.Г.
43

44. Исключения

Что это значит на практике (простыми словами)
Это объект: Когда в программе что-то идет не так (например, деление на ноль), Java
не просто «ломается», она мгновенно создает объект (например, ArithmeticException)
Зачем нужен этот объект: В нем записан «бортовой журнал» аварии: в какой строке
кода это случилось и почему
Кто создает:
Автоматически: Сама Java (например, если закончилась память или индекс
массива вышел за границы)
Программно: Вы сами можете выбросить исключение с помощью слова throw,
если данные не соответствуют вашим правилам (например, пользователь ввел
отрицательный возраст)
Макейчик Е.Г.
44

45. Исключения

В языке Java исключения образуют иерархию классов
Object
Throwable
Error
Exception
Проверяемые
исключения
Макейчик Е.Г.
Непроверяемые
исключения
45

46. Исключения

Exception (Исключения)
Это ситуации, которые возникают в твоем коде. Их можно и нужно
обрабатывать. Они делятся на два типа
Проверяемые (Checked)
Компилятор заставляет тебя их обрабатывать. Если ты вызываешь метод,
который может бросить такое исключение, код не скомпилируется, пока ты не
напишешь try-catch
Причина: Внешние факторы, на которые ты не можешь повлиять на 100 %
Пример: IOException (файл, который ты хочешь прочитать, внезапно удалили)
Макейчик Е.Г.
46

47. Исключения


Непроверяемые (Unchecked / Runtime)
Это исключения, которые наследуются от класса RuntimeException.
Компилятор не заставляет их ловить
Причина: Ошибки самого программиста (логические ошибки)
Примеры: NullPointerException (обращение к пустому объекту),
ArithmeticException (деление на ноль)
Как бороться: Вместо try-catch такие ошибки лучше исправлять
качественным кодом (например, добавить проверку if (object != null)).
Но мы будем тренироваться на них!
Макейчик Е.Г.
47

48. Исключения

Конструкция для обработки (try-catch-finally)
try – «попытаться». Сюда мы помещаем опасный код, который
может сломаться
catch – «поймать». Этот блок сработает только в том случае, если в
try произошла ошибка. Здесь мы ее исправляем или записываем в
лог
finally – «в любом случае». Код в этом блоке выполнится всегда: и
если все прошло хорошо, и если случилась ошибка. Обычно здесь
закрывают файлы или сетевые соединения
Макейчик Е.Г.
48

49. Исключения

Ключевые слова для передачи ошибки (throw, throws)
throw – используется, когда вы хотите вручную создать
(«выбросить») ошибку. Например: if (age < 0) throw new Exception();
throws – используется в заголовке метода, чтобы честно
предупредить других разработчиков: «Внимание, этот метод может
быть опасен и выкинуть исключение, будьте готовы его ловить»
Макейчик Е.Г.
49

50. Исключения

Примеры разрешенных конструкций:
try { } catch { } – классика: пробуем и ловим
try { } catch { } finally { } – полный цикл: пробуем, ловим и
прибираем за собой
try { } finally { } – мы не ловим ошибку (она полетит дальше), но
обязательно выполняем очистку ресурсов в конце
Важный нюанс: блок finally выполнится даже если внутри try или
catch стоит команда return. Это единственный способ
гарантированно завершить дела перед выходом из метода
Макейчик Е.Г.
50

51. Исключения

Пример
В современной Java (начиная с версии 7) появилась конструкция
try-with-resources, которая делает эту «приборку» автоматически без
блока finally
Макейчик Е.Г.
51

52. Создание своих собственных исключений

Создание собственных исключений – это способ сделать ошибки в
твоей программе «говорящими». Вместо стандартного Exception ты
можешь выбросить ошибку, которая точно объясняет, что пошло не
так
Как это сделать (3 простых шага):
Макейчик Е.Г.
Наследование: Твой класс должен наследоваться от Exception
(если хочешь проверяемое) или от RuntimeException (если
непроверяемое)
Конструктор: Обычно создают два конструктора: пустой и тот,
что принимает сообщение об ошибке
Использование: Выбрасываешь его через throw
52

53. Создание своих собственных исключений

1. Наследование от Exception (Checked)
Это «принудительная» ошибка. Java считает ее настолько важной,
что не даст программе запуститься, пока ты не обработаешь ее
(через try-catch) или не пробросишь дальше (через throws)
Когда использовать: Когда ошибка ожидаема и ее нужно
обработать (например, файл не найден или сервер недоступен)
Макейчик Е.Г.
53

54. Создание своих собственных исключений

2. Наследование от RuntimeException (Unchecked)
Это «мягкая» ошибка. Компилятор не заставляет тебя ее ловить
или прописывать в заголовке метода. Код выглядит чище, но если
ошибка случится и ее никто не поймает – программа просто
«упадет»
Когда использовать: Когда ошибка – это результат косяка
программиста (неверная логика, пустой объект) и ее не стоит
ловить, а нужно просто исправить код
Макейчик Е.Г.
54

55. Создание своих собственных исключений

Что выбрать?
Современный тренд в Java (и в таких фреймворках как Spring) – использовать RuntimeException.
Это делает код более лаконичным, а ошибки обрабатываются глобально в одном месте, вместо
того чтобы загромождать каждый метод блоками try-catch
Макейчик Е.Г.
55

56. Паттерны проектирования

Паттернами проектирования (Design Patterns) называют решения
часто встречающихся проблем в области разработки программного
обеспечения. В данном случае предполагается, что есть некоторый
набор общих формализованных проблем, которые довольно часто
встречаются, и паттерны предоставляют ряд принципов для
решения этих проблем
Концепцию паттернов впервые описал Кристофер Александер в
книге «Язык шаблонов. Города. Здания. Строительство»
Макейчик Е.Г.
56

57. Зачем знать паттерны?

• Паттерны упрощают проектирование и
поддержку программ
• Проверенные решения
• Код более предсказуем когда используются
готовые решения, вместо повторного
изобретения велосипеда
• Стандартизация кода
Макейчик Е.Г.
57

58. Зачем знать паттерны?


Меньше ошибок, так как используются типовые
унифицированные решения, в которых
давно найдены все скрытые проблемы
Общий язык
Произносится название паттерна, вместо
того, чтобы час объяснять другим членам
команды какой подход нужно применить и
какие классы для этого нужны
Макейчик Е.Г.
58
English     Русский Правила