Forum >> Principianti >> metodi di classe

Pagina: 1

Salve a tutti,

mi interessava sapere quali sono i vantaggi nell'utilizzare un metodo di classe per istanziare un oggetto con dati di ingresso differenti dall' __init__ rispetto ad utilizzare nel modulo un funzione esterna alla classe che prenda in ingresso i nuovi dati e li converta per instanziare un oggetto con il metodo __init__ della classe.

aggiungo un esempio:

class Rectangle:

   def __init__(self, width, height):
       self.width = width
       self.height = height

   def calculate_area(self):
       return self.width * self.height

   @classmethod
   def new_square(cls, side_length):
       return cls(side_length, side_length)

square1 = Rectangle.new_square(5)

#oppure

def square(side):
   return Rectangle(side,side)

square2 = square(5)

E' esattamente la stessa cosa. Il primo è un metodo di classe "factory", la seconda è una funzione "factory". In entrambi i casi stai applicando lo stesso pattern oop. Ovviamente ci sono casi in cui sembra più naturale includere la factory dentro la classe, e casi in cui invece funziona meglio fare una funzione factory separata. Però specialmente in un linguaggio come python, dove da un lato le funzioni sono first class objects, e dall'altro i metodi sono funzioni, di fatto dipende spesso dalla sensibilità "estetica" del programmatore.


Certo, c'è poi il caso in cui la tua factory deve prendere in considerazione anche altri fattori molto distanti dall'oggetto specifico della classe, prima di restituire l'istanza. In questi casi è probabilmente meglio ricorrere a una funzione factory separata, perché sembrerebbe strano inserire dentro la classe un metodo che si occupa di (e quindi deve accedere a) cose scorrelate. Per esempio se tu avessi una classe tipo
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
e una factory assurda del tipo:
import random, sqlite
def factory():
    age = random.randint(10, 50)
    db_names = sqlite.connect(...)
    ...
    name = db_names.select(...)
    return Person(name, age)
allora avrebbe sicuramente senso tenere questa factory in una funzione ad-hoc, perché non ha senso che "Person", per funzionare, debba accedere a risorse come random, sqlite, il tuo database dei nomi, etc.


Ma se ti limiti all'esempio che hai fatto, le due soluzioni sono praticamente equivalenti. Se hai capito il pattern factory (complimenti) e che cosa sono i metodi di classe (complimenti), allora questi sono problemi minori che risolverai automaticamente con un po' di pratica.







Aggiungo una cosa, che non c'entra strettamente con l'argomento ma insomma, tanto per non sbagliare.


L'esempio che hai fatto (classe Rettangolo -> factory per ottenere un Quadrato) è un esempio *sbagliato* di factory pattern. Va bene che la domanda riguardava un aspetto differente, ma l'importante è esserne comunque consapevoli. Una factory serve a produrre istanze della classe a partire da "situazioni" (genericamente parlando) disparate: ma le istanze prodotte dalla factory sono pur sempre istanze di quella classe, con il comportamento specifico dettato da quella classe. Un "Quadrato" d'altra parte non è un "Rettangolo", e una factory sulla classe "Rettangolo" non può restituirti un "Quadrato" (tranne che nel nome, ovviamente). Un Rettangolo è un oggetto su cui ci aspettiamo di fare una certa serie di manipolazioni. Ma non tutte le manipolazioni valide per il Rettangolo restano valide anche per il Quadrato. Ora, a lato pratico dipende da come implementi la tua classe Rettangolo, si capisce. Metti però che il tuo Rettangolo abbia un metodo "set_width" e "set_height" per cambiare le misure dei lati a runtime. Metti che abbia un metodo "stretch" per deformarlo, e così via. Adesso, se tu spacci la tua factory come un "generatore di Quadrati", stai ingannando il tuo utente. Se qualcuno usa la tua factory pensando di ottenere un Quadrato, ottiene invece un Rettangolo che solo accidentalmente ha i lati uguali. Ma basta che faccia qualche manipolazione sbagliata e il suo Quadrato non sarà più valido.


L'esempio di factory che ti ho fatto io è invece un esempio legittimo, per quanto stravagante. Un altro esempio legittimo sarebbe qualcosa del tipo:
def rectangle_from_string(s):
    h, w = map(int, s.split())
    return Rectangle(h, w)
per ricavare un Rettangolo parsando una stringa, cosa che potrebbe aiutarti a deserializzare dopo una serializzazione.


Un altro esempio legittimo di factory: immagina di avere un Rettangolo che vive in un sistema di coordinate cartesiane, e quindi qualcosa del genere:
class Rectangle:
    def __init__(self, x, y, h, w):
        ...
Dove "h" e "w" sono i lati e "x", "y" sono per convenzione le coordinate del vertice "nord-est". Allora, se ti serve, potresti scrivere una factory del genere:
def rectangle_from_points(x1, y1, x2, y2):
    ....
che prende in input le coordinate di due vertici opposti, fa tutti i calcoli necessari e sputa fuori l'istanza di Rectangle.



Pagina: 1



Esegui il login per scrivere una risposta.