Я делаю проект Java и столкнулся с серьезной проблемой.
В моем проекте у меня много менеджеров и репозиториев.
Я частично решил проблему, обратившись к классу MainApplication
, который содержит все ссылки на репозитории и менеджеры, поэтому, обращаясь к нему, я могу без каких-либо усилий получить каждый нужный мне репозиторий. Упрощенный код выглядит так:
class MainApplication {
private final UserRepository userRepository;
private final GroupRepository groupRepository;
private final PhotoRegistry photoRegistry;
private final UserDAO userDAO;
public MainApplication() {
this.userRepository = new UserRepository(this);
this.groupRepository = new GroupRepository(this);
this.photoRegistry = new PhotoRegistry();
this.userDAO = new UserDAO();
}
public UserRepository getUserRepository() {
return userRepository;
}
public GroupRepository getGroupRepository() {
return groupRepository;
}
public PhotoRegistry getPhotoRegistry() {
return photoRegistry;
}
public UserDAO getUserDAO() {
return userDAO;
}
}
interface ResponseHandler {
void handleResponse(User user, String response);
}
class User {
private final long id;
private final long groupId;
private ResponseHandler responseHandler;
private UserRepository repository;
public User (long id, long groupId, UserRepository repository) {
this.id = id;
this.groupId = groupId;
this.repository = repository;
}
public void processResponse(String response) {
if (this.responseHandler != null) responseHandler.handleResponse(this, response);
}
public void reply(String replyText, byte [] photo) {
// some reply logic
}
public void setResponseHandler(ResponseHandler handler) {
this.responseHandler = handler;
}
public UserRepository getRepository() {
return repository;
}
public long getGroupId() {
return groupId;
}
public long getId() {
return id;
}
}
class Group {
private long id;
private String groupDescription;
public String getDescription() {
return groupDescription;
}
public long getId() {
return id;
}
}
class UserRepository {
private final MainApplication main;
private final Map<Long, User> users;
public UserRepository(MainApplication main) {
this.main = main;
this.users = new HashMap<>();
}
public User createUser(long userId, long groupId) {
User newUser = new User(userId, groupId, this);
this.users.put(newUser.getId(), newUser);
return newUser;
}
public User getUser(long id) {
return users.get(id);
}
public MainApplication getMain() {
return main;
}
}
class GroupRepository {
private final MainApplication main;
private final Map<Long, Group> groups;
public GroupRepository(MainApplication main) {
this.main = main;
this.groups = new HashMap<>();
}
public void addGroup(Group group) {
this.groups.put(group.getId(), group);
}
public Group getGroup(long id) {
return groups.get(id);
}
}
class PhotoRegistry {
private final Map<String, byte[]> photosToName;
public PhotoRegistry() {
this.photosToName = new HashMap<>();
}
public void registerPhoto(String photoName, byte[] data) {
this.photosToName.put(photoName, data);
}
public byte[] getPhoto(String photoName) {
return photosToName.get(photoName);
}
}
class UserDAO {
public double getBalanceFor(long userId) {
double balance = 0;
// retrieving balance from database
return balance;
}
}
и вот простое использование приведенного выше кода:
class Main {
public static void main(String[] args) {
//registering the application
MainApplication main = new MainApplication();
User user = main.getUserRepository().createUser(0, 1);
ResponseHandler handler = new ResponseHandler() {
@Override
public void handleResponse(User user, String response) {
if (!response.equalsIgnoreCase("Hello")) return;
MainApplication main = user.getRepository().getMain();
double userBalance = main.getUserDAO().getBalanceFor(user.getId());
Group userGroup = main.getGroupRepository().getGroup(user.getGroupId());
byte [] attachedPhoto = main.getPhotoRegistry().getPhoto("balancePhoto");
StringBuilder builder = new StringBuilder();
builder.append("Hello user ")
.append(user.getId())
.append("\n")
.append("You are in a group with id: ")
.append(userGroup.getId())
.append(" and description: ")
.append(userGroup.getDescription())
.append("your balance is $")
.append(userBalance);
user.reply(builder.toString(), attachedPhoto);
}
};
user.setResponseHandler(handler);
// supplying a response from user
user.processResponse("Hello");
}
}
Итак, мой вопрос: как мне лучше структурировать этот проект? Нужно ли мне использовать среду внедрения зависимостей, например Spring
или Guice
? На мой взгляд, это можно сделать гораздо лучше, но я понятия не имею, как это сделать, потому что не могу точно предсказать, какие репозитории или менеджеры мне понадобятся в реализации ResponseHandler
.
Я уже пробовал использовать Guice
, но не совсем понимаю, как он сюда подходит. Также я старался предоставлять только необходимые ссылки на UserRepository
вместо передачи всего экземпляра MainApplication
, но в моем проекте гораздо больше репозиториев, чем в этом, поэтому конструктор становится действительно огромным, и с ним нелегко работать.
Итак, мой вопрос: как мне лучше структурировать этот проект?
О структуре программ и архитектуре программного обеспечения написаны целые книги (например, APPP , PEAA), поэтому мы не можем легко ответить на этот вопрос, основываясь на небольшом примере. Тем не менее, вам следует стараться избегать циклических зависимостей, поскольку это обязательно сделает ваш код более сложным, чем он должен быть.
Например, MainApplication
, очевидно, зависит от UserRepository
, но UserRepository
также зависит от MainApplication
. Похоже, то же самое и с GroupRepository
.
По моему опыту, вам никогда не понадобятся циклические зависимости. В APPP Роберт К. Мартин называет это принципом ациклических зависимостей (ADP) (глава 28).
Я моделирую зависимости в виде направленных ациклических графов уже более десяти лет, и мой опыт подсказывает мне, что всегда можно найти альтернативный дизайн, не требующий циклов. Вот пример отчета об опыте.
Нужно ли мне использовать среду внедрения зависимостей, например Spring или Guice?
Нет, вам не нужен DI-контейнер, чтобы воспользоваться преимуществами внедрения зависимостей.