chức năng nhà máy python thực hành tốt nhất


30

Giả sử tôi có một tệp foo.pychứa một lớp Foo:

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

Bây giờ tôi muốn thêm một hàm tạo một Foođối tượng theo một cách nhất định từ dữ liệu nguồn thô. Tôi nên đặt nó như một phương thức tĩnh trong Foo hoặc như một hàm riêng biệt khác?

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

Tôi không biết liệu điều đó có quan trọng hơn để thuận tiện cho người dùng hay không:

foo1 = foo.makeFoo(sourceData)

hoặc liệu điều quan trọng hơn là duy trì khớp nối rõ ràng giữa phương thức và lớp:

foo1 = foo.Foo.fromSourceData(sourceData)

Câu trả lời:


45

Thay vào đó, lựa chọn giữa chức năng xuất xưởng và tùy chọn thứ ba ; phương thức lớp:

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

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

Các nhà máy Classmethod có thêm lợi thế là chúng có thể kế thừa. Bây giờ bạn có thể tạo một lớp con Foovà phương thức nhà máy được kế thừa sau đó sẽ tạo ra các thể hiện của lớp con đó.

Với lựa chọn giữa một hàm và một phương thức lớp, tôi sẽ chọn phương thức lớp. Nó ghi lại khá rõ loại đối tượng mà nhà máy sẽ sản xuất, mà không cần phải làm rõ điều này trong tên hàm. Điều này trở nên rõ ràng hơn khi bạn không chỉ có nhiều phương thức xuất xưởng, mà còn có nhiều lớp.

So sánh hai lựa chọn sau:

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

so với

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

Thậm chí tốt hơn, người dùng cuối của bạn chỉ có thể nhập Foolớp và sử dụng nó cho nhiều cách khác nhau mà bạn có thể tạo, vì bạn có tất cả các phương thức xuất xưởng ở một nơi:

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

datetimeMô-đun stdlib sử dụng rộng rãi các nhà máy phương thức lớp và API của nó rõ ràng hơn cho nó.


Câu trả lời chính xác. Để biết thêm về @classmethod, hãy xem Ý nghĩa của @ classmethod và @staticmethod cho người mới bắt đầu?
nealmcb

Tập trung tuyệt vời vào chức năng của người dùng cuối
drtf

1

Trong Python, tôi thường thích một hệ thống phân cấp lớp hơn các phương thức nhà máy. Cái gì đó như:

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ôi sẽ đặt toàn bộ hệ thống phân cấp (và chỉ cái này) trong một mô-đun foos.py. Lớp cơ sở Foothường thực hiện tất cả các phương thức (và như vậy, giao diện) và thường không được người dùng trực tiếp xây dựng. Các lớp con là một phương tiện ghi đè lên hàm tạo cơ sở và chỉ định cách thức Foođược xây dựng. Sau đó, tôi sẽ tạo một Foobằng cách gọi hàm tạo thích hợp, như:

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

Tôi thấy nó thanh lịch và linh hoạt hơn các nhà máy được xây dựng từ các phương thức tĩnh, phương thức lớp hoặc các hàm cấp mô-đun.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.