Занятие №2
ООП в Java
Кернер
Денис
Кернер
Денис
О себе
Кернер Денис
Опыт работы:
Технологии, с которыми работал:
Из личного:
Мы будем работать со следующими инструментами:
Unit тесты - обычный Java класс, в котором вы пишете проверки тестируемого вами класса.
Для установки библиотеки Junit ее надо указать в зависимостях build.gradle:
dependencies {
testImplementation('org.junit.jupiter:junit-jupiter:5.5.1')
}
test {
useJUnitPlatform()
}
Чтобы запустить все тесты
в проекте: ./gradlew test
Кот Пушок, 2015 г.р., вес 3.5 кг, любит сметану и спать по 12 часов.
Машина хозяина Пушка, пробег 100т.км, нуждаются в замене подушки двигателя. Умеет разгоняться до 100 км/ч за 11 секунд
ООП предлагает строить программу на основе объектов и взаимодействия между ними с помощью сообщений.
Этот способ отличается от алгоритмической декомпозиции и функциональной парадигмы.
В основе объект - с состоянием и
методами
Автор идеи ООП и языка SmallTalk Алан Кей
Объект - это экземпляр класса. Он обладает уникальностью, а также состоянием.
Состояние - это значения типов данных, заложенных в класс.
Класс, чертеж, шаблон
Уникальная скульптура по шаблону
Классы пишут программисты
Объекты создаются во время работы программы
Чтобы построить модель реального мира, нужно правильно применить абстракцию.
Абстракция - выделение нужного.
В чем сложность? Как понять, что нужно?
Два класса "Кошка" с точки зрения бабушки и хирурга ветеринара
Мы разрабатываем программу для кошачьего отеля.
Владельцы сдают кошек в отель на время отпуска.
Что мы должны учитывать?
Мы разрабатываем сайт для продажи породистых кошек.
Что мы должны учитывать?
Из чего состоит класс породистой кошки на продажу?
public class Cat {
String[] awards;
String passportCode;
String name;
String color;
double weight;
String[] owners;
Long cost;
LocalDate birthDay;
}
Cat leopold = new Cat();
String[] awards = new String[] {"Британец 2016", "Порода 2017г."};
String[] owners = new String[] {"Петр Горевой", "Иван Драго"};
LocalDate birthDay = LocalDate.of(2010, 1, 1);
Cat tom = new Cat(awards, "ID-01121",
"Пушок", "grey", 3.5, owners,
5000L, birthDay);
Объект живет во время работы программы. Его данные хранятся в области памяти, называемой кучей, или heap.
Мы можем обращаться к полям объекта через ссылочную переменную (reference) и оператор точки "."
Важно помнить, что ссылочные переменные - не объекты. Нельзя скопировать содержание объекта, присвоив одну ссылку другой.
Cat tom = new Cat(awards, "ID-01121",
"Пушок", "grey", 3.5, owners,
5000L, birthDay);
System.out.println(tom.color);
tom = leopold;
*Рекомендуется делить методы на команды и запросы.
шаблон CQS от Б. Мейера.
Чтобы отправить сообщение объекту, вы вызываете соответствующий метод класса с аргументами.
Синтаксис - ссылочная переменная + точка + метод и аргументы.
Cat cat = new Cat();
String meowString = cat.speak();
Существуют правила вызова.
Метод класса - это блок кода, отвечающий за обработку сообщений, которые принимает объект.
String speak() {
return paint() + meow;
}
void sleep(int duration, String site) {...}
Cat cat = new Cat();
cat.sleep(120, "Кухня");
Вызов метода с правильными аргументами
Методы должны быть уникальны. Иметь свой "отпечаток", сигнатуру метода.
Компилятор не даст вызвать метод с некорректными аргументами.
Метод состоит из сигнатуры и тела (блок кода)
Сигнатура метода - это модификатор доступа, тип возврата, название метода, параметры
public void sleep(int duration, String site) {
// some code ...
}
***Java class может иметь несколько методов с одним названием, но разным набором параметров. (перегрузка методов).
***Блок кода есть, если метод не абстрактный.
Вам должны позволить вызвать метод, т.к. класс может ограничить доступ к своим методам. Если вы попробуете нарушить это правило, то получите ошибку компиляции.
Зачем это нужно?
private void lickTummy() {
...
Кота можно заставить умываться, но как именно - его дело.
Поэтому часть полей и методов мы закрываем, прячем.
Сокрытие информации необходимо для инкапсуляции
Слово "инкапсуляция" происходит от латинского in capsula — "размещение в оболочке".
Цель - сохранение целостности состояния объекта ("state")
Для этого мы можем применить скрытие информации. Тогда мы сможем защитить внутреннее содержимое объекта.
Если это не сделать, произойдет..
Чтобы обеспечить "капсулу", нужен контракт, между нашим классом и клиентом, который его использует (обычно это другой класс).
Контракт говорит о том, что можно сделать с объектом и что нельзя.
Всё, что вне контракта - мы скрываем, используя модификаторы доступа (information hiding).
В Java такой контракт часто оформляют как интерфейс (interface).
Нет. Состояние можно сломать косвенно.
private String[] owners;
public String[] getOwners() {
return owners; }
@Test
public void testBirthDayChange() {
..
cat.getOwners()[cat.getOwners().length - 1] = "Пушок";
System.out.println("Теперь Пушок сам себе хозяин!");
System.out.println(Arrays.toString(cat.getOwners()));
***Сохранение состояния одна из самых сложных задач в ООП
Теперь Пушок сам себе хозяин!
[Петр Горевой, Пушок]
***Иммутабельность (неизменямость) - один из важнейших приемов в программировании.
Хорошие новости!
К нам пришел крупный магазин собак, желающий работать онлайн, когда сможем подключить его к нашему сайту?
Завтра сможем? У меня показ в понедельник, хотелось бы подготовиться заранее и в пятницу попробовать на демо стенде.
Наша архитектура кото-ориентирована, мы не проектировали ее под собак.
***сегодня четверг
Наследование - механизм переноса состояния и поведения на дочерние классы.
Наследование имеет смысл только при сильной связи между классами объектов.
Связь is a, kind of, рода
Нельзя применять наследование, чтобы просто получить код из базового класса.
Такой код неустойчив и плохо сопровождается.
/**
* Животное для продажи
*/
public class Animal {
public Animal() {}
...
}
public class Cat extends Animal {
@Override
protected String paint() {
...
public class Dog extends Animal {
@Override
protected String paint() {
...
Как нарисовать "животное"?
ключевое слово abstract
@Override поможет компилятору искать метод с такой же сигнатурой в базовом классе. И выдавать ошибку, если его нет.
Иначе вы просто перегрузите метод, а базовое поведение останется не тронуто!
Написать свою реализацию с той же сигнатурой и аннотацией @Override
public class Lion extends Cat, WildAnimal {
Проектировщики Java
решили избежать
Deadly Diamond Problem
из c++
Как заставить класс исполнять несколько контрактов?
public interface Breed {
/**
* Награды
*/
String[] getAwards();
/**
* Код родословной
*/
String getPassportCode();
String getName();
}
public interface Pet {
/** список владельцев
*/
String[] getOwners();
String getName();
}
Породистое животное (с документом)
Питомец
Получаем необходимую гибкость + возможность применения default методов.
Что же с Diamond Problem?
@Override
public int getName() {
return Pet.super.getName();
}
Java статически типизирован. Мы заранее указываем тип, с которым работаем.
Должны ли мы создавать отдельную обработку для всех наследников базового класса?
Как бы нам уметь обращаться с многими наследниками как с одним классом, имеющим множество форм!?
Полиморфизм — (греч.) имеющий много форм.
Способность переменной /метода/ класса принимать значения различных типов.
Мы хотим один раз написать код, опираясь на методы и свойства базового класса.
У наследников поведение методов можно изменить.
Класс, который будет выполнять нужный нам метод, определится во время выполнения (позднее связывание).
Таким образом, мы можем расширять поведение системы через добавление новых классов, не меняя основной код, обрабатывающий объекты базового класса.
Upcasting:
Animal animal = (Animal) dog;
Dog dog = (Dog) animal;
Downcasting:
На уровне классов:
Animal animalCat;
animalCat = (Animal) cat;
animalCat.paint();
,_ _
|\\_,-~/
/ _ _ | ,--.
( @ @ ) / ,-'
\ _T_/-._( (
/ `. \
| _ \ |
\ \ , / |
|| |-_\__ /
((_/`(____,-'
upcasting
__ _
o'')}____//
`_/ )
(_(_/-(_/
Animal animalDog;
animalDog = (Animal) dog;
animalDog.paint();
Интерфейсы тоже позволяют использовать полиморфизм
public void viaInterfaces() {
Breed breedCat = new Cat(/*параметры*/);
breedCat.getPassportCode();
}
"ID-232323"
Можно смело идти и писать код?
Давайте узнаем об еще одном типе связей между классами - композиции
Для того, чтобы работать с животными домашними и дикими, создадим интерфейсы порода, питомец. И классы их реализующие.
Теперь мы можем создать класс кошка и собака, со свойствами Breed и Pet, не дублируя код
Также мы можем создать льва, который на выставках не выступает и питомцем не состоит
Или породистого бегемота, выступающего на выставках самостоятельно и без владельцев.
У вас есть:
Оружие. Наносит физический урон и дополнительный урон огнем или холодом.
Урон - это объект со свойствами (физический урон\ урон огнем\ урон холодом)
Ваша задача сделать новое оружие - "меч ночи в якутске". Этот меч наносит дополнительный урон холодом (50)
Так же создайте меч "пылающий асфальт", дающий урон огнем 50.
Потом создайте наследников от класса цели - ледяной великан, иммунитет к холоду. И эфрит - иммунитет к огню.
*** (необязательно) Напишите тесты, проверяющие, что получен ожидаемый урон.
Прислать письмо со ссылкой на проект нам, на эл. почту:
checkhomework.sis@gmail.com
Формат письма:
Тема: Имя Фамилия, номер домашнего задания.
В тексте письма ссылка на репозиторий
Крайний срок: 12 марта 2020 года.
В презентации использованы иллюстрации из книги Гради Буча 'Объектно-ориентированное проектирование и анализ'