Как организовать инкапсуляцию на уровне пакета, чтобы внутри пакета все было доступно, а снаружи запрещено
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Инкапсуляция на уровне пакета в Java
В Java существует четыре уровня доступа, и package-private (по умолчанию) — мощный инструмент для организации инкапсуляции на уровне пакета.
Модификаторы доступа в Java
| Модификатор | Класс | Пакет | Наследник | Вне пакета |
|---|---|---|---|---|
| private | ✓ | ✗ | ✗ | ✗ |
| package-private (нет) | ✓ | ✓ | ✗ | ✗ |
| protected | ✓ | ✓ | ✓ | ✗ |
| public | ✓ | ✓ | ✓ | ✓ |
1. Package-Private (Default Access)
Это идеальный уровень для инкапсуляции на уровне пакета.
Пример структуры
com.example.auth/
├── AuthService.java (public)
├── PasswordValidator.java (package-private)
├── TokenGenerator.java (package-private)
└── JwtParser.java (package-private)
Код
// File: com/example/auth/AuthService.java
package com.example.auth;
public class AuthService {
private PasswordValidator passwordValidator = new PasswordValidator();
private TokenGenerator tokenGenerator = new TokenGenerator();
public boolean authenticate(String username, String password) {
// PasswordValidator доступен внутри пакета
if (!passwordValidator.validate(password)) {
return false;
}
// TokenGenerator доступен внутри пакета
String token = tokenGenerator.generate(username);
return true;
}
}
// File: com/example/auth/PasswordValidator.java
// Без модификатора public = package-private
class PasswordValidator {
boolean validate(String password) {
return password != null && password.length() >= 8;
}
}
// File: com/example/auth/TokenGenerator.java
class TokenGenerator {
String generate(String username) {
return "token_" + username + "_" + System.currentTimeMillis();
}
}
// File: com/example/auth/JwtParser.java
class JwtParser {
String parseToken(String token) {
// Разбор JWT токена
return token.split("_")[1];
}
}
Использование вне пакета
// File: com/example/user/UserService.java
package com.example.user;
import com.example.auth.AuthService;
// PasswordValidator, TokenGenerator недоступны!
import com.example.auth.PasswordValidator; // COMPILE ERROR!
public class UserService {
private AuthService authService = new AuthService();
public void loginUser(String username, String password) {
// ✓ Работает
boolean authenticated = authService.authenticate(username, password);
// ✗ COMPILE ERROR: PasswordValidator не видна
// PasswordValidator validator = new PasswordValidator();
}
}
2. Практический пример: HTTP клиент
Структура
com.example.http/
├── HttpClient.java (public API)
├── Request.java (public DTO)
├── Response.java (public DTO)
├── ConnectionPool.java (package-private внутреннее)
├── RequestSerializer.java (package-private вспомогательный)
└── ResponseParser.java (package-private вспомогательный)
Реализация
// Public API
public class HttpClient {
private ConnectionPool pool = new ConnectionPool();
private RequestSerializer serializer = new RequestSerializer();
private ResponseParser parser = new ResponseParser();
public Response get(String url) {
// Эти классы видны только внутри пакета
Connection conn = pool.getConnection(url);
String serialized = serializer.serialize(new Request(url));
return parser.parse(conn.execute(serialized));
}
}
public class Request {
private String url;
public Request(String url) { this.url = url; }
public String getUrl() { return url; }
}
public class Response {
private int statusCode;
private String body;
// Getters...
}
// Скрыто от внешних пакетов
class ConnectionPool {
Connection getConnection(String url) {
// Сложная логика управления подключениями
return new Connection(url);
}
}
class RequestSerializer {
String serialize(Request request) {
return "{\"url\":\"" + request.getUrl() + "\"}";
}
}
class ResponseParser {
Response parse(String response) {
// Парсинг ответа
return new Response(200, response);
}
}
class Connection {
String execute(String data) { return data; }
}
3. Сложный пример: ORM пакет
// com/example/orm/EntityManager.java (PUBLIC API)
public class EntityManager {
private SessionFactory factory = new SessionFactory();
private QueryBuilder queryBuilder = new QueryBuilder();
public <T> T find(Class<T> entityClass, Long id) {
Session session = factory.createSession();
return queryBuilder.buildSelectQuery(entityClass, id).execute(session);
}
}
// com/example/orm/Session.java (PUBLIC)
public interface Session {
void save(Object entity);
Object load(Class<?> entityClass, Long id);
}
// Внутренние классы (PACKAGE-PRIVATE)
class SessionFactory {
Session createSession() {
return new SessionImpl();
}
}
class SessionImpl implements Session {
@Override
public void save(Object entity) {
// Сложная логика сохранения
}
@Override
public Object load(Class<?> entityClass, Long id) {
// Сложная логика загрузки
return null;
}
}
class QueryBuilder {
Query buildSelectQuery(Class<?> entityClass, Long id) {
return new SqlQuery("SELECT * FROM " + entityClass.getSimpleName() + " WHERE id = " + id);
}
}
class SqlQuery implements Query {
private String sql;
SqlQuery(String sql) { this.sql = sql; }
Object execute(Session session) {
// Выполнение запроса
return null;
}
}
interface Query {
Object execute(Session session);
}
4. Лучшие практики
1. Создавайте фасад (Facade Pattern)
// Один public класс как точка входа
public class DatabaseService {
private ConnectionManager connManager = new ConnectionManager();
private TransactionManager transactionManager = new TransactionManager();
private QueryExecutor queryExecutor = new QueryExecutor();
public void executeTransaction(String sql) {
// Координирует внутренние компоненты
transactionManager.begin();
queryExecutor.execute(sql);
transactionManager.commit();
}
}
// Внутренние вспомогательные классы
class ConnectionManager { }
class TransactionManager { }
class QueryExecutor { }
2. Группируйте по функциям
com.example.ecommerce/
└── order/ // Пакет заказов
├── OrderService.java // PUBLIC
├── Order.java // PUBLIC DTO
├── OrderRepository.java // PUBLIC интерфейс
├── OrderRepositoryImpl.java // PACKAGE-PRIVATE
├── OrderValidator.java // PACKAGE-PRIVATE
└── OrderPriceCalculator.java // PACKAGE-PRIVATE
3. Разделяйте интерфейсы и реализацию
// PUBLIC интерфейс
public interface PaymentProcessor {
boolean process(Payment payment);
}
// PACKAGE-PRIVATE реализация
class StripePaymentProcessor implements PaymentProcessor {
@Override
public boolean process(Payment payment) {
// Специфичная для Stripe логика
return true;
}
}
class PayPalPaymentProcessor implements PaymentProcessor {
@Override
public boolean process(Payment payment) {
// Специфичная для PayPal логика
return true;
}
}
// PUBLIC фабрика
public class PaymentProcessorFactory {
public static PaymentProcessor create(String provider) {
switch (provider) {
case "stripe":
return new StripePaymentProcessor();
case "paypal":
return new PayPalPaymentProcessor();
default:
throw new IllegalArgumentException();
}
}
}
5. Аннотации для документации
/**
* Internal API - not for public use.
* This class is part of internal implementation and can change without notice.
*
* @internal
*/
class InternalHelper {
// ...
}
Преимущества
✓ Инкапсуляция — скрываем реализацию ✓ Сохранение совместимости — можем менять внутренние классы без breaking changes ✓ Чистая архитектура — четкое разделение на public API и internal ✓ Безопасность — запрещаем неправильное использование ✓ Простота — не требует дополнительных библиотек
Недостатки
✗ IDE не может помочь с автодополнением для package-private ✗ Рефлексия может обойти защиту ✗ Требует понимания архитектуры
Вывод
Package-private доступ — мощный инструмент для организации инкапсуляции на уровне пакета. Используйте его для скрытия внутренней реализации и предоставления четкого public API.