Evitare le definizioni di classi strettamente accoppiate in Python per le relazioni has-a

4

Ho il seguente codice:

class Car(object):
    def __init__(self, my_id):
        self.my_id = my_id
        self.color = color
        self.brand = brand

        self.get_color()
        self.get_brand()

    def get_color():
        return requests.get('https://example.com/{}/color'.format(self.my_id))

    def get_brand():
        return requests.get('https://example.com/{}/brand'.format(self.my_id))

def get_description(car):
    return 'My {} is {}'.format(car.brand, car.color)

def get_color(car):
    return 'My car is {}'.format(car.color)

def main():
    c = Car(123)
    print get_description(c)

Funziona piuttosto bene inizializzando inizialmente un oggetto Car che raccoglie tutti i dati necessari e quindi permettendomi di lavorare con quei dati attraverso le funzioni di prima classe.

Tuttavia, mi piacerebbe spostare tutte quelle funzioni in una classe separata Description . Questa classe, poiché utilizza una serie di dati di Car, verrebbe inizializzata come:

class Description(object):
    def __init__(self, car):
        self.car = car
        self.description_text = description_text
        self.color_text = color_text

in cui l'istanza car viene passata come argomento all'oggetto Description nell'istanza.

Mi è stato detto, tuttavia, che questo approccio non è il migliore dal momento che le due classi sono ora strettamente accoppiate.

Come dovrebbe essere rielaborato questo esempio per utilizzare due classi? Dovrebbe Description ereditare i dati da Car ? Devo istanziare Description passando tutti i dati da Car ?

    
posta mart1n 11.03.2016 - 15:20
fonte

1 risposta

3

Rielaborazione delle classi con un mixin e proprietà

How should this example be reworked to use two classes? Should Description inherit the data from Car? Should I instantiate Description by passing all data from Car?

Questo è in realtà un ottimo caso d'uso per la relazione has-a di un mixin e proprietà. Non precedere i metodi che vengono eseguiti immediatamente con get , utilizzare invece @property .

class Description_Mixin(object):
    """mixin assumes you have a brand and color"""

    @property
    def brand_and_color_description(self):
        return 'My {} is {}'.format(self.brand, self.color)

    @property
    def type_and_color_description(self):
        return 'My {} is {}'.format(type(self).__name__.lower(), self.color)

Non utilizzare le proprietà per i metodi di richiesta, utilizzare metodi regolari per significare che questa è una funzione che può (e richiederà) impiegare del tempo per completarla. Qui, il codice dice semanticamente che l'auto ha una descrizione.:

class Car(Description_Mixin):
    def __init__(self, my_id):
        self.my_id = my_id
        self.color = self.get_color()
        self.brand = self.get_brand()

    def get_color(self):
        return requests.get('https://example.com/{}/color'.format(self.my_id))

    def get_brand(self):
        return requests.get('https://example.com/{}/brand'.format(self.my_id))

E utilizzo:

def main():
    c = Car(123)
    print c.brand_and_color_description

E puoi riutilizzare il tuo mixin per altri tipi che hanno bisogno di questi tipi di descrizioni.

Follow-up - Lazy che carica gli attributi:

If I want to have cli options that only return specific parts of the description, there is no need to load up all the resources for Car. For example, if I want to, based on a cli option, only get a car's type_and_color_description, there is no need to call get_brand(). Is there a way to modify this code to account for that (i.e. not assume I have all the data from Car)?

Questa è una questione di logistica. Ecco un'implementazione che esegue questa operazione, ma rende il ritorno degli attributi di colore e marchio lenti al primo recupero:

class Car(Description_Mixin):
    def __init__(self, my_id):
        self.my_id = my_id

    @property
    def color(self):
        try:
            return self._color
        except AttributeError:
            self._color = requests.get(
              'https://example.com/{}/color'.format(self.my_id))
            return self._color

    @property
    def brand(self)
        try:
            return self._brand
        except AttributeError:
            self._brand = requests.get(
              'https://example.com/{}/brand'.format(self.my_id))   
            return self._brand
    
risposta data 11.03.2016 - 16:43
fonte

Leggi altre domande sui tag