Come cambia il concetto di una classe quando si passano dati al costruttore anziché ai parametri del metodo?

10

Diciamo che stiamo facendo un parser. Un'implementazione potrebbe essere:

public sealed class Parser1
{
    public string Parse(string text)
    {
       ...
    }
}

Oppure potremmo passare il testo al costruttore:

public sealed class Parser2
{
    public Parser2(string text)
    {
       this.text = text;
    }

    public string Parse()
    {
       ...
    }
}

L'utilizzo è semplice in entrambi i casi, ma cosa significa significare per abilitare l'input del parametro a Parser1 , rispetto all'altro? Quale messaggio ho inviato a un collega programmatore, quando guardano all'API? Inoltre, ci sono vantaggi / svantaggi tecnici in alcuni casi?

Un'altra domanda sorge quando mi rendo conto che un'interfaccia sarebbe del tutto priva di significato nella seconda implementazione:

public interface IParser
{
    string Parse();
}

... dove un'interfaccia sul primo potrebbe servire almeno a uno scopo. Significa qualcosa in particolare, che una classe è "interfacciabile" o no?

    
posta ciscoheat 04.06.2013 - 22:17
fonte

5 risposte

10

Semanticamente parlando, in OOP dovresti solo passare al costruttore un insieme di parametri necessari per costruire la classe - similmente quando chiami un metodo, dovresti solo passargli i parametri necessari per eseguire la sua logica di business.

I parametri che devi passare nel costruttore sono parametri che non hanno alcun valore predefinito ragionevole e se la tua classe è immutabile (o in effetti un struct ) allora devono essere passate le proprietà non predefinite.

Per quanto riguarda il tuo esempio:

  • se si passa text al costruttore, si suggerisce che la classe Parser2 sarà costruita specificamente per analizzare quell'istanza di testo in un secondo momento. Sarà un parser specifico. Questo è in genere il caso in cui la costruzione della classe è molto costosa o sottile, una compilazione può essere compilata nel costruttore, quindi una volta che si tiene un'istanza è possibile riutilizzarla senza dover pagare il costo della compilazione; un altro esempio è l'inizializzazione del PRNG - è meglio se è fatto raramente.
  • se passi il text al metodo, segnala che Parser1 può essere riutilizzato per analizzare testi diversi per chiamata.
risposta data 05.06.2013 - 00:49
fonte
8

Bene, ricordiamo cosa significa passare una variabile come parametro costruttore: si inizializza un oggetto per usare le sue variabili di istanza nei metodi dell'oggetto. Il punto è che probabilmente vorrai usarlo in più di un metodo visto che vuoi avere una alta coesione nella tua classe.

Passare un parametro direttamente a un metodo significa in un modo inviare un messaggio a un oggetto e probabilmente ricevere una risposta. Di conseguenza, il cliente desidera che l'oggetto fornisca un servizio per lui.

Quindi, in conclusione, questi sono due modi molto diversi per passare i parametri e dovresti scegliere se il tuo oggetto dovrebbe o fornire un servizio o fornire alcune funzionalità intrinseche mentre gestisci alcune informazioni internamente.

    
risposta data 04.06.2013 - 22:27
fonte
4

Usage is simple in both cases, but what does it mean to enable parameter input to Parser1, compared to the other?

È un cambiamento di progettazione fondamentale. E il design dovrebbe trasmettere intento e significato. Hai bisogno di avere oggetti separati per ogni stringa che vuoi analizzare? In altre parole, perché abbiamo bisogno di un'istanza di parser con stringX e un'altra istanza con stringY? Di cosa si tratta parse (ing) e della stringa data che i due devono vivere e morire insieme? Partendo dal presupposto che "l'implementazione [di parsing] sottostante" (come dice Robert Harvey) non cambia, sembra non esserci motivo. E anche allora il suo dubbio IMHO.

How does the concept of a class change when passing data to the constructor instead of method parameters?

I parametri del costruttore mi dicono che queste cose sono necessarie per un oggetto. Lo stato corretto non è garantito senza di loro. Inoltre, so come / perché un parser è fondamentalmente diverso da un altro.

I parametri del costruttore mi impediscono di sapere troppo su come utilizzare la classe. Se invece dovrei impostare certe proprietà - come faccio a saperlo? Si apre un intero barattolo di vermi. Quali proprietà? In che ordine? Prima di usare quali metodi? e così via.

Another question comes up when I realize that an interface would be quite meaningless in the second implementation:

Un'interfaccia, come in A.P.I., sono i metodi e le proprietà esposti al codice client. Non lasciarti impacchettare solo in public interface { ... } . Quindi il significato dell'interfaccia si trova nel dilemma dei parametri del costruttore o del metodo vs, NON public interface Iparser vs public sealed class Parser

La classe sealed è dispari. Se sto pensando a diverse implementazioni del parser - hai menzionato "Iparser" - allora l'ereditarietà è il mio primo pensiero. È solo un'estensione concettuale naturale nel mio modo di pensare. OSSIA tutto ParserX s è fondamentalmente Parser s. Come altro dirlo? ... Un pastore tedesco è un cane (eredità), ma posso addestrare il mio pappagallo ad abbaiare (comportarsi come un cane - "interfaccia"); ma Polly non è un cane, semplicemente fa finta, avendo imparato un sottoinsieme di dogness. Le classi, astratte o meno, funzionano perfettamente come interfacce .

    
risposta data 05.06.2013 - 00:45
fonte
2

La seconda versione della classe può essere resa immutabile.

L'interfaccia può ancora essere utilizzata per fornire la possibilità di sostituire l'implementazione sottostante.

    
risposta data 04.06.2013 - 22:20
fonte
2

Parser1

Costruire con un costruttore predefinito e passare il testo di input in un metodo implica che Parser1 sia riutilizzabile.

Parser2

Il passaggio del testo di input nel costruttore implica che un nuovo Parser2 deve essere creato per ogni stringa di input.

    
risposta data 04.06.2013 - 22:22
fonte

Leggi altre domande sui tag