Тема 4. Objects and Classes
Когда код становится больше пары файлов, процедурный стиль начинает ломаться: данные и действия расходятся по проекту, поведение сложно менять безопасно. Объектно-ориентированный подход решает это через классы: состояние и поведение объединяются в одной модели.
Introduction to Object-Oriented Programming
В ООП программа выглядит как набор объектов, которые обмениваются сообщениями через методы.
Плюс подхода: каждый объект отвечает за свою часть задачи. Например, Order считает итоговую сумму заказа, а EmailService отправляет уведомление. Это снижает связность и упрощает изменения.
Using Existing Classes
В реальном проекте вы почти всегда сочетаете свои классы с готовыми (String, List, Map, LocalDate, классы фреймворка).
Ключевой навык: читать контракт класса. Нужно понимать:
- Какие входные данные допустимы.
- Что метод возвращает в обычном случае.
- Какие исключения возможны.
- Какие есть побочные эффекты.
Building Your Own Classes
Хороший класс начинается с инвариантов: какие состояния считаются валидными всегда.
public class Student {
private final String name;
private int score;
public Student(String name, int score) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("name is required");
}
if (score < 0) {
throw new IllegalArgumentException("score must be >= 0");
}
this.name = name;
this.score = score;
}
public void addScore(int delta) {
if (delta > 0) {
score += delta;
}
}
public int getScore() {
return score;
}
}
Static Fields and Methods
static означает, что поле или метод принадлежит классу, а не конкретному объекту.
public class IdGenerator {
private static long next = 1;
public static long nextId() {
return next++;
}
}
Используйте static для констант, фабрик и утилит. Не храните в static случайное изменяемое бизнес-состояние, иначе код сложнее тестировать.
Method Parameters
Параметры метода это входной контракт метода.
Что важно в теории
- Тип параметра ограничивает допустимые входные данные.
- Имя параметра должно отражать доменный смысл (
amount,birthDate,studentId), а не технический шум (x,obj1). - Слишком длинный список параметров ухудшает читаемость и повышает риск ошибок.
Как Java передает параметры
Java всегда передает по значению:
- Для примитива копируется само значение.
- Для объекта копируется значение ссылки.
Это значит:
- Метод не может заменить переменную вызывающего кода на другой объект.
- Метод может изменить состояние переданного объекта, если у объекта есть изменяемые поля.
public class ParamDemo {
static void changeInt(int n) {
n = 99;
}
static void changeName(Student s) {
s.addScore(10);
}
}
changeInt не изменит исходную переменную снаружи. changeName изменит состояние объекта Student, на который ссылаются обе переменные.
Практические шаги при проектировании метода
- Определите минимально необходимый набор входных данных.
- Если параметров больше 3-4, подумайте об отдельном объекте запроса.
- Зафиксируйте предусловия (null-check, диапазоны, формат).
- Явно определите, может ли метод менять состояние переданных объектов.
Object Construction
Цель конструктора: создать объект сразу в валидном состоянии.
Плохой путь для новичка: создать «пустой» объект, а потом по частям донастраивать через сеттеры. В таком подходе объект может жить в поломанном промежуточном состоянии.
Хороший базовый алгоритм:
- Определите обязательные поля.
- Передайте их в конструктор.
- Проверьте входные данные в конструкторе.
- Если данные невалидны, выбросьте понятное исключение.
- После конструктора объект должен быть готов к использованию.
Пример:
public class BankAccount {
private final String owner;
private long balance;
public BankAccount(String owner, long initialBalance) {
if (owner == null || owner.isBlank()) {
throw new IllegalArgumentException("owner is required");
}
if (initialBalance < 0) {
throw new IllegalArgumentException("initialBalance must be >= 0");
}
this.owner = owner;
this.balance = initialBalance;
}
}
Для сложных объектов позже применяют Builder, но сначала нужно уверенно владеть «валидным конструктором».
Packages
package это способ организовать классы по смыслу, а не просто по папкам. Пакеты решают две задачи:
- Логическая структура проекта (где искать нужный класс).
- Границы видимости (
public, package-private,protected).
Базовая модель для новичка
Представьте проект как город:
- Пакет это район (
controller,service,repository,model). - Класс это дом в районе.
importэто адрес, по которому другой класс находит нужный дом.
Пример структуры:
com.course.courseweb
├── controller
│ └── ArticleController
├── service
│ └── ArticleService
├── repo
│ └── ArticleRepository
└── model
└── Article
Как это работает в коде
В начале файла объявляется пакет:
package com.course.courseweb.service;
Для использования класса из другого пакета нужен import:
import com.course.courseweb.model.Article;
Практическое правило: группируйте классы по ответственности (feature/layer), и пусть по имени пакета сразу видно роль класса.
Documentation Comments
Javadoc нужен для публичного API классов и методов. Полезно описывать:
- Назначение метода.
- Ограничения входных параметров.
- Что возвращается.
- Какие исключения и в каких случаях.
Class Design Hints
- Один класс должен решать одну основную задачу.
- Инварианты проверяются в конструкторе и/или ключевых методах.
- Публичный API класса должен быть минимальным и понятным.
- Поля по умолчанию делайте
private, открывайте доступ только при необходимости. - Предпочитайте говорящие имена классов, методов и параметров.
- Избегайте «бог-объектов», которые знают и делают слишком много.
- Если метод трудно понять без комментариев, сначала упростите сам метод.