2016-11-03 1 views
1

Этот вопрос касается правильного использования Guice @Assisted и @Provides, а также, как это сделать.@Assisted @ Использование использования в создании объектов в иерархическом дизайне

Текущий дизайн я имею в виду что-то вроде этого: Класс в верхней части иерархии также класс только, который подвергается воздействию клиента (в основном, общественность API), его внешности что-то подобное :

public class Manager{ 
public Manager(int managerId, ShiftFactory sf, WorkerFactory wf); 
// methods ... 
} 

Как вы, наверное, понимаете, идентификатор предоставляется пользователем на время создания (? @Assisted) , но другие не являются, они только заводы.

Класс Менеджер создает экземпляры класса Shift. Класс Shift создает экземпляры класса Рабочий.

Теперь для того, чтобы создать класс Сдвиг мы используем его конструктор:

public Shift(int managerId, int shiftId, WorkerFactory wf); 

shiftId предоставленный менеджером, а остальные являются одни и те же объекты с менеджер «s конструктор ,

Для того, чтобы создать работник мы используем 2 статические фабричные методы (но он может быть изменен ..):

public Worker createWorkerTypeA(int shiftId, int workerId) 
    public Worker createWorkerTypeB(int shiftId, int workerId) 

workerId предоставленную Смена класса. а остальное делегировано от Shift конструктор.

Какая уместность, Guice-y способ сделать это? Где я должен поставить @Assisted? @Provides?

Мне действительно понравился пример кода, включая абстрактный модуль, потому что код, который я видел до сих пор, пока не понятен мне.

Благодаря

ответ

2

На высоком уровне, что вы хотите для ваших заводов, чтобы скрыть предсказуемую зависимость, поэтому вам нужно только указать те, которые изменяются. Кто-то, у кого есть экземпляр фабрики, должен только передавать данные, а не фабрики или зависимости. Я представляю интерфейс таким образом.

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 рамки:

  1. Это нормальный код Java бесплатно отражения в Guice и его FactoryModuleBuilder; отражения на Android плохой, так что это может быть хорошим выигрышем в производительности.

  2. С созданием кода вам даже не нужно создавать интерфейс ManagerFactory - без каких-либо параметров для @AutoFactory вы бы закончили с final class ManagerFactory { ... }, который имеет именно такое поведение, которое Guice подключится через FactoryModuleBuilder. Конечно, вы можете настроить имя и интерфейсы самостоятельно, что также может помочь разработчикам как сгенерированный код, порой, не очень хорошо подходит для инструментов и IDE.


UPDATE ответить комментарии:

  • Что касается createWorker: Да, извините, ошибка CopyPaste.
  • Что касается автоматизации: это потому, что ни Assisted Inject, ни AutoFactory не имеют возможности делегировать статические методы или работать с конструкторами, которые имеют одинаковые вспомогательные (предоставленные пользователем) аргументы. Это случай, когда вам, возможно, придется написать собственную фабрику.
  • Относительно менеджера, не нуждающегося в WorkerFactory: Единственная причина, по которой менеджер потребует WorkerFactory, это если он создает либо ShiftFactory, либо сам Shift, вызывая конструктор. Обратите внимание, что мой пример не делает ни того, ни другого: вы разрешаете базе данных зависимостей (Guice) предоставлять зависимости, что означает, что WorkerFactory скрывается в ShiftFactory, который уже предоставляет Guice.
+0

Этот ответ полезен, но прежде чем я приму его, просто небольшой вопрос, если я могу. здесь: Рабочий createWorker (int workerId) { shiftFactory.createShift (this.managerId, this.shiftId, workerId); } вы имели в виду использование workerFactory и имеете 2 метода? (createWorker A и B)? – slashms

+0

Кроме того, почему вы сказали, что будет сложнее автоматизировать? в чем причина? – slashms

+0

Кроме того, если мне нужна инъекция конструктора, то как я могу по-прежнему запретить WorkerFactory отправлять диспетчеру? – slashms

Смежные вопросы