Существует только одна причина хранить ссылки в обоих направлениях: для производительности.Если вам не нужно хранить и искать большой объем данных, сохраняйте их как можно более простыми.
class Store {
private Manager manager;
public Manager hasManager(Manager manager) {
return this.manager.equals(manager);
}
}
class StoreList {
private final List<Store> stores;
public Stream<Store> getManagedStores(Manager manager) {
return stores.stream().filter(s -> s.hasManager(manager));
}
}
Это автоматически обеспечивает ссылочную целостность, поскольку в хранилище может быть только один менеджер.Он содержит логику управления магазином в списке магазинов, а не в менеджере.Приятно то, что классу Manager не нужно ничего знать о магазинах: один и тот же класс можно использовать для управления ресторанами без каких-либо изменений.
Если вам действительно нужен getStores
вManager
класс, то вам нужно будет реализовать некоторые проверки:
class StoreList {
public boolean hasStore(Store store) {
return stores.contains(store);
}
}
class Manager {
private final StoreList stores;
public boolean hasStoreInStoreList(Store store) {
return stores.hasStore(store);
}
public Stream<Store> getStores() {
return stores.getManagedStores(this);
}
}
class Store {
public void setManager(Manager manager) {
if (manager.hasStoreInStoreList(this))
this.manager = manager;
else
throw new IllegalArgumentException("Manager for different stores");
}
}
Если производительность вызывает беспокойство, то общепринятым подходом является использование кэша.Это хорошо понятный шаблон проектирования, который включает в себя сохранение копии результатов поиска, чтобы избежать необходимости повторять поиск.Кэш очищается, если ссылка изменяется, заставляя поиск выполняться снова.Это может выглядеть примерно так:
class Manager {
private Optional<List<Store>> managedStores = Optional.empty();
public void purgeCache() {
managedStores = Optional.empty();
}
public Stream<Store> getStores() {
if (!managedStores.isPresent())
managedStores = Optional.of(stores.getManagedStores(this)
.collect(Collectors.toList()));
return managedStores.get().stream();
}
}
class Store {
public void setManager(Manager manager) {
if (!manager.equals(this.manager)) {
this.manager.purgeCache();
this.manager = manager;
this.manager.purgeCache();
}
}
}
Наконец, если вы действительно хотите сохранить ссылки в обоих направлениях, вам потребуется каждое обновление, чтобы автоматически изменять ссылку в другом направлении.Это общеизвестно, склонный к ошибкам подход.Как только ваша модель усложняется, становится легко обнаружить трудно обнаруживаемые повреждения ссылок (например, через подкласс, который переопределяет установщик без вызова установщика суперкласса).Избегайте этого, если можете.