2015-05-08 12 views
18

In einem Projekt arbeite ich an I 2 Klassen haben, die aufeinander stark abhängig sind:Dolch 2 Kreis dependancy

@Singleton 
class WorkExecutor { 
    @Inject Provider<ExecutionServices> services; 
    ... 
    public void execute(Work w){ 
     w.execute(services.get()); 
     ... 
    } 
    ... 
} 

class ExecutionServicesImpl implements ExecutionServices { 
    @Inject WorkExecutor executor; 
    ... 
} 

Die Idee ist, dass, wenn eine Arbeit ausführt, die Arbeit Zugriff auf mehrere Dienste hat - Einer von ihnen ist der Executor selbst, so dass eine Arbeit Subarbeiten ausführen kann.

Wie man sehen kann, gibt es hier eine zirkuläre Abhängigkeit, die ich aber sehr schwer zu brechen fand.

Das Hauptproblem besteht darin, dass der WorkExecutor tatsächlich keine Instanz des ExecutionServices-Objekts zur Entwurfszeit des Diagramms benötigt, sondern nur einen Anbieter, der später verwendet werden kann. Leider weiß Dagger nicht, dass der WorkExecutor den ExecutionServices-Provider nicht vom Konstruktor der Klasse aufruft, so dass er davon ausgeht, dass ExecutionServices von WorkExecutor und umgekehrt abhängt.

Eine mögliche Lösung, die ich gefunden ist ein Modul und Komponente in der folgenden Art und Weise zu definieren:

interface DelayedProvider<T> extends Provider<T>{} 

@Module 
class AppModule { 
    Provider<ExecutionServices> delayedProvider = null; 

    @Provides DelayedProvider<ExecutionServices> provideDelayed() { 
     return() -> delayedProvider.get(); 
    } 

    @Provides @Named("late-binding-conf") Void latebindingConf(Provider<ExecutionServices> eager){ 
     this.delayedProvider = eager; 
     return null; //notice we returning Void and not void 
    } 

} 

@Component(modules=AppModule.class) 
interface AppComponent { 
    App app(); 
    @Named("late-binding-conf") Void configureLateBinding(); 
} 

und modifiziert dann ich die ursprünglichen Klassen zu sein:

@Singleton 
class WorkExecutor { 
    @Inject DelayedProvider<ExecutionServices> services; 
    ... 
    public void execute(Work w){ 
     w.execute(services.get()); 
     ... 
    } 
    ... 
} 

class ExecutionServicesImpl implements ExecutionServices { 
    @Inject WorkExecutor executor; 
    ... 
} 

Und dann, um meine App erstellen ich habe zu tun:

AppComponent acomp = DaggerAppComponent.create(); 
App = acomp.app(); 
acomp.configureLateBinding(); 

Aber ich bin nicht sicher, ob dies die richtige ist Vorgehensweise - Gibt es einen besseren Weg?

+0

Ich habe ein ähnliches Problem. Haben Sie jemals eine bessere Lösung gefunden? – KennethJ

+0

@KennethJ, leider nicht .. – bennyl

+2

Warum benötigt 'ExecutionServicesImpl' einen' WorkExecutor'? Sie sagen, dies ist so, dass eine Arbeit Subworks ausführen kann. Könnte man nicht einfach den 'WorkExecutor' an die' execute' Methode von 'Work' übergeben, anstatt ein Mitglied von' ExecutionServicesImpl' zu sein? – Frans

Antwort

1

Ich habe nicht den Verdacht, OP wird dies mögen, wie Sie einen Weg, etwas 'falsch' machen wollen, arbeiten 'richtig'. Das kann nicht wirklich sein. Wenn Sie auf eine zirkuläre Abhängigkeit stoßen, ist die "richtige" Lösung, diese Abhängigkeit zu reformieren.

In Ihrem Fall ist WorkExecutor ein Singleton, also muss er wahrscheinlich so bleiben wie er ist. ExecutionServicesImpl sollte dann geändert werden, um die Abhängigkeit von WorkExecutor zu entfernen. Ohne die Besonderheiten des Codes zu kennen, kann man nicht viel mehr sagen. Da ein ExecutionService unabhängig von seinem "Worker" ist, reduziert sich die Kopplung und ist auf lange Sicht wahrscheinlich eine sehr gute Sache.

0

Warum hängt ExecutionServicesImpl vom WorkExecutor ab?

Zeige mehr Kern, ExecutionServices klingt auch wie ein Singleton, warum hängt es voneinander ab, um richtig zu funktionieren?

WorkExecutor klingt wie etwas, das Sie die ExecutionService als WorkExecutor passieren können, wird woanders injiziert werden, vielleicht derjenige, der die Service verwendet.

Ich weiß nicht, zeigen mehr Code und wahrscheinlich ist das die Antwort, es sieht komplex aus.

0

Ich bin in eine Situation in einem Swing-Projekt geraten, wo ein Objekt eines Drittanbieters von einem JFrame abhing und das JFrame von dem Objekt eines Drittanbieters abhing, um seinen Inhaltsbereich zu erstellen. Ich kam zu dieser Lösung, entschied mich aber letztendlich, die Schleife in meiner Hauptmethode zu schließen, nachdem der Objektgraph erstellt wurde.Im Grunde genommen, habe ich zwei Namen JFrame Provider, der zweite in Abhängigkeit von der ersten und der gleichen Instanz der Rückkehr:

@Provides 
@Singleton 
@Named("DO_NOT_INJECT") 
JFrame mainWindowIncomplete() { 
    return new JFrame(); // after setting it up 
} 

@Provides 
@Singleton 
CControl dockControl(@Named("DO_NOT_INJECT") JFrame mainWindow) { 
    return new CControl(mainWindow); 
} 

@Provides 
@Singleton 
@Named("MAIN_WINDOW") 
JFrame mainWindow(@Named("DO_NOT_INJECT") JFrame mainWindow, CControl dockControl) { 
    mainWindow.add(dockControl.getContentArea()); 
    return mainWindow; 
} 

Damit dies funktioniert, der zweite Anbieter müssten mindestens einmal verwendet werden. Sie können dies sicherstellen, indem Sie wie in this answer beschrieben dem ersten Paket einen privaten Paketqualifikator hinzufügen.