На высоком уровне, что вы хотите для ваших заводов, чтобы скрыть предсказуемую зависимость, поэтому вам нужно только указать те, которые изменяются. Кто-то, у кого есть экземпляр фабрики, должен только передавать данные, а не фабрики или зависимости. Я представляю интерфейс таким образом.
interface ManagerFactory {
Manager createManager(int managerId);
}
interface ShiftFactory {
Shift createShift(int managerId, int shiftId);
}
interface WorkerFactory { // The two methods here might be difficult to automate.
Worker createWorkerA(int managerId, int shiftId, int workerId);
Worker createWorkerB(int managerId, int shiftId, int workerId);
}
class Manager {
@Inject ShiftFactory shiftFactory; // set by Guice, possibly in constructor
private final int managerId; // set in constructor
Shift createShift(int shiftId) {
shiftFactory.createWorkerA(this.managerId, shiftId); // or B?
}
}
class Shift {
@Inject WorkerFactory workerFactory; // set by Guice, possibly in constructor
private final int managerId; // set in constructor
private final int shiftId; // set in constructor
Worker createWorker(int workerId) {
shiftFactory.createShift(this.managerId, this.shiftId, workerId);
}
}
Здесь следует отметить, что менеджер не заботится о всех рабочих-он не создает их, так что в отличие от вашего вопроса, вы не должны принимать WorkerFactory просто передать его вместе с вашим Сдвига , Это часть привлекательности инъекции зависимостей; вам не нужно относиться к среднему менеджеру (middle-Manager
?) с зависимостями зависимостей.
Обратите внимание, что ни один из интерфейсов или реализаций Factory
даже не отображается в вашем общедоступном API за пределами конструкторов.Это детали реализации, и вы можете следить за иерархией объектов, не называя их снаружи.
Теперь, как выглядит реализация ManagerFactory? Может быть, как это:
class ManualManagerFactory {
// ShiftFactory is stateless, so you don't have to inject a Provider,
// but if it were stateful like a Database or Cache this would matter more.
@Inject Provider<ShiftFactory> shiftFactoryProvider;
@Override public Manager createManager(int managerId) {
return new Manager(managerId, shiftFactoryProvider.get());
}
}
... но это в значительной степени шаблонное, и, возможно, гораздо более, когда есть много инжектированных или не инжектированные параметры. Guice может сделать это для вас, а, до тех пор, как вы по-прежнему предоставлять свой интерфейс ManagerFactory и аннотированный конструктор:
class Manager {
private final ShiftFactory shiftFactory; // set in constructor
private final int managerId; // set in constructor
@Inject Manager(ShiftFactory shiftFactory, @Assisted int managerId) {
this.shiftFactory = shiftFactory;
this.managerId = managerId;
}
// ...
}
// and in your AbstractModule's configure method:
new FactoryModuleBuilder().build(ManagerFactory.class);
Вот так. Guice создает свою собственную реализацию ManagerFactory на основе отражения, читая возвращаемый тип метода Manager, сопоставляя его с аннотациями @Inject и @Assisted, а также параметрами метода интерфейса и вычисляя их оттуда. Вам даже не нужно вызывать метод implement
на FactoryModuleBuilder, если только диспетчер не был интерфейсом; то вам придется сказать Guice, какой конкретный тип создать.
Для пинков и усмешек, давайте посмотрим, то же самое с Google's code-generating AutoFactory package:
@AutoFactory(
className = "AutoManagerFactory", implementing = {ManagerFactory.class})
class Manager {
private final ShiftFactory shiftFactory; // set in constructor
private final int managerId; // set in constructor
@Inject Manager(@Provided ShiftFactory shiftFactory, int managerId) {
this.shiftFactory = shiftFactory;
this.managerId = managerId;
}
// ...
}
Почти идентичное, верно? Это создаст класс Java (с исходным кодом, который вы можете прочитать!), Который проверяет класс Manager и его конструкторы, читает аннотации @Provided (nb @Provided - это противоположность FactoryModuleBuilder @Assisted) и делегирует конструктору его комбинацию параметров и введенных полей. Две другие преимущества Auto, который работает с Guice, а также кинжалом и других JSR-330 Dependency Injection рамки:
Это нормальный код Java бесплатно отражения в Guice и его FactoryModuleBuilder; отражения на Android плохой, так что это может быть хорошим выигрышем в производительности.
С созданием кода вам даже не нужно создавать интерфейс ManagerFactory - без каких-либо параметров для @AutoFactory вы бы закончили с final class ManagerFactory { ... }
, который имеет именно такое поведение, которое Guice подключится через FactoryModuleBuilder. Конечно, вы можете настроить имя и интерфейсы самостоятельно, что также может помочь разработчикам как сгенерированный код, порой, не очень хорошо подходит для инструментов и IDE.
UPDATE ответить комментарии:
- Что касается createWorker: Да, извините, ошибка CopyPaste.
- Что касается автоматизации: это потому, что ни Assisted Inject, ни AutoFactory не имеют возможности делегировать статические методы или работать с конструкторами, которые имеют одинаковые вспомогательные (предоставленные пользователем) аргументы. Это случай, когда вам, возможно, придется написать собственную фабрику.
- Относительно менеджера, не нуждающегося в WorkerFactory: Единственная причина, по которой менеджер потребует WorkerFactory, это если он создает либо ShiftFactory, либо сам Shift, вызывая конструктор. Обратите внимание, что мой пример не делает ни того, ни другого: вы разрешаете базе данных зависимостей (Guice) предоставлять зависимости, что означает, что WorkerFactory скрывается в ShiftFactory, который уже предоставляет Guice.
Этот ответ полезен, но прежде чем я приму его, просто небольшой вопрос, если я могу. здесь: Рабочий createWorker (int workerId) { shiftFactory.createShift (this.managerId, this.shiftId, workerId); } вы имели в виду использование workerFactory и имеете 2 метода? (createWorker A и B)? – slashms
Кроме того, почему вы сказали, что будет сложнее автоматизировать? в чем причина? – slashms
Кроме того, если мне нужна инъекция конструктора, то как я могу по-прежнему запретить WorkerFactory отправлять диспетчеру? – slashms