2016-04-29 12 views
2

Ich habe drei Klassen, die ihre Schnittstelle implementieren.Kreisabhängigkeit mit Dependency Injection auflösen

Orders

public interface IOrderService 
{ 
    //SomeOrderFunction() 
} 

public class OrderService: IOrderService 
{ 
    //SomeOrderFunction(); 
} 

Treiber

public interface IDriverService 
{ 
    //SomeDriverFunction() 
} 

public class DriverService: IDriverService 
{ 
    //SomeDriverFunction(); 
} 

Plans - Verwendet Treiber und Plan Dienste

public interface IPlanService 
{ 
    //SomePlanFunction() 
} 

public class PlanService: IPlanService 
{ 
    private readonly IOrderService _orderService; 
    private readonly IDriverService _driverService; 

    public PlanService(IDriverService driverService, IOrderService orderService) 
    { 
     _orderService = orderService; 
     _driverService = driverService; 
    } 

    //PlanFunctionsThatUseServices(); 
} 

Das Problem, das ich jetzt aber ist haben, dass der Orden und Fahrerservice müssen Sprechen Sie zurück mit dem Plan-Service, wenn entweder eine Bestellung oder ein Fahrer geändert wird, wie würde ich dies tun w ohne eine zirkuläre Abhängigkeit zu haben?

Bearbeiten: Ich habe speziell auf die Schaffung eines vierten Service, der alle anderen wie die in diesem Artikel erläutert verwaltet.

Breaking Dependency Cycles

Was mich an dieser Implementierung verwirrt ist mein Plan Klasse, hat mein Plan Klasse implementieren sowohl die neue Klasse und die iPLAN-Schnittstelle?

+2

Ich glaube nicht, dass es möglich ist, ohne diese Referenz. Alternativ können Sie die Klasse _fourth_ erstellen, die die Kommunikation zwischen den drei Diensten verwaltet. – rbm

Antwort

1

Überdenken Sie zuerst Ihren Ansatz. Wenn Sie immer noch darauf bestehen, eine zirkuläre Abhängigkeit zu haben, könnten Sie sie weniger explizit machen, indem Sie den abhängigen Dienst für die Einstellung der Rückwärtsabhängigkeit verantwortlich machen, z.

public interface IOrderService 
{ 
    //SomeOrderFunction() 
    IPlanService planService; 
} 

.... 

public class PlanService: IPlanService 
{ 
    private readonly IOrderService _orderService; 

    public PlanService(IOrderService orderService) 
    { 
     _orderService = orderService; 
     _orderService.planService = this; 
    } 
} 
+0

Ich habe versucht, über einen anderen Ansatz nachzudenken, aber am Ende muss der Plan mitgeteilt werden, dass entweder der Auftrag oder der Fahrer geändert wurde. Was denken Sie über diesen Ansatz, mit einem Hauptknoten, der alle Dienste kennt. http://blog.schauderhaft.de/2011/07/17/breaking-dependency-cylces/ –

+0

imho dies ist eher eine Problemumgehung und es führt auch zeitliche Kopplung. – jgauffin

6

über Ereignisse.

erstellen einen Event-Handler-Schnittstelle:

public interface IEventSubscriber<TEvent> 
{ 
    void Handle(TEvent evt); 
} 

dann ein Ereignis definieren:

public class PlanCreated 
{ 
    public int PlanId { get; set; } 
    //and other properties. 
} 

Nun wollen wir eine der anderen Klassen implementieren es:

public class DriverService : IDriverService, IEventSubscriber<PlanCreated> 
{ 
    public void Handle(PlanCreated evt) 
    { 
     //handle it here. 
    } 
} 

jetzt sein In der Lage, Ereignisse zu veröffentlichen benötigen Sie eine andere Schnittstelle:

public interface IEventPublisher 
{ 
    void Publish<TEvent>(TEvent evt); 
} 

die sich von der Klasse aufgerufen werden kann:

public class PlanService: IPlanService 
{ 
    private readonly IOrderService _orderService; 
    private readonly IDriverService _driverService; 

    public PlanService(IDriverService driverService, IOrderService orderService, IEventPublisher publisher) 
    { 
     _orderService = orderService; 
     _driverService = driverService; 
    } 

    public void PlanFunctionsThatUseServices() 
    { 

     //business code.... 


     _publisher.Publish(new PlanCreated(){ Id = plan.Id }); 
} 

.. es aufzurufen wir Service-Standort (Implementierungsdetail) verwenden können:

public class EventPublisher : IEventPublisher 
{ 

    public EventPublisher(YourFavoriteContainer container) 
    { 
    } 


    public void Publish<TEvent>(TEvent evt) 
    { 
     using (var scope = _container.BeginLifetimeScope()) 
     { 
      var handlers = scope.ResolveAll<IEventSubscriber<TEvent>>(); 
      foreach (var handler in handlers) 
      { 
       //TODO: Handle exceptions= 
       handler.Handle(evt); 
      } 
     } 

    } 
} 

.. als Ergebnis erhalten Sie geringe Kopplung zwischen den Klassen.

+0

Es könnte fair sein zu erwähnen, dass dieser Ansatz ein wohlbekanntes Muster namens Event Aggregator ist. Dies gibt dem OP die Möglichkeit, weitere Informationen selbst zu finden. –

+1

Ich glaube nicht. Der Ereignisaggregator soll Ereignisse von mehreren Objekten *** zu einem einzigen Objekt *** kanalisieren, um die Registrierung für Clients zu vereinfachen. Das Muster behandelt mehr Ereignisse für mehrere Instanzen desselben Objekttyps. – jgauffin

1

In die IOrderService und IDriverService eine Func<IPlanService> anstelle der IPlanService injizieren. Dies "bricht" die Kette von Instanzen, die der Container erstellen muss.

public class OrderService { 
    public OrderService(Func<IPlanService> planServiceFactory) { 
     _planServiceFactory = planServiceFactory; 
    } 

    private readonly Func<IPlanService> _planServiceFactory; 

    public void SomeOrderFunction() { 
     _planServiceFactory().Notify(...); 
    } 
}