Design: metodo Object vs metodo della classe separata che accetta Object come parametro?


14

Ad esempio, è meglio fare:

Pdf pdf = new Pdf();
pdf.Print();

o:

Pdf pdf = new Pdf();
PdfPrinter printer = new PdfPrinter();
printer.Print(pdf);

Un altro esempio:

Country m = new Country("Mexico");
double ratio = m.GetDebtToGDPRatio();

o:

Country m = new Country("Mexico");
Country us = new Country("US");
DebtStatistics ds = new DebtStatistics();
double usRatio = ds.GetDebtToGDPRatio(us);
double mRatio = ds.GetDebtToGDPRatio(m);    

La mia preoccupazione nell'ultimo esempio è che ci sono statistiche potenzialmente infinite (ma diciamo anche solo 10) che potresti voler conoscere su un paese; appartengono tutti all'oggetto Paese?

per esempio

Country m = new Country("Mexico");
double ratio = m.GetGDPToMedianIncomeRatio();

Questi sono rapporti semplici ma supponiamo che le statistiche siano abbastanza complicate da giustificare un metodo.

Dov'è quella linea tra operazioni intrinseche a un oggetto e operazioni che possono essere eseguite su un oggetto ma che non ne fanno parte?

Risposte:


16

Prendendo i tuoi esempi PDF come punto di partenza, diamo un'occhiata a questo.

http://en.wikipedia.org/wiki/Single_responsibility_principle

Il principio di responsabilità singola suggerisce che un oggetto dovrebbe avere un unico obiettivo. Tienilo a mente.

http://en.wikipedia.org/wiki/Separation_of_concerns

Il principio di separazione delle preoccupazioni ci dice che le classi non dovrebbero avere funzioni sovrapposte.

Quando guardi questi due, suggeriscono che la logica dovrebbe andare in una classe solo se ha senso, solo se quella classe è responsabile di farlo.

Ora, nel tuo esempio PDF, la domanda è: chi è responsabile della stampa? Cosa ha senso?

Primo frammento di codice:

Pdf pdf = new Pdf();
pdf.Print();

Questo non è un bene. Un documento PDF non viene stampato da solo. Viene stampato da ... ta da! .. una stampante. Quindi il tuo secondo frammento di codice è molto meglio:

Pdf pdf = new Pdf();
PdfPrinter printer = new PdfPrinter();
printer.Print(pdf);

Questo ha senso. Una stampante PDF stampa un documento pdf. Meglio ancora, una stampante non dovrebbe essere una stampante PDF o una stampante fotografica. Dovrebbe essere solo una stampante in grado di stampare le cose inviate al meglio delle sue capacità.

Pdf pdf = new Pdf();
Printer printer = new Printer();
printer.Print(pdf);

Quindi è semplice. Metti i metodi dove hanno senso. Ovviamente, non è sempre così semplice. Prendi ad esempio le statistiche del tuo paese:

Country m = new Country("Mexico");
double ratio = m.GetDebtToGDPRatio();

La tua preoccupazione è che potrebbero esserci n numero di statistiche e che non dovrebbero essere in una classe Paese. Questo è vero. Tuttavia, se il tuo modello richiede solo quella particolare statistica, questo esempio di modellazione potrebbe effettivamente andare bene.

In questo caso, potresti dire abbastanza logicamente che un paese dovrebbe essere in grado di calcolare le proprie statistiche, specifiche per il tuo modello e i requisiti a portata di mano.

E qui sta la cosa: quali sono le tue esigenze? I tuoi requisiti guideranno il modo in cui modellerai il mondo, il contesto, in cui questi requisiti devono essere soddisfatti.

Se hai davvero un numero variabile / variabile di statistiche, il tuo secondo esempio ha più senso:

Country m = new Country("Mexico");
DebtStatistics ds = new DebtStatistics();
double usRatio = ds.GetDebtToGDPRatio(m);

Meglio ancora, avere una superclasse astratta o un'interfaccia chiamata Statistics che prende un paese come parametro:

interface StatisticsCalculator // or a pure abstract class if doing C++
{
   double getStatistics(Country country); // or a pure virtual function if in C++
}

class DebtToGDPRatioStatisticsCalculator implementa StatisticsCalculator ....

class InfantMortalityStatisticsCalculator implementa StatisticsCalculator ...

E così via e così via. Il che porta a quanto segue: generalizzazione, delega, astrazione. La raccolta statistica viene delegata a istanze specifiche che generalizzano un'astrazione specifica (un'API di raccolta delle statistiche).

Non so se questo risponde alla tua domanda al 100%. Dopotutto, non abbiamo modelli infallibili basati su leggi inviolabili (come fanno le persone di EE). Tutto quello che puoi fare è mettere le cose nel senso che hanno senso. E questa è una decisione ingegneristica che devi prendere. La cosa migliore da fare è conoscere veramente i principi di OO (e buoni principi di modellizzazione del software in generale).


1
+1 per l'interfaccia StatisticsCalculator (e successivo utilizzo del modello di strategia). E la risposta accurata e ben ponderata
edwardsmatt

3
tempo insufficiente per decostruire completamente questo al momento, ma deve sottolineare che la classe Printer diventerà una classe God nel tempo, strettamente accoppiata a tutti i tipi di classi di documenti. Pdf.Print sarebbe preferibile - ma tutto dipende da come si definisce la "responsabilità singola" ;-)
Steven A. Lowe

@Steve - quello che stai proponendo è un'idea orribile (avere Pdf implementare print ()). Non riflette il modo in cui la stampa viene implementata nella vita reale. Ogni sistema operativo e API di stampa di cui sono a conoscenza fornisce un'astrazione per Printer. Guarda l'elenco delle stampanti nel tuo computer XP / Vista (o sotto / var / spool o equivalente in * nix.) Ogni applicazione serializza un oggetto documento su una delle sue stampanti. Non esiste una stampante Word, né una stampante di testo o una stampante PDF. Esistono solo stampanti specifiche per il dispositivo di stampa e non specifiche per il tipo di documento.
luis.espinal,

2
+1 Mi piace Sto riflettendo su ciò che hai detto .. @Steve e Luis: Penso che il pezzo mancante del dibattito sugli oggetti di Dio sia un oggetto stampante generico dovrebbe accettare alcuni formati standard come ASCII o bitmap (anche se pdf è probabilmente anche ragionevole) e dovrebbe essere responsabilità di una terza classe convertire un determinato tipo di documento (diciamo un documento in ms word) in uno di questi formati standard.
Utente

2
Mi sembra che forse un PDF dovrebbe essere in grado di renderizzarsi su un'interfaccia Canvas o in un oggetto Image che potrebbe quindi essere elaborato da un oggetto Printer.
Winston Ewert,

4

Penso che nessuno dei due sia decisamente migliore dell'altro. L'uso di pdf.Print () è più rigoroso, ma avere una classe PdfPrinter potrebbe essere migliore se:

  • È necessario gestire le istanze delle stampanti
  • Esistono numerose opzioni e azioni che eliminerebbero la complessità di pdf.Print (...) (ad es. Annullamento della stampa, formattazione aggiuntiva, ecc.)

Altrimenti non mi sarei impiccato.


una buona risposta pratica; il tempo ci dirà come deve evolversi
Steven A. Lowe,

1
Il breve suggerimento è quello di esaminare sia la logica che i dati quando si applica SRP, al fine di decidere se ci pentiremo di non averli disaccoppiati prima. Il problema con l'archiviazione delle impostazioni per stampante nella Pdfclasse è che non devono essere archiviati insieme: Pdfè archiviato in un file ma le impostazioni per stampante devono essere archiviate con il profilo utente / macchina.
rwong,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.