Domanda

Ho una situazione in cui sto cercando di mantenere il mio modello e la mia implementazione il più vagamente accoppiati possibile, tuttavia mi trovo di fronte a una situazione in cui l'accoppiamento potrebbe potenzialmente avvicinarsi molto di più di quello che voglio.

Ho una selezione di classi 'Model', tutte le interfacce di implementazione. Inoltre ho classi di "Accesso ai dati", che forniscono una serie di funzioni, una delle quali è la decodifica dei valori di ricerca di numeri interi nella loro rappresentazione "oggetto" completa.

All'interno delle mie classi di modelli voglio fornire accesso a questi valori decodificati senza richiedere al modello di conoscere le classi di accesso ai dati.

Un esempio semplificato è:

/// Core classes --

class Car : ICar
{
    public int MakeId { get {...} set { ... } }

    public IMakeInfo Make { get {...} }

    public string Registration { get { ... } set { ... } }

    public int CurrentOwnerId { get { ... } set { ... } }

    public IPerson CurrentOwner { get { ... } }
}

class MakeInfo : IMakeInfo
{
    public string Name { ... }
    public int Id { ... }
    public decimal Weight { ... }
    // etc etc
}

/// Data Access Classes --

class ResolveMake 
{
    public IMakeInfo GetMakeInfo(int id)
    { 
        // Implementation here...
    }

}

Come abilito la classe Car a fornire l'oggetto IMakeInfo a tutte le classi che consumano senza renderlo direttamente consapevole della classe ResolveMake? Nel vero caso sto lavorando con la classe Car non è nello stesso spazio dei nomi della classe ResolveMake e non contiene riferimenti ad alcuna istanza di esso.

Alcune delle mie opzioni:

  • Implementa un delegato in Car che può essere fornito con un'istanza del metodo GetMakeInfo .
  • Una sorta di iniezione di dipendenza
  • Associa da vicino Car a ResolveMake e falla finita.
  • Altre opzioni?

Eventuali suggerimenti sono benvenuti!

È stato utile?

Soluzione

Metodi di estensione ?

namespace CarStuff
{
   class Car : ICar
   {
      public int MakeId { get {...} set { ... } }
      // no Make property...
      public string Registration { get { ... } set { ... } }
      public int CurrentOwnerId { get { ... } set { ... } }
      public IPerson CurrentOwner { get { ... } }
   }
}


namespace MakeExts
{
   class ResolveMake
   {
      public static IMakeInfo Make(this Car myCar)
      {
         //implementation here
      }
   }
}

altrove:

using MakeExts;

Car c = new Car();
Console.WriteLine(c.Make().ToString());

Modifica: per utilizzare i metodi di estensione in .NET 2.0, hai bisogno di qualcosa come:

Fondamentalmente, una classe contenente:

namespace System.Runtime.CompilerServices 
{ 
   class ExtensionAttribute : Attribute
   {
   }
 }

e un " using System.Runtime.CompilerServices " sparsi in luoghi pertinenti.

Altri suggerimenti

Mi sembra Dependency Injection per me. Ho fatto cose simili con MS PP Unity e sia con l'iniezione del contruttore che con l'iniezione di metodo e proprietà. Quindi la tua classe Car avrebbe una sorta di iniezione di IMakeInfo ... esempio:

[InjectionMethod]
public void Initialize([Dependency] IMakeInfo makeInfo)
{
  this.MakeInfo = makeInfo;
}

Poiché la classe Car ha una proprietà Make che restituisce IMakeInfo sembra che fornisca già le informazioni. Quindi ho ragione nel presumere che il tuo problema sia più il modo in cui l'auto è dotata del valore da restituire?

Se questo è il caso, forse vuoi guardare alla creazione di un metodo di fabbrica che conosce su auto e ResolveMake.

Seguendo la tua analogia, ovviamente l'auto deve sapere quando get_Make viene chiamato come è IMakeInfo. Vorrei anche considerare il rendere una caratteristica chiave della macchina. Quindi penso che passare un IMakeInfo al costruttore di auto (possibilmente usando IOC) sia molto ragionevole. Se questa è una buona analogia con il tuo codice reale (ogni "Auto" ha un singolo, intrinseco, "IMakeInfo"), andrei con questo.

Puoi anche usare un setter come ha detto Johan, sempre con IOC opzionale. Il motivo per cui preferirei un costruttore è che sembra ogni " Car " dovrebbe avere un " Make " ;.

Alla fine (visti i vincoli in cui stavo lavorando) ho effettivamente usato una variazione su alcuni dei temi sopra.

Poiché ho dovuto evitare qualsiasi riferimento al mio livello di accesso ai dati a causa dei riferimenti circolari che si sono verificati, ho finito per fare qualcosa con i delegati e il codice "factory". Inserendolo nel mio scenario originale ho fatto quanto segue:

class Car 
{
    public void SetLookupProvider(ILookupProvider value) { _lookupProvider = value; }

    public IMakeInfo Make { get { return _lookupProvider.ResolveMake(MakeId); } }

    ....
}

interface ILookupProvider
{
    IMakeInfo ResolveMake(int id);
}

class LookupProvider
{
    public delegate IMakeInfo ResolveMakeDelegate(int id);

    public ResolveMakeDelegate ResolveMakeDel { set { _resolvemake = value; } }

    public IMakeInfo ResolveMake(int id){ return _resolvemake(id); }  
}

Quindi nel mio metodo di fabbrica ...

ICar returnedObject = new Car(blah, foo, bar, etc);

ILookupProvider luprovider = new LookupProvider();
luprovider.ResolveMakeDel = DataAccessLayer.FunctToGetMakeInfo;

(Car)returnedObject.SetLookupProvider(luprovider).

Ora sono il primo ad ammettere che questa non è la soluzione più carina (sarei andato con i metodi di estensione se avessi avuto accesso a un compilatore 3.0 ..) ma mantiene la classe Car liberamente accoppiata al livello DataAccess (che nella mia situazione ha impedito l'inferno di riferimento circolare ...). La classe Car non ha bisogno di sapere come sta ottenendo il risultato e il metodo factory che sta producendo gli oggetti Car in primo luogo è l'unica cosa che è accoppiata al livello di accesso ai Dati.

Non ho ancora segnato una risposta, lascerò che poche altre persone votino e vadano con il più alto, in particolare perché penso che siano tutte risposte valide (è solo che non potevo usare nessuno per intero in questa istanza).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top