Istanzia un motore da un secondo progetto senza avere un riferimento al secondo progetto causando un riferimento circolare?

0

Parliamo di tre progetti. Ho un progetto Cinema , un progetto Cinema.Engine e un progetto Cinema.Client1 .

Nel progetto Cinema , ho un'interfaccia ICinema e una classe factory CinemaFactory .

Nel progetto Cinema.Engine , ho una classe che implementa ICinema ... la chiameremo CinemaEngine : ICinema . Questo progetto è esposto come servizio WCF che fa riferimento al progetto Cinema .

Ciò consente al mio progetto Cinema.Client1 di fare riferimento solo al progetto Cinema . Cinema.Client1 può chiamare CinemaFactory e ottenere un riferimento al ICinema fornito dal servizio WCF. Tutto va bene e bene ....... Ora diventa pignolo.

Aggiungiamo un quarto progetto chiamato Cinema.Client2 che ha riferimenti a entrambi i progetti Cinema e Cinema.Engine . Poiché ho un riferimento a Cinema.Engine , voglio essere in grado di chiamare la mia fabbrica con un diverso set di parametri e avere il factory istanziare il motore localmente invece di chiamare il servizio WCF.

Nota importante: il progetto Cinema non ha riferimenti ad altri progetti.

Quindi, se ho solo un riferimento al progetto Cinema , la factory dovrebbe apparire così:

public class CinemaFactory {
    public ICinema GetCinema (Uri RemoteCinema) {}
}

Ma se ho un riferimento a Cinema.Engine , la factory dovrebbe apparire così:

public class CinameFactory {
    public ICinema GetCinema (string CinemaParameters) {}
}

Per dirla in altro modo: Il cliente (residente in un progetto) deve ottenere un'istanza del motore (residente in un secondo progetto). Tale istanza sarà o un proxy per un servizio WCF o verrà istanziata localmente. Questa istanza sarà ottenuta da una fabbrica (residente in un terzo progetto). Il primo progetto (contenente il cliente) e il terzo progetto (contenente il motore) fanno entrambi riferimento al secondo progetto (contenente la fabbrica).

Se il client sta ottenendo il proxy, non dovrebbe avere bisogno di un riferimento al secondo progetto. Se il client ha un riferimento al secondo progetto, solo allora la fabbrica fornirà l'opzione di istanziare il motore localmente.

Come posso ottenere la fabbrica (nel terzo progetto) per istanziare il motore localmente (dal secondo progetto) senza avere un riferimento al secondo progetto (che causa un riferimento circolare)?

Cose che ho esaminato ma che non funzionano:

  • Le classi parziali non funzionano. Le classi parziali sono zucchero sintattico e non può estendersi su progetti.
  • Stessa classe, spazio dei nomi diverso (per la fabbrica): ma come faccio a sapere da quale spazio dei nomi estrarre la classe?
  • new parola chiave: si applica solo ai membri, non a intere classi.
posta J Fenter 05.08.2016 - 18:02
fonte

2 risposte

3

Questo è esattamente ciò che i metodi di estensione sono per. Posso definire la fabbrica nel progetto Cinema con la sola firma del metodo. Posso definire un metodo di estensione in Cinema.Engine .

Esempio di codice qui sotto, che mostra come ottenere ciò usando i metodi di estensione. I quattro spazi dei nomi sono destinati a risiedere in quattro diversi progetti.

namespace Cinema {

    // Required for WCF
    public interface ICinema {
    }

    public sealed class CinemaFactory {
        // Singleton pattern to support extension methods. (Can't add extension methods to static classes.)
        private static readonly Lazy<CinemaFactory> LazyInstance = new Lazy<CinemaFactory> (() => new CinemaFactory());

        public static CinemaFactory Instance {
        get {
                return LazyInstance.Value;
            }
        }

        private CinemaFactory () {
        }

        public ICinema  GetCinema (EndpointAddress10 RemoteCinemaUri) {
            ICinema CinemaEngineInstance;

            // Get a reference from the WCF service...

            return CinemaEngineInstance;
        }
    }
}

namespace Cinema.Engine {
    using Cinema;

    public class CinemaEngine : ICinema {
    }

    // Add polymorphism to the class defined in a different project.
    public static class CinemaFactoryHelper {
        public static ICinema GetCinema (this CinemaFactory _This) {
            return new CinemaEngine ();
        }
    }
}

namespace Cinema.Client1 {
    using Cinema;

    public class Consumer1 {
        // Intellisense shows that there are no overloads to the GetCinema() method. Yay!
        private ICinema ref = CinemaFactory.Instance.GetCinema (EndpointAddress10.FromEndpointAddress (new EndpointAddress ("http://localhost"));
    }
}

namespace Cinema.Client2 {
    using Cinema;
    using Cinema.Engine; // Add visibility to the extension method that provides a local instance of the engine.

    public class Consumer2 {
        // Intellisense shows the method +1 overload. Yay!
        private ICinema ref = CinemaFactory.Instance.GetCinema ();
    }
}
    
risposta data 05.08.2016 - 20:38
fonte
1

Quindi, hai qualcosa di simile a questo:

public interface IWidget {}

public class RemoteWidget : IWidget {}
public class LocalWidget : IWidget {}

public class WidgetFactory
{
  public IWidget Create(Configuration configuration)
  {
      return new LocalWidget(configuration);
  }

  public IWidget Create(Uri origin)
  {
      return new RemoteWidget(origin);
  }
}

E, per qualche motivo, ora desideri distribuire WidgetFactory separatamente da LocalWidget . Come crei un WidgetFactory che non conosce al momento della compilazione quali implementazioni IWdidget sono disponibili o dove trovarle?

Hai alcune opzioni. Ad esempio:

  • Scansione di una directory di estensioni, moduli o plug-in per DLL e caricali dinamicamente .
  • Utilizza un framework DI che può eseguire il caricamento di alcune delle librerie dinamiche per te.
  • Utilizza il Framework di estensibilità gestita .
  • Metodi di estensione ... forse . Ho appena capito che potrebbe funzionare; Non riesco a pensare a come offhand.
  • Consenti all'applicazione client di configurare WidgetFactory o registrare le implementazioni Widget disponibili. Questo è probabilmente il percorso di minima assunzione: il cliente può scegliere di utilizzare DI, MEF, metodi di estensioni, riflessione diretta, ecc. Non si sta imponendo molto in termini di architettura in questo modo.
  • Allo stesso modo, fai in modo che% co_de accetti un parametro WidgetFactory.Create (potenzialmente come generico). Nel tuo caso, il tuo cliente sembra aver bisogno di sapere quale tipo ha bisogno di comunque . Quindi, questo può essere accettabile ...

O , cambia il tuo design: non considerare il progetto "Widget" come distribuibile; includilo nel tuo distribuibile. In tal caso, non inseriresti Type nel progetto "Widget", inseriresti WidgetFactory in.

Quindi, chiedi al cliente di fornire la fabbrica di cemento o crea alcune distro in base al modello di prezzo (presumo che sia ciò che sta guidando il bisogno di separazione). Ad esempio, IWidgetFactory , FreeDistro , StandardDistro ... Ciascuna di queste distro includerebbe solo riferimenti rigidi ai tipi concreti (e alle fabbriche) disponibili a quel "livello di supporto".

Molte opzioni ... Non tutte le opzioni si escludono a vicenda!

    
risposta data 05.08.2016 - 22:12
fonte

Leggi altre domande sui tag