Principi OOP e nomi dei metodi


22
class Boxer:

    def punch(self, punching_bag, strength):
        punching_bag.punch(strength)


class PunchingBag:

    def punch(self, strength):
        print "Punching bag punched with strength", strength

boxer = Boxer()
punching_bag = PunchingBag()

boxer.punch(punching_bag, 2)

Non c'è dubbio che punchsia un buon nome di metodo in caso di un pugile. Ma il nome punchè buono anche per il metodo del sacco da boxe? In entrambi i casi intendo punch come comando (cioè do punch).

Risposte:


23

Una buona regola empirica è che i nomi dei metodi dovrebbero essere verbi o predicati in modo tale che l'oggetto su cui li chiami ( selfnella convenzione Python standard, thisnella maggior parte degli altri linguaggi) diventi il ​​soggetto.

Secondo questa regola, file.closeè un po 'sbagliato, a meno che non si segua il modello mentale che il file si chiude da solo o che l' fileoggetto non rappresenta il file stesso, ma piuttosto un handle di file o una sorta di oggetto proxy.

Un sacco da boxe non si punchingBag.punch()dà mai un pugno, quindi è sbagliato in entrambi i casi. be_punched()è tecnicamente corretto, ma brutto. receive_punch()potrebbe funzionare, o handle_punch(). Un altro approccio, abbastanza popolare in JavaScript, è quello di trattare tali chiamate di metodo come eventi, e la convenzione deve seguire il nome dell'evento, con il prefisso 'on', in modo che sia on_punched()o on_hit(). In alternativa, potresti adottare la convenzione secondo la quale i participi passati indicano la voce passiva e, in base a tale convenzione, il nome del metodo sarebbe giusto punched().

Un altro aspetto da considerare è se il sacco da boxe sa davvero cosa lo ha colpito: fa la differenza se lo dai un pugno, lo batti con un bastone o ti imbatti in un camion? In tal caso, qual è la differenza? Puoi ridurre la differenza a un argomento o hai bisogno di metodi diversi per diversi tipi di punizione ricevuta? Un singolo metodo con un parametro generico è probabilmente la soluzione più elegante, perché mantiene basso il grado di accoppiamento e tale metodo non dovrebbe essere chiamato punched()o handle_punch(), ma piuttosto qualcosa di più generico receive_hit(). Con un tale metodo, è possibile implementare tutti i tipi di attori che possono colpire i sacchi da boxe, senza cambiare il sacco stesso.


4
@Artur: sì e no. I file possono (concettualmente parlando) chiudersi quando richiesto; le matrici possono ordinare da sole; ma i sacchi da boxe non si danno un pugno.
tdammers,

2
Va bene, se il nostro sacco da boxe colpisce il muro ad una velocità folle, è il muro che lo ha colpito, o è stato il sacco da boxe a subire un colpo su se stesso e da solo?

1
@tdammers: il tuo suggerimento di generalizzare potrebbe anche portare a un'interfaccia chiamata Hitable.
Jens Piegsa,

2
@Artur: Penso che sia qui che l'ipotesi OOP che ogni frase abbia un soggetto naturale e che questa idea sia applicabile alla programmazione, si rompe.
tdammers,

1
Quindi la domanda principale è. Se i file possono chiudersi da soli, gli array possono ordinarsi da soli, ecc., Perché i sacchi da boxe non possono perforarsi? C'è qualche vera differenza o è solo che nel primo caso ci siamo abituati e nel secondo no?
Clime

6

Penso che sia un problema concettuale (come pensiamo al mondo). Va bene dire:

  • Guarda, la porta si sta chiudendo. door.close()
  • Caspita, la carta si sta piegando da sola. paper.fold()
  • Che diavolo?! Quel file sulla scrivania è appena chiuso, nessuno è in giro. file.close()

È strano dire:

  • Quel sacco da boxe in palestra si è appena perforato. bag.punch()

In primo luogo, dovrebbe avere qualcosa con cui colpirsi (ad esempio le armi). Probabilmente diresti:

  • Il sacco da boxe ha iniziato a muoversi da solo come se qualcuno l'avesse preso a pugni. punching_bag.move()

Va bene per gli oggetti programmatici fare cose che normalmente gli altri fanno / con loro (nel "mondo reale"). Ma credo che dovrebbe sempre avere almeno un certo senso che la cosa lo stia facendo da sola / con se stessa . Dovresti essere in grado di immaginarlo facilmente senza diventare oscuro (come nel caso del punching_bag).


2

È questione di gusti, penso. Punching bagIl punch()metodo è almeno coerente file.close()o frame.move()nel senso di sperimentare l'azione su se stesso. La domanda più grande sarebbe: perché Boxerha del punch(something)metodo?


Mi piace il tuo punto su file.close (). Era qualcosa a cui stavo arrivando. Forse il pugile ha il metodo del pugno perché c'è anche un allenatore che sta addestrando il pugile. Beh, in effetti, stavo solo cercando di dare qualche esempio di un'azione (messaggio) che viene passata attraverso diversi oggetti con l'ultimo che è "oggetto di un'azione". Ho un leggero problema con list.append (4), account.deposit (50), file.close (), paper.fold () vs. boxer.punch (), dog.bark (), logger.log () ecc .
clime

Quando passi attraverso diversi oggetti, ci sono 2 casi: usi il contesto rilegato (sé) e non lo fai. Se lo fai, i metodi dovrebbero essere Coach.sayPunchToBoxer(), Boxer.punchNearestBag()e Bag.punch(). Altrimenti devi indovinare cosa succederà ogni volta che chiami Coach.punch(). La regola generale è: se l'oggetto che sperimenta un'azione non è specificato nel nome del metodo, allora il destinatario è quell'oggetto.

Bene, penso che vada anche bene: coach.say_punch (boxer, punching_bag), boxer.punch (punching_bag). cioè il ricevitore non è nel nome del metodo ma nei parametri.
Clime

1
Certo, intendevo dire che il ricevitore di azioni dovrebbe essere indovinabile dall'istruzione call.

2

Hai due messaggi diversi: uno per comandare un oggetto da perforare e uno per informare un oggetto che è stato perforato. Considera che un oggetto Boxer dovrà probabilmente rispondere ad entrambi . Diversamente . Questa è davvero una buona ragione per dare loro nomi diversi.

La mia inclinazione sarebbe quella di mantenere punch(boxer, object, strength)e rinominare il metodo opposto a punched. Potresti chiamarlo handle_puncho qualcosa del genere, ma poi è ancora ambiguo se gestire un comando di pugno o la notifica che è stato perforato.


Un buon punto su Boxer che necessita sia di un pugno che di qualcosa come handle_punch (sarebbe defendin questo caso particolare). Ma il sacco da boxe non sarà mai bidirezionale in quel modo. E c'è già questo file.close () ...
clime

defendè un comando. È una possibile azione che un oggetto potrebbe intraprendere in risposta punched, ma non vorresti che altri oggetti invocassero defenddirettamente.
user2313838

2

Il tuo approccio alla fine porterà a un codice molto accoppiato.

Per riassumere Eric Lippert idealmente qui, vorresti che il pugile fosse in grado di dare un pugno a molte cose. Avere il sacco da boxe come firma della funzione boxer implica che il boxer viene creato con una conoscenza immediata di Tutto (che è punibile). Inoltre dare un pugno e ricevere un pugno sono due cose MOLTO diverse, quindi non dovrebbero condividere lo stesso nome.

Preferirei modellarlo come un pugile che crea un pugno (un altro oggetto che contiene la forza, la portata, la direzione dell'attributo del pugno).

Quindi avere il sacco da boxe con un metodo come onPunch che riceve questo oggetto punch può calcolare l'effetto del punch su se stesso.

Tenendo presente questo, il nome delle cose conta molto. Deve adattarsi al modello mentale che hai della situazione. Se ti ritrovi a cercare di spiegare come può succedere qualcosa che non ha senso a prima vista, o se hai difficoltà a nominare qualcosa, forse il tuo modello è sbagliato e deve cambiare.

È difficile cambiare un modello dopo aver iniziato, le persone generalmente tendono a piegare la realtà per adattarla al modello. Il problema è che mentre pieghi le cose per adattarle (come un sacco da boxe che può dare pugni alle cose) il mondo che stai creando diventa sempre più complesso e le interazioni diventano sempre più difficili da implementare. Alla fine raggiungerai un punto in cui l'aggiunta anche della cosa più banale diventa un incubo di cambiamenti e bug. Questo debito tecnico concettuale può avere un prezzo molto elevato anche se al momento il costo iniziale era percepito come la cosa più economica da fare.


1

Questo è il problema che chiamo confusione "oggetto / soggetto" ed è abbastanza diffuso.

Le frasi generalmente hanno un soggetto che fa il verbo sul loro oggetto target .

Ora, per quanto riguarda la programmazione, l'unica cosa che effettivamente fa le cose è il computer. O praticamente un processo, filo o fibra. Gli oggetti non sono animati per impostazione predefinita. Non hanno i loro thread in esecuzione, quindi non possono davvero fare nulla.

Questo significa che i metodi operano su di essi, sono il bersaglio dell'azione e non chi fa l'azione. Ecco perché li chiamiamo "oggetti" non "soggetti"!

Quando dici File.closeche non è il file a chiudersi da solo, è il thread attualmente in esecuzione a chiudere il file. Se dici Array.sort, il thread corrente corrente ordina l'array. Se dici HttpServer.sendRequest, l'attuale thread in esecuzione invia la richiesta al server (non viceversa!). Allo stesso modo dire che PunchingBag.punchsignifica che il filo in esecuzione corrente perfora il sacco.

Questo significa che se vuoi Boxerche sia in grado di dare un pugno, allora deve essere una sottoclasse di un Threadmodo che possa fare cose come i sacchi da boxe nella sua funzione thread.

Tuttavia a volte ha anche senso dire che il sacco da boxe si punzona nel caso in cui ogni oggetto abbia il proprio thread, potresti voler evitare le condizioni di gara e implementare le chiamate di metodo come passaggio del messaggio: dai un pugno al sacco inviandolo il punchmessaggio, è un punch al thread stesso quindi ti restituisce il punch successfulmessaggio, ma questo è solo un dettaglio di implementazione.


0

Concordo sul fatto che "pugno" è un buon nome di metodo per la classe Boxer, poiché (con qualche modifica) potrebbe essere riutilizzato contro altri oggetti. Descrive anche accuratamente che un oggetto di una classe sta facendo un'azione su un altro oggetto. Tuttavia, rinominerei il metodo in "doPunch", per dimostrare più chiaramente la relazione.

Per la classe PunchingBag, tuttavia, trovo che il nome del metodo sia troppo vago o un po 'impreciso rispetto a ciò che sta accadendo nel metodo. Quando vedo "pugno", penso che qualcosa stia punzonando qualcos'altro. Tuttavia, qui, l'oggetto PunchingBag sta reagendo a un pugno da un oggetto (in questo caso, un oggetto Boxer). Quindi, rinominerei il metodo qui in "isPunched" per illustrare che l'oggetto sta reagendo a un pugno.

Tuttavia, questa è la mia interpretazione di come definirei i metodi. È tutta una questione di gusti e quali standard stai seguendo.


3
isPunchedè davvero fuorviante (più o meno, a seconda dello schema di denominazione del framework).

È normale che il metodo venga applicato a quell'oggetto, su cui viene chiamato. Cosa c'è che non va punch()?

Bene, capisco perfettamente che è necessario specificare la direzione dell'azione, ma penso che ci sia qualcosa in OOP e nella sua filosofia che lo rende inutile. Una sorta di astrazione connessa a quella famosa spiegazione secondo cui gli oggetti "si inviano messaggi" l'un l'altro.
Clime

Se non è ovvio dal nome del metodo cosa sta facendo il metodo, allora è un problema con il nome. Non è un problema con OO o qualunque paradigma venga utilizzato. Ecco perché punch () su un sacco da boxe è sbagliato in qualunque contesto tu voglia usarlo. Cosa significa dire un pugno in un sacco da boxe? È anche il motivo per cui non si può presumere da nessuna filosofia che qualcosa non sia necessario in situazioni in cui l'assunzione crea ambiguità. Ci sono casi in cui le regole pratiche funzionano e situazioni in cui non lo fanno. Se le regole empiriche funzionassero sempre, sarebbero chiamate regole (senza "empirie").
Dunk

-2

Hmmmm. sto mettendo in discussione il sacco da boxe come classe, perché non ti importa davvero del sacco da boxe - ti preoccupi dell'impatto e della forza del pugno dei pugili. quindi i metodi dovrebbero riguardare tutto ciò che sta misurando e riportando l'impatto del pugno. anche se questo proviene dal "sacco da boxe", la denominazione dovrebbe comunque rivelare la responsabilità - come punchImpactMeter ecc.


-3

Il pugile prende a pugni il sacco da boxe -> boxer.punch

Il punchingbag viene perforato dal pugile -> punchingbag.get_punch


3
questo non sembra offrire nulla di sostanziale rispetto ai punti formulati e spiegati nelle precedenti 6 risposte
moscerino del
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.