2013-06-30 18 views
5

In alcune revisioni del codice di eredità codice C# che ho fatto di recente ho visto un certo numero di esempi come questo:La pratica del passaggio dell'oggetto padre come riferimento per l'interazione oggetto padre Evil?

class ManagerParentClass 
{ 
    public string CustomProperty{get;set;} 
    public void Log(string message); 

    void DoABunchOfTasks() 
    { 
     new SomethingService().DoSomething(this); 
    } 
} 

con il seguente:

public class SomethingService 
{ 
    ManagerParentClass _manager; 

    void DoSomething(ManagerParentClass manager) 
    { 
     _manager = manager; 

     // do something 
     _manager.CustomProperty = "hello world"; 
     _manager.Log("said hello world"); 
    } 
} 

Anche se questo funziona bene su In superficie, sono preoccupato che questo sia un po 'un anti-pattern che potrebbe causare cose malvagie con la garbage collection.

Questo interromperà il garbage collector generazionale nella capacità di .Net di pulire correttamente gli oggetti padre e figlio o qualsiasi altra cosa negativa?

+2

Il GC non manterrà una radice se non sono in uso. Questa è solo una cattiva programmazione, penso. Le classi hanno mescolato le responsabilità. Domanda fantastica però. –

+2

I riferimenti padre-figlio sono comuni in qualsiasi grafico di oggetto coinvolto in modo decente. Questa è la benedizione di .NET GC.Prima dovevamo usare 'CopyMemory' e' ObjPtr' per rompere i riferimenti circolari nell'imperdente sistema di gestione della memoria in VB6. – tcarvin

risposta

2

Oh sì, questo è un terribile anti-pattern in generale. Lavoro con una base di codice che usa molto questo ed è pura follia.

Il più grande reato? Violazione dell'incapsulamento e stretto accoppiamento tra le classi che ne derivano: SomethingService sa troppo su ManagerParentClass e ManagerParentClass rinuncia al controllo di se stesso su SomethingService.

Due alternative migliori:

  1. guadagna DoSomething() un metodo di ManagerParentClass esempio, ciò è in stretta sintonia con un punto essenziale di indirizzo dell'oggetto: strutture dati portano loro operatori
  2. Fai SomethingService un puro metodo che esegue alcuni calcoli e restituisce un valore, il chiamante può quindi eseguire l'assegnazione allo ManagerParentClass

Ovviamente, entrambi questi refactoring implicano una mutazione di fine partita di ManagerParentClass e, provenendo da un angolo di programmazione funzionale, proverei a evitarlo del tutto. Ma senza ulteriori informazioni, non posso raccomandare un corso per questo.

+0

Non sono d'accordo. È quasi necessario fare questo genere di cose quando si utilizza il modello di visitatore per implementare ciò che equivale a un linguaggio di programmazione in cui si creano funzioni che operano su un insieme di dati contenuto in una cache o in una memoria esterna. Forse "in generale" è una cattiva idea, ma ci sono alcune circostanze in cui sembra che sia assolutamente necessario fare questo genere di cose. Prova a implementare una CPU virtuale con istruzioni, memoria esterna e verifica che la memoria esterna sia legittima senza farlo. È molto difficile. –

+0

@MikeTaber sì, sono d'accordo che l'applicazione appropriata del modello di visitatore è un caso legittimo in cui si vorrebbe fare questo genere di cose. Tuttavia, si noti che alcune lingue come F # hanno caratteristiche (ad esempio unioni discriminanti e pattern matching) che soppiantano il pattern del visitatore. ovvero, la legittima necessità di fare ciò può semplicemente riflettere una caratteristica della lingua mancante. –

+0

Forse è vero. Ma questa domanda riguarda C#, non F #, e se la pratica di passare un riferimento genitore al bambino sia intrinsecamente malvagia. Penso che la risposta sia molto più contestuale e dato l'esempio sopra, è difficile da dire. Penso che sia un design scarso dato questo specifico esempio, ma a volte ci sono buone ragioni per farlo. Questo era il mio unico punto. Un posto che questo causerà assolutamente problemi è nella serializzazione perché i riferimenti parent sono invalidati quando l'oggetto è serializzato. Puoi tenerne conto, ma diventa brutto. Comunque funziona ... quindi ... :) –

1

Questo è in realtà un modo decente di disaccoppiare classi l'uno dall'altro: ciò che hai scritto assomiglia molto al modello di visitatore.

L'esempio come scritto non ha gran parte dell'impatto della memoria, perché SomethingService non è presente su ManagerParentClass tranne la lunghezza di tale metodo. Se dovessimo assumere che SomethingService salverebbe una tale istanza durante la costruzione o metodi regolari, allora sarebbe leggermente più complicato.

Avere SomethingService contiene un riferimento a ManagerParentClass significa che ManagerParentClass si terrà nel riferimento 1) finché SomethingService viene tenuto in memoria attraverso una catena di riferimenti che riconducono a una radice GC e 2) purché SomethingService mantiene il suo riferimento a MPC.

Se SS doveva rilasciare il suo riferimento (annullarlo), il problema è risolto. Se SS non fosse più a sua volta referenziato da nulla, allora il GC saprà che SS può essere GCd, e se MPC è quindi detenuto solo da SS, allora MPC può essere a sua volta GCd.

+1

-1 'ManagerParentClass' e' SomethingService' sono chiaramente accoppiati a questo approccio. Se fosse in realtà il modello del visitatore, allora questo è strettamente associato a uno scopo (linguaggio OO che compensa la mancanza di unioni discriminanti), ma non sono convinto che lo sia (e somigli allo schema del visitatore, ma non sembra davvero spaventoso). –

+0

Non penso che sia affatto un visitatore, ma il resto della risposta va bene. – tcarvin

Problemi correlati