Изоляция
Проблема вызвана недостаточной изоляцией различных подсистем.С точки зрения подсистемы S
, тип X
со временем изменил свой контракт.В прошлом тип давал S
возможность контролировать все свои экземпляры.Но с интеграциями контроль экземпляров был потерян.Тип X
изменил свое поведение несовместимым образом.
Подсистема S
должна была быть более изолированной.Он не должен использовать типы, которые могут конфликтовать с другими системами.Вместо этого он должен был использовать свои собственные закрытые типы, тогда проблем не было бы.
Теперь, скажем, в нашей гипотетической проблеме есть подсистема Database
.Он использует тайм-аут при отправке сетевых вызовов.Он хочет экземпляр Timeout
, который имеет значение 100 миллис.Подсистема EmailSender
ожидает замедления.Его Timeout
должно равняться 5 секундам.При интеграции системы конфликтуют.
Один из подходов: частные типы обёрток
// EmailSender-private wrapper. Class is not public on purpose.
class EmailSenderTimeout {
final Timeout timeout;
EmailSenderTimeout(Timeout t) { this.timeout = t; }
}
// In the module
bind(EmailSenderTimeout.class)
.toInstance(new EmailSenderTimeout(Timeout.seconds(5));
// In the service
@Inject
EmailSendingService(EmailSenderTimeout timeout) {
long millis = timeout.timeout.millis();
}
Уолла! Если кто-нибудь когда-нибудь придет и свяжет Timeout
с тем, что пожелает его сердце,у нас EmailSender
еще есть 5 секунд!
Мы добились изоляции.Мы все еще разделяем тип Timeout
, но больше не делимся экземплярами.
Guicier: привязка аннотаций
Этот механизм является ответом Guice на нашу точную проблему.
// Define this annotation once in the sub-system somewhere.
// Perhaps even directly in the Module class.
@Retention(RetentionPolicy.RUNTIME)
@BindingAnnotation
@interface ForEmail { }
// EmailModule
protected void configure() {
bind(Timeout.class).annotatedWith(ForEmail.class)
.toInstance(Timeout.seconds(5);
}
// Service
class EmailSendingService {
@Inject
EmailServiceImpl(@ForEmail Timeout timeout) {
long millis = timeout.millis();
}
}
Вы также можете повторно использовать аннотацию для других общих типов:
class EmailServiceImpl {
@Inject
EmailServiceImpl(@ForEmail Timeout timeout,
@ForEmail RemoteAddress remoteAddress,
@ForEmail Protocol protocol) {
}
}
Каждая подсистема объявляет свою собственную аннотацию привязки и использует ее повсюду.
В абсолютном выражении никакие две подсистемы не должны связывать одни и те же типы , независимо от того, интегрированы они сегодня или нет.
Упрощенная ментальная модель Guice
Никогда не должно быть дубликатов в bindings
:
class Guice {
HashMap<Key, Provider> bindings;
}
// Combines 3 things: Class, Generic Types, and Annotation
class Key {
Class<?> actualClass;
@Nullable Class<?> annotationClass;
@Nullable Type genericTypes;
}
Подробнее: Key.java , TypeLiteral.java