python factory işlevi en iyi yöntemler


30

foo.pyBir sınıf içeren bir dosyam olduğunu varsayalım Foo:

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

Şimdi Foo, ham kaynak verilerinden belirli bir şekilde bir nesne yaratan bir işlev eklemek istiyorum . Foo'da statik bir yöntem mi yoksa ayrı bir işlev olarak mı kullanmalıyım?

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

Kullanıcılar için uygun olmanın daha önemli olup olmadığını bilmiyorum:

foo1 = foo.makeFoo(sourceData)

veya yöntem ile sınıf arasında açık bir ilişki kurmanın daha önemli olup olmadığı:

foo1 = foo.Foo.fromSourceData(sourceData)

Yanıtlar:


45

Seçim, bir fabrika fonksiyonu ile bunun yerine üçüncü bir seçenek arasında olmalıdır ; sınıf yöntemi:

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

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

Classmethod fabrikaları kalıtsal olmaları avantajına sahiptir. Şimdi bir alt sınıf oluşturabilirsiniz Foove miras alınan fabrika yöntemi bu alt sınıfın örneklerini üretecektir.

Bir işlev ve bir sınıf yöntemi arasında bir seçim yaparak, sınıf yöntemini seçerdim. İşlev adında açık bir şekilde bunu yapmak zorunda kalmadan, fabrikanın ne tür bir nesne üreteceğini açıkça açıkça belgeliyor . Yalnızca birden fazla fabrika yönteminiz değil, aynı zamanda birden fazla sınıfınız olduğunda bu çok daha belirgin hale gelir.

Aşağıdaki iki alternatifi karşılaştırın:

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

Daha da iyisi, son kullanıcınız yalnızca Foosınıfı içe aktarabilir ve tüm fabrika yöntemlerini tek bir yerde kullandığınız için onu oluşturabileceğiniz çeşitli yollar için kullanabilir:

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

Stdlib datetimemodülü , sınıf yöntemi fabrikalarını yaygın olarak kullanır ve API'si bunun için daha açıktır.


Mükemmel cevap. Daha fazla bilgi için @classmethodbkz . Yeni başlayanlar için @classmethod ve @staticmethod'un Anlamı?
nealmcb

Son kullanıcı işlevselliğine büyük odaklanma
drtf

1

Python'da genellikle fabrika yöntemlerine göre bir sınıf hiyerarşisini tercih ederim. Gibi bir şey:

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)

Tüm hiyerarşiyi (ve sadece bunu) bir modüle koyacağım foos.py. Temel sınıf Foogenellikle tüm yöntemleri (ve böylece arayüz) uygular ve genellikle doğrudan kullanıcılar tarafından oluşturulmaz. Alt sınıflar, temel yapıcının üzerine yazmanın ve nasıl Fooyapılacağını belirten bir araçtır . Sonra Foouygun yapıcıyı arayarak şöyle bir şey oluşturabilirim :

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

Statik yöntemlerden, sınıf yöntemlerinden veya modül düzeyindeki işlevlerden üretilen fabrikalardan daha zarif ve esnek buluyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.