2012-06-13 10 views
15

Sto creando una classe di generazione di query che aiuterà nella costruzione di una query per mongodb dai parametri URL. Non ho mai fatto molta programmazione orientata agli oggetti, o progettato classi per il consumo da parte di persone diverse da me, oltre all'uso di costrutti linguistici di base e all'uso dei modelli incorporati di django.I metodi della classe interna restituiscono valori o semplicemente modificano le variabili di istanza in python?

così ho questa classe QueryBuilder

class QueryHelper(): 
    """ 
    Help abstract out the problem of querying over vastly 
    different dataschemas. 
    """ 

    def __init__(self, collection_name, field_name, params_dict): 
     self.query_dict = {} 
     self.params_dict = params_dict 
     db = connection.get_db() 
     self.collection = db[collection_name] 

    def _build_query(self): 
     # check params dict and build a mongo query 
     pass 

Ora in _build_query sarò controllando il dict params e popolando query_dict in modo da passare a Mongo find() funzione. Nel fare questo mi chiedevo se c'era un approccio assolutamente corretto a se _build_query dovrebbe restituire un dizionario o se dovrebbe solo modificare self.query_dict. Poiché si tratta di un metodo interno, suppongo che sia giusto modificare semplicemente self.query_dict. C'è un modo giusto (pitonico) per avvicinarti a questo? È solo sciocco e non è una decisione importante del design ?? Qualsiasi aiuto è apprezzato. Grazie.

risposta

4

È preferibile restituire un valore in quanto consente di mantenere tutti gli attributi modificabili in un'unica posizione (__init__). Inoltre, questo rende più facile estendere il codice in seguito; si supponga di voler ignorare _build_query in una sottoclasse, quindi il metodo di sovrascrittura può solo restituire un valore, senza la necessità di sapere quale attributo impostare. Ecco un esempio:

class QueryHelper(object): 
    def __init__(self, param, text): 
     self._param = param 
     self._query = self._build_query(text) 

    def _build_query(self, text): 
     return text + " and ham!" 

class RefinedQueryHelper(QueryHelper): 
    def _build_query(self, text): 
     # no need to know how the query object is going to be used 
     q = super(RefinedQueryHelper, self)._build_query() 
     return q.replace("ham", "spam") 

contro la "versione setter":

class QueryHelper(object): 
    def __init__(self, param, text): 
     self._param = param 
     self._build_query(text) 

    def _build_query(self, text): 
     self._query = text + " and ham!" 

class RefinedQueryHelper(QueryHelper): 
    def _build_query(self, text): 
     # what if we want to store the query in __query instead? 
     # then we need to modify two classes... 
     super(RefinedQueryHelper, self)._build_query() 
     self._query = self._query.replace("ham", "spam") 

Se si sceglie di impostare un attributo, si potrebbe desiderare di chiamare il metodo _set_query per chiarezza.

9

È perfettamente necessario modificare self.query_dict poiché l'intera idea della programmazione orientata agli oggetti è che i metodi possono modificare lo stato di un oggetto. Finché un oggetto si trova in uno stato coerente dopo che un metodo è finito, stai bene. Il fatto che _build_query sia un metodo interno non ha importanza. È possibile scegliere di chiamare _build_query dopo __init__ per costruire la query già al momento della creazione dell'oggetto.

La decisione è importante per gli scopi di test. A fini di test, è conveniente testare ciascun metodo singolarmente senza dover necessariamente testare lo stato dell'intero oggetto. Ma questo non si applica in questo caso perché stiamo parlando di un metodo interno, quindi solo tu decidi quando chiamare quel metodo, non altri oggetti o altro codice.

6

Se si restituisce qualcosa, suggerirei self. Tornando self dai metodi di istanza è conveniente per il metodo di concatenazione, dato che il valore di ritorno permette un'altra chiamata metodo sullo stesso oggetto:

foo.add_thing(x).add_thing(y).set_goal(42).execute() 

Questo è talvolta indicato come un API "fluente".

Tuttavia, mentre Python permette metodo di concatenamento per tipi immutabili come int e str, non prevede per metodi di contenitori mutabili come list e set progettazione in modo -da non è probabilmente "Pythonic" per farlo per il tuo tipo mutevole. Tuttavia, molte librerie Python hanno API "fluide".

Uno svantaggio è che tale API può rendere più difficile il debug.Poiché esegui l'intera istruzione o nessuna di esse, non puoi facilmente vedere l'oggetto nei punti intermedi all'interno dell'istruzione. Certo, di solito trovo lo print perfettamente adeguato per il debug di codice Python, quindi mi limito a lanciare uno print in qualsiasi metodo il cui valore di ritorno mi interessava!

+1

'.execute()' non è necessario. Puoi attivare l'esecuzione usando '.all()', '.first()', '.one()'. – jfs

+0

Certo, va bene se hai più di un metodo che effettivamente fa qualcosa. – kindall

2

Mentre è comune che i metodi di un oggetto modifichino direttamente il suo stato, a volte può essere vantaggioso che un oggetto sia il proprio "client" e accedano autonomamente tramite metodi di accesso (in genere) privati.

Ciò fornisce un incapsulamento migliore e i vantaggi che ne derivano. Tuttavia, farlo potrebbe essere poco pratico perché richiederebbe un numero eccessivo di codice aggiuntivo e spesso è più lento e potrebbe influire negativamente sulle prestazioni in modo inaccettabile, pertanto i compromessi devono essere fatti con l'ideale.

Problemi correlati