2012-06-13 9 views
15

Tworzę klasę konstruktora zapytań, która pomoże w skonstruowaniu zapytania dla mongodb z parametrów URL. Nigdy nie robiłem zbyt wiele programowania obiektowego lub zaprojektowałem klasy do konsumpcji przez ludzi innych niż ja, oprócz używania podstawowych konstrukcji językowych i używania wbudowanych modeli Django.Czy metody klasy wewnętrznej powinny zwracać wartości lub po prostu modyfikować zmienne instancji w pythonie?

Więc mam ten QueryBuilder klasa

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 

Teraz w _build_query i będzie sprawdzenie dict params i zapełnianie query_dict tak aby przekazać go do Mongo na find() funkcję. Robiąc to, zastanawiałem się, czy istnieje absolutne poprawne podejście do tego, czy _build_query powinien zwrócić słownik, czy też powinien zmodyfikować tylko self.query_dict. Ponieważ jest to metoda wewnętrzna, zakładam, że wystarczy zmodyfikować self.query_dict. Czy istnieje właściwy sposób (pythonic) sposób podejścia do tego? Czy to po prostu głupie i nie jest to ważna decyzja projektowa? Każda pomoc jest doceniana. Dziękuję Ci.

Odpowiedz

4

Zwracanie wartości jest preferowane, ponieważ pozwala zachować wszystkie modyfikacje atrybutów w jednym miejscu (__init__). Ponadto ułatwia to późniejszy rozwój kodu; Załóżmy, że chcesz zastąpić _build_query w podklasie, a następnie metoda nadpisująca może po prostu zwrócić wartość, bez konieczności wiedzieć, który atrybut ustawić. Oto przykład:

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") 

kontra „setter wersji”:

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") 

Jeśli zdecydujesz się ustawić atrybut, można wywołać metodę _set_query dla jasności.

9

Zupełnie dobrze jest zmodyfikować self.query_dict, ponieważ cała idea programowania obiektowego polega na tym, że metody mogą modyfikować stan obiektu. Dopóki obiekt jest w stanie spójnym po zakończeniu metody, wszystko jest w porządku. Fakt, że _build_query jest metodą wewnętrzną, nie ma znaczenia. Możesz wybrać wywołanie _build_query po __init__, aby skonstruować zapytanie już po utworzeniu obiektu.

Decyzja dotyczy głównie testów. W przypadku testowania futra, wygodne jest przetestowanie każdej metody indywidualnie, bez konieczności testowania stanu całego obiektu. Ale to nie ma zastosowania w tym przypadku, ponieważ mówimy o wewnętrznej metodzie, więc sam decydujesz, kiedy wywołać tę metodę, a nie inne obiekty czy inny kod.

6

Jeśli zwrócisz cokolwiek, zasugeruję self. Wracając self z metod przykład jest wygodne dla metody łańcuchowych, ponieważ każda wartość powrotu pozwala innego połączenia metodą na tym samym obiekcie:

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

jest czasem określane jako „płynny” API.

Jednakże, podczas gdy Python pozwala metoda łańcuchowym dla niezmiennych typów, takich jak int i str, to nie dostarcza go do metod modyfikowalnych pojemnikach, takich jak list i set -by design-więc nie jest zapewne „pythonowy” to zrobić dla twojego własnego typu zmiennego. Mimo to wiele bibliotek Pythona ma "płynne" interfejsy API.

Minusem jest to, że taki interfejs API może znacznie utrudnić debugowanie.Ponieważ wykonujesz całą instrukcję lub nic z tego, nie możesz łatwo zobaczyć obiektu w punktach pośrednich w instrukcji. Oczywiście, zwykle uważam, że print jest całkowicie odpowiedni do debugowania kodu Pythona, więc po prostu wyrzuciłbym print w dowolnej metodzie, której wartością zwracaną byłam zainteresowany!

+1

'.execute()' jest niepotrzebne. Możesz uruchomić wywołanie używając '.all()', '.first()', '.one()'. – jfs

+0

Oczywiście, to dobrze, jeśli masz więcej niż jedną metodę, która faktycznie coś robi. – kindall

2

Podczas gdy metody obiektów bezpośrednio modyfikują jego stan, czasami może być korzystne, aby obiekt był jego własnym "klientem", i uzyskać dostęp pośrednio poprzez (zazwyczaj) prywatne metody dostępu.

Takie postępowanie zapewnia lepszą enkapsulację i wynikające z tego korzyści. Jednak może to być niepraktyczne, ponieważ wymagałoby to zbyt wiele dodatkowego kodu i często jest wolniejsze, co może negatywnie wpłynąć na wydajność, a zatem nie można zaakceptować kompromisu.

Powiązane problemy