แนวทางปฏิบัติที่ดีที่สุดของโรงงานหลาม


30

สมมติว่าฉันมีไฟล์foo.pyที่มีคลาสFoo:

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

ตอนนี้ฉันต้องการเพิ่มฟังก์ชั่นที่สร้างFooวัตถุในวิธีที่แน่นอนจากแหล่งข้อมูลดิบ ฉันควรวางมันเป็นวิธีการคงที่ใน Foo หรือเป็นฟังก์ชั่นอื่นที่แยกจากกันหรือไม่?

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

ฉันไม่รู้ว่าการอำนวยความสะดวกให้กับผู้ใช้สำคัญกว่าหรือไม่:

foo1 = foo.makeFoo(sourceData)

หรือไม่ว่าจะสำคัญกว่าหรือไม่ในการรักษาคัปปลิ้งที่ชัดเจนระหว่างเมธอดและคลาส:

foo1 = foo.Foo.fromSourceData(sourceData)

คำตอบ:


45

ตัวเลือกควรอยู่ระหว่างฟังก์ชั่นจากโรงงานและตัวเลือกที่สามแทน วิธีการเรียน:

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

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

โรงงานใน Classmethod มีข้อได้เปรียบที่เพิ่มเข้ามา ตอนนี้คุณสามารถสร้างคลาสย่อยของFooและวิธีการที่สืบทอดมาจากโรงงานจะผลิตอินสแตนซ์ของคลาสย่อยนั้น

ด้วยตัวเลือกระหว่างฟังก์ชั่นและวิธีการเรียนฉันจะเลือกวิธีการเรียน มันบันทึกไว้อย่างชัดเจนว่าวัตถุชนิดใดที่โรงงานจะผลิตโดยไม่ต้องทำให้ชัดเจนในชื่อฟังก์ชั่น สิ่งนี้ชัดเจนมากขึ้นเมื่อคุณไม่เพียง แต่มีวิธีการหลายวิธีจากโรงงาน แต่ยังมีหลายคลาสด้วย

เปรียบเทียบสองทางเลือกต่อไปนี้:

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

เมื่อเทียบกับ

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

ยิ่งไปกว่านั้นผู้ใช้ปลายทางของคุณสามารถนำเข้าเฉพาะFooคลาสและใช้สำหรับวิธีการต่าง ๆ ที่คุณสามารถสร้างได้เนื่องจากคุณมีวิธีการทั้งหมดจากโรงงานในที่เดียว:

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

datetimeโมดูล stdlib ใช้โรงงานวิธีการเรียนอย่างกว้างขวางและเป็น API นั้นมีความชัดเจนมากขึ้น


คำตอบที่ดี สำหรับข้อมูลเพิ่มเติม@classmethodดูที่ความหมายของ @classmethod และ @staticmethod สำหรับผู้เริ่มต้น
nealmcb

มุ่งเน้นไปที่ฟังก์ชั่นการใช้งานของผู้ใช้
drtf

1

ใน Python ฉันมักจะชอบลำดับชั้นของชั้นเรียนมากกว่าวิธีการจากโรงงาน สิ่งที่ต้องการ:

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)

ฉันจะใส่ลำดับชั้นทั้งหมด (และนี้) foos.pyในโมดูลการพูด ชั้นฐานFooมักจะใช้วิธีการทั้งหมด (และเช่นอินเทอร์เฟซ) และมักจะไม่ได้สร้างโดยผู้ใช้โดยตรง คลาสย่อยมีความหมายของการเขียนทับตัวสร้างฐานและระบุวิธีFooที่จะสร้าง แล้วฉันจะสร้างFooโดยการเรียกตัวสร้างที่เหมาะสมเช่น:

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

ฉันพบว่ามันสวยงามและยืดหยุ่นมากกว่าโรงงานที่สร้างขึ้นจากวิธีการคงที่วิธีการเรียนหรือฟังก์ชั่นระดับโมดูล

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.