le migliori pratiche della funzione factory python

25

Supponiamo di avere un file foo.py contenente una classe Foo :

class Foo(object):
   def __init__(self, data):
      ...

Ora voglio aggiungere una funzione che crea un oggetto Foo in un certo modo dai dati di origine grezza. Dovrei metterlo come metodo statico in Foo o come un'altra funzione separata?

class Foo(object):
   def __init__(self, data):
      ...
# option 1:
   @staticmethod
   def fromSourceData(sourceData):
      return Foo(processData(sourceData))

# option 2:
def makeFoo(sourceData):
   return Foo(processData(sourceData))

Non so se sia più importante essere convenienti per gli utenti:

foo1 = foo.makeFoo(sourceData)

o se è più importante mantenere un accoppiamento chiaro tra il metodo e la classe:

foo1 = foo.Foo.fromSourceData(sourceData)
    
posta Jason S 29.09.2012 - 15:58
fonte

2 risposte

39

La scelta dovrebbe invece tra una funzione factory e un'opzione third ; il metodo di classe:

class Foo(object):
   def __init__(self, data):
      ...

   @classmethod
   def fromSourceData(klass, sourceData):
      return klass(processData(sourceData))

Le fabbriche Classmethod hanno il vantaggio aggiunto di essere ereditabili. Ora puoi creare una sottoclasse di Foo , e il metodo factory ereditato produrrebbe quindi istanze di tale sottoclasse.

Con una scelta tra una funzione e un metodo di classe, sceglierei il metodo di classe. Documenta abbastanza chiaramente quale tipo di oggetto produrrà la fabbrica, senza doverlo esplicitare nel nome della funzione. Ciò diventa molto più chiaro quando non solo hai più metodi di fabbrica, ma anche più classi.

Confronta le seguenti due alternative:

foo.Foo.fromFrobnar(...)
foo.Foo.fromSpamNEggs(...)
foo.Foo.someComputedVersion()
foo.Bar.fromFrobnar(...)
foo.Bar.someComputedVersion()

vs.

foo.createFooFromFrobnar()
foo.createFooFromSpamNEggs()
foo.createSomeComputedVersionFoo()
foo.createBarFromFrobnar()
foo.createSomeComputedVersionBar()

Ancora meglio, il tuo utente finale potrebbe importare solo la classe Foo e usarlo per i vari modi in cui puoi crearlo, dato che hai tutti i metodi di fabbrica in un unico posto:

from foo import Foo
Foo()
Foo.fromFrobnar(...)
Foo.fromSpamNEggs(...)
Foo.someComputedVersion()

Il modulo stdlib datetime utilizza estensivamente le factory dei metodi di classe, e la sua API è molto più chiara .

    
risposta data 29.09.2012 - 19:09
fonte
1

In Python, di solito preferisco una gerarchia di classi rispetto ai metodi factory. Qualcosa come:

class Foo(object):
    def __init__(self, data):
        pass

    def method(self, param):
        pass

class SpecialFoo(Foo):
    def __init__(self, param1, param2):
        # Some processing.
        super().__init__(data)

class FromFile(Foo):
    def __init__(self, path):
        # Some processing.
        super().__init__(data)

Metterò l'intera gerarchia (e solo questa) in un modulo, diciamo foos.py . La classe base Foo di solito implementa tutti i metodi (e in quanto tale, l'interfaccia) e di solito non è costruita direttamente dagli utenti. Le sottoclassi sono una media per sovrascrivere il costruttore di base e specificare come deve essere costruito Foo . Quindi creerei un Foo chiamando il costruttore appropriato, ad esempio:

foo1 = mypackage.foos.SpecialFoo(param1, param2)
foo2 = mypackage.foos.FromFile('/path/to/foo')

Lo trovo più elegante e flessibile rispetto alle fabbriche costruite con metodi statici, metodi di classe o funzioni a livello di modulo.

    
risposta data 28.08.2018 - 11:15
fonte

Leggi altre domande sui tag