Rappresenta le dipendenze di oggetti complessi

3

Ho diverse classi con un grafico di dipendenza ragionevolmente complesso (ma aciclico). Tutte le dipendenze sono nella forma: class X istanza contiene un attributo di class Y . Tutti questi attributi vengono impostati durante l'inizializzazione e non vengono mai più modificati.

Ogni costruttore di classi ha solo un paio di parametri e ogni oggetto conosce i parametri corretti da passare ai costruttori degli oggetti che contiene. class Outer si trova nella parte superiore della gerarchia delle dipendenze, cioè nessuna classe dipende da essa.

Attualmente, il livello dell'interfaccia utente crea solo un'istanza Outer ; i parametri per il costruttore Outer derivano dall'input dell'utente. Ovviamente, Outer nel processo di inizializzazione, crea gli oggetti di cui ha bisogno, che a loro volta creano gli oggetti di cui hanno bisogno, e così via.

Il nuovo sviluppo è che un utente che conosce il grafico delle dipendenze potrebbe desiderare di raggiungerlo in profondità, e impostare i valori di alcuni degli argomenti passati ai costruttori delle classi interne (essenzialmente sovrascrivendo i valori usati al momento). Come dovrei cambiare il design per supportare questo?

Potrei mantenere l'approccio corrente in cui tutte le classi interne sono create dalle classi che ne hanno bisogno. In questo caso, le informazioni su "sostituzioni dell'utente" devono essere passate al costruttore della classe Outer in una struttura user_overrides complessa. Forse user_overrides potrebbe essere la rappresentazione logica completa del grafico delle dipendenze, con le sostituzioni associate ai bordi appropriati. Outer class passerebbe user_overrides a ogni oggetto che crea, e farebbero lo stesso. Ogni oggetto, prima di inizializzare oggetti di livello inferiore, troverà la sua posizione in quel grafico e controllerà se l'utente ha richiesto un override a uno qualsiasi degli argomenti del costruttore.

In alternativa, potrei riscrivere tutti i costruttori degli oggetti per prendere come parametri gli oggetti completi di cui hanno bisogno. Pertanto, la creazione di tutti gli oggetti interni verrebbe spostata all'esterno dell'intera gerarchia, in un nuovo livello controller che si trova tra Outer e livello UI. Il livello del controller attraverserebbe essenzialmente il grafico delle dipendenze dal basso, creando tutti gli oggetti man mano che procede. Il livello controller dovrebbe chiedere agli oggetti di livello superiore i valori dei parametri per gli oggetti di livello inferiore ogni volta che il parametro pertinente non viene fornito dall'utente.

Nessun approccio sembra terribilmente semplice. C'è qualche altro approccio? Questo problema si è presentato abbastanza in passato per avere uno schema di cui posso leggere? Sto usando Python, ma non penso che importi molto a livello di design.

    
posta max 28.11.2012 - 13:04
fonte

4 risposte

1

Devi guardare tutti dei modelli di creazione di oggetti comuni e fare una valutazione basata sullo stato della tua logica, il tempo necessario per implementare la modifica e così via. Inoltre, piuttosto che gli oggetti partecipanti sanno come costruirsi a vicenda, dovresti considerare Inversion of Control.

È difficile essere specifici senza ulteriori informazioni, ma dato che si sta parlando di un grafico complesso, penserei che Abstract Factory o Builder sarebbe più utile qui che solo una semplice Factory, a seconda di quanto diversi sono i diversi scenari.

Se hai delle decisioni iniziali per fare quella creazione di influenza del grafico ma le differenze sono sfumature, guarda Builder ... in particolare se ci sono alcune differenze strutturali tra i "prodotti" (come, alcuni oggetti partecipanti sono opzionali o in alcuni scenari gli oggetti partecipanti trascinano in un altro gruppo di collaboratori).

Se le differenze tra gli scenari sono più distinte / elaborate e prendono la forma di "famiglie" di oggetti che dovrebbero essere usati insieme, guarda a Abstract Factory con alcune diverse implementazioni di fabbrica.

Se in realtà hai solo alcuni specifici sapori di questo grafico e vuoi scegliere e scegliere tra loro dinamicamente, potresti essere in grado di andare con Prototype; considera l'implementazione corrente come un prototipo, codifica le tue implementazioni alternative come un altro prototipo e clona il prototipo appropriato secondo necessità.

    
risposta data 11.12.2012 - 19:42
fonte
2

Sposterei tutte le logiche di costruzione di oggetti da Outer in una classe diversa usando Factory_pattern .

I costruttori delle classi di dominio "Esterno" e "Interno" ottengono tutti i parametri figli richiesti

public Outer(Inner inner)
{
    _inner = inner;
}

e la fabbrica crea gli articoli per te:

public class OuterFactory
{
    // Existing behaviour - concrete Inner instantiation is hard coded
    public virtual Outer createOuter(CreationParameters parameters)
    {
        Inner _inner = createInner(parameters);
        Outer outer = new Outer(inner);

        return outer;           
    }

    public virtual Inner createInner(CreationParameters parameters)
    {
        Inner _inner = new Inner();     
        return _inner;          
    }
}

Se hai bisogno di una classe interna diversa puoi creare facilmente un nuovo stabilimento che deriva dalla fabbrica di base:

public class MySpecialOuterFactory : OuterFactory
{
    public overrrde Inner createInner(CreationParameters parameters)
    {
        Inner _inner = new SpecialInner();      
        return _inner;          
    }
}
    
risposta data 11.12.2012 - 13:12
fonte
1

Un approccio con la riflessione potrebbe funzionare bene qui.

Quindi, supponendo che sia ok per impostare il grafico nel modo predefinito, e quindi impostare i valori che si desidera sovrascrivere dopo (che richiede la mutevolezza dei propri oggetti, ovviamente):

Avere il grafico degli oggetti impostato nel modo predefinito. Quindi utilizzare la riflessione per percorrere il grafico dell'oggetto. Per ogni oggetto che trovi nel grafico, controlla le proprietà rappresentate nella tua struttura helper user_overrides per l'override. Se trovi una corrispondenza, imposta la proprietà sul nuovo valore (di nuovo, usando la riflessione).

A seconda del tuo scenario esatto, potresti essere in grado di eguagliare i tipi di classe nella tua struttura user_overrides ; in alternativa, potrebbe essere necessario memorizzare i keypath, con cui intendo qualcosa che rappresenta il percorso verso una determinata classe nel grafico dell'oggetto.

Potrebbe essere necessario prestare attenzione all'ordine in cui si ignorano le cose nella struttura ad albero.

Ci sono un sacco di informazioni là fuori sulla riflessione .

    
risposta data 11.12.2012 - 11:47
fonte
1

La tua descrizione di user_overrides inizia a sembrare un po 'come il modello di strategia - dove oggetti al di fuori della gerarchia passare una strategia ( richiamata ) per creare la dipendenza ..

esempio in pseudocode

type OuterType
{
    private inner : InnerType

    // Existing behaviour - concrete Inner instantiation is hard coded
    OuterConstructor()
    {
        _inner = new Inner()
    }

    // Strategy pattern, instantiation strategy is passed from outside as
    // a callback function which returns an Inner instance
    OuterConstructor(func() innerBuilderCallback)
    {
        _inner = innerBuilderCallback()
    }
}

Cosa succede se Inner ha anche una dipendenza che vuoi esporre con una strategia di istanziazione? Non devi passare entrambe le strategie al costruttore Outer , dal momento che quella strategia stessa "incatena" la strategia nidificata

void Foo()
{
    func innerBuilderCallback = lambda: new Inner(lambda: new InnerDependency());

    var outer = new Outer(innerBuilderCallback);
}

A seconda di quanto profonda è la tua gerarchia, potresti scoprire che ti ritroverai con alcune di queste strategie di callback che diventano un po 'difficili da gestire. In tal caso, incapsula l'intero processo in un oggetto Factory .

    
risposta data 11.12.2012 - 12:41
fonte