Python: come decidere quali metodi di classe devono fornire un comportamento (funzionalità) che influenza più classi

5

Ho una domanda sulla progettazione orientata agli oggetti che non è specifica di Python ma dal momento che il mio codice è in Python, l'ho taggato come tale. Come faccio a decidere quale delle mie classi dovrebbe essere responsabile dell'implementazione di determinate funzionalità che potrebbero interessare più di una classe? Quali sono alcune buone linee guida > (se presenti) o considerazioni?

Nell'esempio seguente di una classe Città , il comportamento di spostarsi in / da una città viene suddiviso tra la classe Città e Abitante classe. Il codice funziona. Ma mi chiedo se ci siano validi motivi per assegnare questo comportamento a una sola classe, ad es. consenti al metodo Città addpeople di aggiornare l'attributo Inhabitant città in modo che sia possibile tracciare il luogo di residenza di ciascun abitante.

Sto solo provando a capire cosa sarebbe una buona pratica o un design intelligente orientato agli oggetti.

class Town():

    def __init__(self,name):
        self.name = name
        self.inhab = []

    def addpeople(self,person):
        self.inhab.append(person)

    def removepeople(self, person):
        self.inhab.remove(person)


class Inhabitant():

    def __init__(self,name):
        self.name = name
        self.town = ""

    def move(self,town):
        if self.town:
            print self.name,"is moving from",self.town.name, "to",town.name
            self.town.removepeople(self)
        else:
            print self.name,"is moving to",town.name
        self.town = town
        town.addpeople(self)
    
posta Arne 03.01.2014 - 22:34
fonte

3 risposte

1

In the below example of a Town class, the behavior of moving to/from a town is split up among the Town class and Inhabitant class. The code works. But I am wondering if there are compelling reasons to assign this behavior to only one class, e.g. allow the Town method addpeople to update the Inhabitant attribute town so it's possible to track each inhabitant's place of residence.

C'è in realtà una buona ragione per non assegnare questo comportamento a una sola classe e mantenerla simile a ciò che si ha - nella programmazione orientata agli oggetti si vuole veramente che ogni oggetto mantenga il controllo sui propri dati. Il tuo codice può facilmente diventare molto complicato e difficile da eseguire il debug se si modificano variabili al di fuori dell'oggetto a cui appartiene la variabile. Questo fa parte del concetto di incapsulamento che è centrale nella programmazione orientata agli oggetti.

Un'altra parte di OOP che è rilevante qui è l'astrazione, cioè l'interazione tra gli oggetti nel tuo codice ha senso ad un livello più alto. Solo osservando questo codice di esempio, I indicherebbe che gli abitanti delle città sono più al centro del tuo programma rispetto alle città stesse. Se l'utente sta interagendo maggiormente con la città, I si aspetta che la città faccia più lavoro ( Town.addpeople chiama Inhabitant.move , ad esempio).

Così come stai scrivendo il tuo programma, pensa a quali entità faranno il lavoro. Quando il tuo codice combacia con la tua astrazione intuitiva di ciò che sta accadendo, sarà molto più facile per te tenere traccia di ciò che sta succedendo e non introdurre bug nel tuo codice.

Un altro consiglio che ti darò potrebbe essere utile o meno, a seconda del progetto. Se questo è un programma che stai effettivamente usando, mantenendo e aggiornando, scrivi prima un prototipo "usa e getta" . Come ha detto Joel Cornett nel commento, " Dipende dal caso d'uso ", il che significa che per conoscere il modo migliore per scrivere il codice devi capire la situazione. Parte di questo viene dall'esperienza, ma a meno che tu non stia scrivendo continuamente lo stesso programma più e più volte, ti imbatterai in una situazione che non è del tutto familiare. Scrivere un prototipo è un modo eccellente per fare esperienza con il problema specifico che stai affrontando, perché spesso non sarai in grado di capire perché in un modo o nell'altro funzionerà meglio finché non sarai abbastanza avanti nella scrittura del programma.

Una cosa che impedisce alle persone di creare un buon sistema è che vengano attaccati al loro codice. Quindi, se scegli di scrivere un prototipo, renditi conto che è un espediente - qualcosa che scrivi rapidamente solo allo scopo di ottenere una buona sensazione per il problema. Può anche aiutare se ti rendi conto mentre stai scrivendo che non ha nemmeno bisogno di lavorare, a patto che ti aiuti a capire cosa ci vorrebbe perché funzioni bene. Una volta che hai scritto il tuo prototipo, avrai una sensazione molto migliore di come dovresti aver scritto il tuo codice. Quindi puoi ricominciare e scrivere correttamente molto più velocemente.

In breve, " Dipende dal caso d'uso ", quindi assicurati di aver compreso il caso d'uso e che il codice corrisponda alla tua comprensione.

    
risposta data 04.01.2014 - 00:27
fonte
3

Q. Come faccio a decidere quale delle mie classi dovrebbe essere responsabile dell'implementazione di determinate funzionalità che potrebbero interessare più di una classe? Quali sono alcune buone linee guida (se presenti) o considerazioni?

A. Schemi (o principi) del software di assegnazione della responsabilità generale , GRASP abbreviato, consiste in linee guida per l'assegnazione di responsabilità a classi e oggetti nella progettazione orientata agli oggetti.

Queste linee guida aiutano a bilanciare i trade-off ei vantaggi per la scrittura di metodi di classe con comportamenti che influenzano più classi. In generale, stai provando a mettere i metodi in una classe che ha la maggior conoscenza su come implementare il metodo, nella classe che funge da controller per altre classi, e in un modo minimizza il numero di interdipendenze tra ogni classe.

    
risposta data 04.01.2014 - 00:11
fonte
0

Non vedo niente di sbagliato in entrambi. Questa è una domanda che ha risposte diverse per casi diversi: fai solo ciò che sembra più naturale per te.

In alcuni casi, dovresti attenersi a uno dei metodi. In altri casi, entrambi i metodi possono essere applicabili per l'utilizzo in parallelo, ma è necessario fare attenzione se si sceglie di farlo:

Assicurati che entrambi abbiano esattamente lo stesso risultato.

Ho già identificato un potenziale bug nel tuo esempio: il addpeople di Town non aggiorna town dell'abitante. Se Inhabitant non ha ancora town , viene impostato l'attributo città. Se scegli di spostare la persona usando Inhabitant.move , la città viene impostata. Questo è un modo tipico per introdurre bug, quindi fai attenzione se scegli questo percorso!

    
risposta data 03.01.2014 - 23:34
fonte

Leggi altre domande sui tag