Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как скрыть внутреннюю структуру
Инкапсуляция — это один из основных принципов OOP. Скрывание внутренней структуры защищает от неправильного использования и позволяет менять реализацию без влияния на внешний код.
Модификаторы доступа (Access Modifiers)
// public — видно отовсюду
public class PublicClass {
public void publicMethod() { }
}
// default (package-private) — видно только в пакете
class PackagePrivateClass {
void packagePrivateMethod() { }
}
// protected — видно в пакете и наследникам
class Parent {
protected void protectedMethod() { }
}
// private — видно только в этом классе
class PrivateClass {
private void privateMethod() { }
}
Пример: скрытие деталей реализации
// ПЛОХО — внутренняя структура видна
public class BankAccount {
public List<Transaction> transactions = new ArrayList<>();
public double balance = 0;
// Кто-то может напрямую менять баланс!
// account.balance = -1000; // Ошибка, но скомпилируется
}
// ХОРОШО — инкапсуляция
public class BankAccount {
private List<Transaction> transactions = new ArrayList<>();
private double balance = 0;
public double getBalance() {
return balance;
}
public void withdraw(double amount) {
if (amount > balance) {
throw new IllegalArgumentException("Insufficient funds");
}
balance -= amount;
transactions.add(new Transaction("WITHDRAW", amount));
}
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
balance += amount;
transactions.add(new Transaction("DEPOSIT", amount));
}
public List<Transaction> getTransactionHistory() {
// Возвращаем копию, чтобы избежать внешних изменений
return new ArrayList<>(transactions);
}
}
Принцип: возвращай неизменяемые копии
public class User {
private List<String> roles = new ArrayList<>();
private Map<String, String> metadata = new HashMap<>();
// ПЛОХО — вернули ссылку на внутреннее состояние
public List<String> getRoles() {
return roles; // Кто-то может сделать roles.clear()!
}
// ХОРОШО — вернули неизменяемый список
public List<String> getRoles() {
return Collections.unmodifiableList(roles);
}
// ХОРОШО — вернули копию
public List<String> getRolesAsCopy() {
return new ArrayList<>(roles);
}
// ХОРОШО для больших данных — используй Stream
public List<String> getRolesStream() {
return roles.stream()
.collect(Collectors.toUnmodifiableList());
}
public Map<String, String> getMetadata() {
return Collections.unmodifiableMap(metadata);
}
}
Скрытие сложности через Facade паттерн
// Сложная структура — прячем детали
public class DatabaseConnection {
private Connection connection;
private Statement statement;
private ResultSet resultSet;
// Детали скрыты
}
public class PaymentProcessor {
private DatabaseConnection db;
private PaymentGateway gateway;
private EmailService email;
// Детали скрыты
}
// Фасад — скрывает всю сложность
public class CheckoutFacade {
private PaymentProcessor paymentProcessor;
private OrderService orderService;
// Простой публичный метод скрывает всю сложность внутри
public void checkout(Order order, PaymentInfo payment) {
paymentProcessor.processPayment(order.getId(), payment);
orderService.confirmOrder(order);
}
}
// Клиент работает с простым фасадом
checkoutFacade.checkout(order, paymentInfo);
Data Transfer Object (DTO) для скрытия сущности
// Сущность БД — скрыта от клиента
@Entity
@Table(name = "users")
public class UserEntity {
@Id
private Long id;
private String name;
private String email;
private String passwordHash;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// много других деталей реализации
}
// DTO — только нужные данные
public class UserDTO {
private Long id;
private String name;
private String email;
// НЕ ВКЛЮЧАЕМ passwordHash, timestamps, etc.
}
// Контроллер возвращает DTO, не сущность
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/{id}")
public UserDTO getUser(@PathVariable Long id) {
UserEntity entity = userRepository.findById(id).orElseThrow();
// Преобразуем в DTO, скрывая детали
return new UserDTO(
entity.getId(),
entity.getName(),
entity.getEmail()
);
}
}
Builder паттерн для сложных объектов
// Сложный объект со множеством параметров
public class DatabaseConfig {
private final String host;
private final int port;
private final String username;
private final String password;
private final int connectionPoolSize;
private final int timeoutSeconds;
private final boolean sslEnabled;
// Конструктор скрыт
private DatabaseConfig(Builder builder) {
this.host = builder.host;
this.port = builder.port;
this.username = builder.username;
this.password = builder.password;
this.connectionPoolSize = builder.connectionPoolSize;
this.timeoutSeconds = builder.timeoutSeconds;
this.sslEnabled = builder.sslEnabled;
}
// Простой Builder API
public static class Builder {
private String host;
private int port = 5432;
private String username;
private String password;
private int connectionPoolSize = 10;
private int timeoutSeconds = 30;
private boolean sslEnabled = false;
public Builder host(String host) {
this.host = host;
return this;
}
public Builder port(int port) {
this.port = port;
return this;
}
public DatabaseConfig build() {
if (host == null || username == null) {
throw new IllegalStateException("Required fields missing");
}
return new DatabaseConfig(this);
}
}
}
// Клиент не знает деталей конструирования
DatabaseConfig config = new DatabaseConfig.Builder()
.host("localhost")
.port(5432)
.username("admin")
.build();
Интерфейсы для скрытия реализации
// Интерфейс определяет контракт
public interface PaymentService {
void processPayment(BigDecimal amount);
PaymentStatus getStatus(String transactionId);
}
// Реализация скрыта за интерфейсом
class StripePaymentService implements PaymentService {
@Override
public void processPayment(BigDecimal amount) {
// Детали работы со Stripe API
}
}
class PayPalPaymentService implements PaymentService {
@Override
public void processPayment(BigDecimal amount) {
// Детали работы с PayPal API
}
}
// Клиент работает только с интерфейсом
@Configuration
public class PaymentConfig {
@Bean
public PaymentService paymentService() {
return new StripePaymentService();
// Можем менять реализацию, не меняя клиентский код
}
}
Приватные вспомогательные методы
public class OrderProcessor {
// Публичный метод — простой интерфейс
public void processOrder(Order order) {
validateOrder(order);
calculateDiscount(order);
applyTaxes(order);
saveToDatabase(order);
}
// Приватные методы — детали реализации
private void validateOrder(Order order) { }
private void calculateDiscount(Order order) { }
private void applyTaxes(Order order) { }
private void saveToDatabase(Order order) { }
}
Ключевые правила инкапсуляции
- Делай поля приватными — используй public getter/setter
- Возвращай копии — не ссылки на внутреннее состояние
- Используй Collections.unmodifiable* — для защиты списков
- Создавай DTO — для публичных API вместо сущностей
- Используй интерфейсы — чтобы скрыть реализацию
- Builder для сложных объектов — упростить конструирование
- Фасад для сложности — один простой метод вместо много операций
- Валидация в сеттерах — защити инварианты объекта
Хорошая инкапсуляция делает код надёжнее, проще тестировать и менять.