Лабораторная работа № 2 — «Классы и объекты в Java. Инкапсуляция и ответственность класса»
Цель работы. После ознакомительной ЛР №1 студент впервые проектирует классы самостоятельно. Он должен научиться отличать класс от объекта; создавать поля, методы и конструкторы; использовать this; понимать static и состояние экземпляра; применять public, private и package-private; перегружать конструкторы/методы; организовывать пакеты и объяснять SRP.
Стартовая предметная область — личные финансы. На этом этапе наследование запрещено.
Необходимо разработать:
ru.fa.finance.model Wallet Transaction Category TransactionType ru.fa.finance.service TransactionCalculator ru.fa.finance.app FinanceApp
Минимальный контракт Transaction:
public class Transaction { private final long id; private final BigDecimal amount; private final LocalDate date; private final TransactionType type; private final Category category; private String description; // TODO: конструкторы // TODO: методы доступа // TODO: изменение description с проверкой }
TransactionType должен быть enum:
public enum TransactionType { INCOME, EXPENSE }
Category содержит идентификатор и имя. Wallet содержит название и начальный баланс. На этом этапе студент не должен превращать каждый private в пару бессмысленных setter/getter: неизменяемые свойства остаются final, а изменения состояния выполняются через осмысленные методы.
Практическое задание. Студент создаёт не менее пяти объектов транзакций и два кошелька; реализует два перегруженных конструктора Transaction; не позволяет создать транзакцию с amount <= 0, пустым описанием категории или датой null; выносит форматирование информации в отдельный TransactionPrinter, а не помещает пользовательский интерфейс внутрь Transaction; создаёт пакеты model, service, app; запускает проект через Maven и IDE.
Maven-проект должен следовать стандартному layout:
src/main/java src/main/resources src/test/java
что соответствует официальной структуре Maven.
Эксперимент на инкапсуляцию. Студент сначала пытается выполнить:
transaction.amount = new BigDecimal("-1000");
и фиксирует ошибку компиляции. Затем объясняет, почему запрет прямого изменения состояния полезнее, чем просто соглашение «не трогать поле».
После этого сравниваются два класса:
class BadWallet { public BigDecimal balance; }
и:
class Wallet { private BigDecimal balance; public void deposit(BigDecimal amount) { // проверка и изменение состояния } }
Главный вывод: инкапсуляция — это не “сделать поля private и сгенерировать setters”, а контролировать допустимые изменения объекта.
Эксперимент на SRP. Студенту предлагается «плохой» вариант:
class Transaction { ... void printToConsole() { ... } void saveToFile() { ... } void sendEmail() { ... } }
и требуется объяснить, какие ответственности смешаны и что следует вынести в другие классы.
Что сдаётся: проект; диаграмма классов в простейшем виде; скрин запуска; таблица «поле → почему такой модификатор»; объяснение SRP на своём проекте.
Автоматически проверяемые условия: отрицательная/нулевая сумма отвергается; null в обязательных полях отвергается; поля модели не public; присутствуют перегруженные конструкторы; классы находятся в требуемых пакетах; FinanceApp создаёт и выводит корректные объекты.
Защита: чем класс отличается от объекта; что хранится отдельно в каждом экземпляре; что означает static; зачем this; когда вызывается конструктор; чем перегрузка отличается от переопределения; чем package-private отличается от private; почему публичный setter на каждое поле не равен хорошей инкапсуляции; что нарушает SRP.
Вопросы для погружения:
Чем отличаются класс и объект в Java?Что такое
поля, методы, конструкторы; зачем нужна
перегрузка?Как работают модификаторы доступа
и какие задачи решает инкапсуляция?Зачем
нужны пакеты и как они помогают организации
кода?В чем смысл принципа единственной
ответственности (SRP) при проектировании
классов?
