controlla un oggetto attraverso un altro oggetto?

5

Oggi ho visto il seguente schema: hai un oggetto A e un oggetto B. L'oggetto B accetta un puntatore ad A nel suo costruttore.

Una volta che B è stato creato, esiste un metodo B.doCalc () che esegue un calcolo (internamente usando le informazioni di A). Il risultato è ottenuto con il metodo B.getResult ().

Per eseguire un altro calcolo, A viene modificato e B.doCalc () viene chiamato di nuovo.

Qual è la tua opinione su questa scelta? L'avrei progettato diversamente, ma voglio sentire la tua voce.

Modifica : nota che la mia obiezione principale è modificare A per avere un risultato diverso da B, senza toccare B.

Sebbene sia simile, penso che proprio questa disciplina esprima una sensazione molto migliore di ciò che sta accadendo. Invece di

a = new A
a.whatever = 5
b = new B(a)
b.doCalc()
res = b.getResult()
a.whatever = 6
b.doCalc()
res = b.getResult()

Ottieni l'oggetto puntatore da b stesso.

a = new A
a.whatever = 5
b = new B(a)
b.doCalc()
res = b.getResult()

a = b.getAPointer()
a.whatever = 6
b.doCalc()
res = b.getResult()

perché rende più esplicito il fatto che a è preso da b e quindi modificato. Non mi piace ancora, però ...

Modifica 2 : uno dei motivi per cui B accetta A al costruttore è perché deve configurare i dati interni per il calcolo. Questi dati

  • dipende dalla natura di A
  • devono essere letti dal disco, che può essere lento
  • B può essere eseguito molte volte sullo stesso A, anche se leggermente modificato A. La natura di queste modifiche non invalida i dati interni di B. Non vogliamo caricare nuovamente le informazioni ogni volta che doCalc () viene invocato, né correre il rischio di eseguire doCalc (a) con i dati interni di B che sono incoerenti con l'A inoltrato.
posta Stefano Borini 01.02.2011 - 20:57
fonte

6 risposte

7

Mi rendo conto che questo problema è preso fuori dal contesto, ma che sembra sbagliato.

Vedo alcuni potenziali problemi con questo:

  1. Ti baserai sul fatto che l'implementazione interna di B non è cambiata e che utilizza ancora A nel modo che ti aspetti.
  2. Perché B.doCalc () non restituisce il risultato in primo luogo, senza dover chiamare B.getResult ()?
  3. Un'altra parte del codice potrebbe potenzialmente modificare l'istanza di A? Questo invaliderebbe i tuoi calcoli?

Senza conoscere i tuoi limiti effettivi, è difficile suggerire un altro modo, ma probabilmente farei qualcosa del genere:

a = new A
a.whatever = 5 
b = new B(a) // initialize B from A, but don't keep relying on the instance of A
result = b.doCalc()

a.whatever = 7
b.setWhatever(6)
result = b.doCalc() // this calc will use the value 6

In generale sembra un caso di responsabilità malriposta. Se il calcolo dipende interamente da A, allora potrebbe essere la responsabilità di A o B potrebbe consumare A come parametro per il suo metodo doCalc ().

    
risposta data 01.02.2011 - 22:03
fonte
3

< TL; DR >

Ciò consente flessibilità nel modo in cui la classe contenuta opera. Se esponi i dettagli della classe contenuta al client della classe contenente, questa diventa parte dell'interfaccia della classe contenente.

< / Tl; DR >

Questo è molto comune negli scenari dell'interfaccia utente in cui un widget è legato ad un oggetto. Quando l'oggetto cambia, vogliamo che l'interfaccia utente mostri quel cambiamento. Facciamo un altro esempio. Supponiamo che tu abbia un carrello della spesa e desideri calcolare il totale sul carrello. Uno dei lineitems nel carrello viene aggiornato per modificare la quantità. Lo scenario sarebbe

var shoppingCart =new ShoppingCart();
var lineItem=new lineItem(product,5);
shoppingCart.Add(lineItem);
var total = shoppingCart.CalculateTotal();

Nel caso semplice CalculateTotal sarebbe simile a questo:

public Double CalculateTotal()
{
  var total = 0.0;
  foreach (var item in _lineItems)
  {
    total+=item.Product.Price*item.Quantity;
  }
  return total;
}

Ma per quanto riguarda lo scenario in cui si dice di voler applicare uno sconto in base alla quantità acquistata? Potresti avere il carrello della spesa calcolare lo sconto o puoi lasciare il calcolo fino al lineitem. Ecco come apparirà il calcolo del prezzo in tale scenario.

// LineItem.GetTotal

public double Total
{
  get
  {
    var total = Product.Price* Quantity;
    //apply a 5% discount if more than five purchased.
    if(Quantity>=5)
    {
      total-=total*.05;
    }
    return total;
  }
}

Ora la funzione di calcolo del totale nel carrello è più semplice.

public Double CalculateTotal()
{
  var total=0.0;
  foreach(var item in LineItems)
  {
    total+=item.Total;
  }
}

Rimuovendo la dipendenza del carrello dalla modalità di calcolo del totale dell'elemento pubblicitario, siamo stati in grado di rimuovere la preoccupazione di calcolare gli sconti dal carrello degli acquisti. Non sono sicuro che il tuo scenario sia simile a quello che abbiamo appena discusso, ma può essere estrapolato a molti casi diversi. Andando da lì, il lineitem potrebbe estrarre la sua conoscenza del calcolo del totale a una strategia di sconto che potrebbe leggere le sue informazioni da un database.

    
risposta data 01.02.2011 - 23:52
fonte
2

Non è certo il motivo per cui è necessario separare docalc e getresult a meno che non si stiano eseguendo async, ma io uso le cose in questo modo quando l'oggetto A è un oggetto di trasferimento dati e la trasformazione dell'oggetto B è consentita solo su oggetti di tipo A per sicurezza / motivi di sicurezza a volte.

Se non hai una buona ragione per la complessità è piuttosto difficile. Dovrei sapere di più sull'esempio per offrire un'opinione migliore.

EDIT:

Con i dettagli aggiunti all'OP sembra che l'autore originale del codice stesse cercando di creare qualcosa seguendo le linee di un metodo di estensione C #. Se si dispone di una tale funzione nella propria lingua, preferirei tale approccio, ma se non lo si fa si potrebbe comunque ottenere un risultato più pulito con una funzione statica docalc.

    
risposta data 01.02.2011 - 21:04
fonte
1

Sono confuso sul motivo per cui B deve essere un oggetto separato qui. Se sta gestendo calcoli con oggetti A, perché non fa parte o meno dell'oggetto A o una funzione separata che accetta un A come parametro? Sulla base delle informazioni fornite nella domanda, direi che una di queste opzioni sarebbe migliore.

    
risposta data 01.02.2011 - 21:45
fonte
1

Non lo farei in questo modo semplicemente perché i risultati di doCalc() non sono ovvi. Farei qualcosa di più simile (prendendo a prestito dalla programmazione funzionale):

A a = new A();
a.setWhatever(5);

B b = new B();
res = b.doCalc(a);

In questo modo è ovvio che doCalc() dipende dall'oggetto A per il suo calcolo.

Non sono sicuro del motivo per cui il risultato viene recuperato separatamente. Forse il calcolo richiede molto tempo e l'autore ha avuto bisogno dello stesso risultato più volte. La risposta è di mantenere un riferimento al risultato e di trasmetterlo piuttosto che un riferimento all'oggetto che crea il risultato.

    
risposta data 02.02.2011 - 14:58
fonte
0

Mi sembra che il metodo doCalc () sia su A. Perché separare la funzione chiama in un oggetto separato? Nella migliore delle ipotesi, l'oggetto B potrebbe inoltrare a A.

    
risposta data 02.02.2011 - 06:54
fonte

Leggi altre domande sui tag