Dovresti refactoring tutte le classi helper in piccoli oggetti con stato?

3

Immagina di voler dipingere una stanza. Sei il proprietario di questa stanza e un Painter è in grado di dipingere la stanza. Esistono diversi tipi di Painters : a BluePainter , a RedPainter ecc. Per ogni colore esiste un Painter ! Non ti interessa in quale colore verrà dipinta la stanza, purché la stanza venga dipinta. Questa è una situazione di modello strategico.

(A proposito, so che ha più senso avere diversi tipi di Colors e dare Paint o Painter a Color istanza, ma usiamo il mio esempio dove ogni tipo di Painter è solo in grado di dipingere un colore.)

Ci sono due modi generali per dipingere la mia stanza. Illustrerò entrambi questi modi in due diverse situazioni.

Nella prima situazione, l'istanza Painter non richiede alcun parametro.

Opzione 1: ti è stata fornita un'istanza Painter che hai detto $painter->paintRoom() . Il Painter quindi va e dipinge la stanza.

Si potrebbe dire che Painter non ha stato ed è un'istanza di una classe Helper .

Opzione 2: ti è stata fornita un'istanza PainterFactory che dici a $painter_factory->createPainter()->paintRoom() . Il Painter quindi va e dipinge la stanza.

In questa situazione il PainterFactory è quello senza stato ed è in istanza di una classe Helper . Vedremo perché Painter avrà lo stato nella prossima situazione.

Nella seconda situazione, l'istanza di Painter richiede un parametro. A Painter è istanziato da: new Painter(paint_thickness) . Lo spessore della vernice determina quanto devono essere spessi gli strati di vernice sulle pareti.

Opzione1: ti è stata fornita un'istanza Painter che hai detto $painter->paintRoom(paint_thickness) . Il Painter quindi va e dipinge la stanza. Immagina che Painter sia solo certificato / in grado di applicare la vernice di un certo spessore.

È più ovvio ora che Painter è una classe Helper .

Opzione 2: ti è stata fornita un'istanza PainterFactory che dici a $painter_factory->createPainter(paint_depth)->paintRoom() . Il Painter quindi va e dipinge la stanza.

In questa situazione, PainterFactory rimane la classe Helper . Il Painter avrà lo stato: contiene una proprietà paint_thickness . Painter non è una classe Helper per questa opzione.

Il motivo per cui penso che Painter sia una classe Helper nella prima situazione e PainterFactory è una classe Helper nella seconda situazione è che non sarà mai necessario avere più di una istanza di una Painter class.

Per riassumere: è spesso possibile utilizzare una sola istanza di una classe per determinate funzionalità o riscrivere la classe per contenere effettivamente lo stato. La prima opzione rappresenta l'utilizzo delle classi Helper . Le classi Helper sono spesso criticate come di natura procedurale e statica e quindi cattive in OOP e un potenziale debito tecnico.

Tuttavia, la creazione di istanze di oggetti che hanno uno stato deve avvenire attraverso un Helper di fabbrica (come abbiamo visto nell'esempio) o mediante l'istanziazione diretta. Dato che Helper factory è ancora un Helper , sembra che l'uso delle classi Helper non sia ancora completamente evitato - e l'istanziazione diretta è essenzialmente di natura statica.

Quindi la mia domanda è: le classi Helper sono davvero negative? Si dovrebbe favorire un oggetto con stato su un oggetto senza stato ma con comportamento? Ma ripeto, che dire degli oggetti piccoli che non hanno un comportamento diverso dal comportamento che esiste nel loro unico metodo (ad esempio, immagina di riscrivere una grande classe% co_de chiamata Helper in classi separate come Math , Multiplication , Addition ecc.).

Quali sono alcune buone regole generali per scegliere un'opzione in una situazione Division ?

    
posta user2180613 29.08.2016 - 18:01
fonte

3 risposte

2

Vorrei scegliere la variante in cui paint_thickness è un parametro del metodo paintRoom . Può essere facoltativo:

$some_painter->paintRoom(paint_thickness);
$painter_factory->createPainter()->paintRoom(paint_thickness);

Se non vi è alcun motivo per Painter di ricordare lo spessore, quindi non memorizzarlo dentro di lui. Metterlo dentro renderebbe le cose inutilmente più statefull.

Anche perché Painter non contiene nessuno stato, non lo chiamerei classe helper. Le temute classi di helper sono quelle che vengono utilizzate come spazio dei nomi per contenere diversi metodi statici di utilità.

    
risposta data 29.08.2016 - 18:28
fonte
1

Per prima cosa vorrei sottolineare che c'è una enorme differenza tra stato immutabile e stato mutabile. È uno stato mutabile a cui bisogna fare attenzione. Lo stato immutabile non è generalmente molto diverso da nessuno stato. Quindi, nel tuo caso, il pittore che mantiene il parametro paint_thickness non è un problema, purché tale valore sia impostato durante la costruzione e quindi mantenuto di sola lettura.

La seconda cosa da notare è che le tue opzioni potrebbero avere senso quando painter viene creato e usato nello stesso posto. In tal caso, non c'è molta differenza tra le tue opzioni. Ma diventa una grande differenza quando il luogo in cui viene creato il pittore e il luogo in cui viene utilizzato sono separati. Quindi, diventa questione di quali parametri sono disponibili in quale luogo. Se paint_thickness è noto durante la creazione di painter , passa lì. Se è noto durante l'uso, passalo lì. Questo è l'intero punto dell'astrazione. Se il chiamante del metodo paint non si preoccupa di paint_thickness , non dovrebbe essere costretto a conoscerlo in modo che possa passarlo nel metodo. Forse lo spessore della vernice dipende dal colore o dal tipo di superficie e io come cliente non dovrei dirlo. Questo è il risultato di una semplice regola: l'API dell'oggetto (o dell'interfaccia) è determinata da chi lo usa, non da ciò che fornisce.

    
risposta data 29.08.2016 - 19:31
fonte
1

Le classi apolidi non sono "cattive", puoi ancora deriderle se è quello di cui hai bisogno a scopo di test. Il tipo di "classi helper" (che non è un termine con una definizione chiara e ampiamente accettata) che potrebbe essere problematico sono le classi statiche , perché non è possibile sostituirle facilmente con una simulazione quando si desidera un'unità prova altri codici che usano quelle classi.

Quindi l'anwer è chiaramente "no, non devi refactoring tutte le classi senza stato in oggetti con stato". Si potrebbe prendere in considerazione la sostituzione di classi statiche con quelle non statiche, ma solo se si vede la possibile necessità di testare altri codici che potrebbero dipendere da tali classi.

    
risposta data 29.08.2016 - 21:59
fonte

Leggi altre domande sui tag