不可撤銷地與基類搭售這樣的:
class AutoRegister(type):
def __new__(mcs, name, bases, D):
self = type.__new__(mcs, name, bases, D)
if "ID" in D: # only register if has ID attribute directly
if self.ID in self._by_id:
raise ValueError("duplicate ID: %r" % self.ID)
self._by_id[self.ID] = self
return self
class Base(object):
__metaclass__ = AutoRegister
_by_id = {}
ID = "base"
@classmethod
def from_id(cls, id):
return cls._by_id[id]()
class A(Base):
ID = "A"
class B(Base):
ID = "B"
print Base.from_id("A")
print Base.from_id("B")
或者保持不同的關注其實是分開的:
class IDFactory(object):
def __init__(self):
self._by_id = {}
def register(self, cls):
self._by_id[cls.ID] = cls
return cls
def __call__(self, id, *args, **kwds):
return self._by_id[id](*args, **kwds)
# could use a from_id function instead, as above
factory = IDFactory()
@factory.register
class Base(object):
ID = "base"
@factory.register
class A(Base):
ID = "A"
@factory.register
class B(Base):
ID = "B"
print factory("A")
print factory("B")
你可能已經對我更喜歡哪一個回升。從類層次結構單獨定義,你可以很容易地擴展和修改,如通過在兩個名字註冊(使用ID屬性只允許一個):
class IDFactory(object):
def __init__(self):
self._by_id = {}
def register(self, cls):
self._by_id[cls.ID] = cls
return cls
def register_as(self, name):
def wrapper(cls):
self._by_id[name] = cls
return cls
return wrapper
# ...
@factory.register_as("A") # doesn't require ID anymore
@factory.register # can still use ID, even mix and match
@factory.register_as("B") # imagine we got rid of B,
class A(object): # and A fulfills that roll now
ID = "A"
您也可以保持工廠實例基地「內」同時保持它的解耦:
class IDFactory(object):
#...
class Base(object):
factory = IDFactory()
@classmethod
def register(cls, subclass):
if subclass.ID in cls.factory:
raise ValueError("duplicate ID: %r" % subclass.ID)
cls.factory[subclass.ID] = subclass
return subclass
@Base.factory.register # still completely decoupled
# (it's an attribute of Base, but that can be easily
# changed without modifying the class A below)
@Base.register # alternatively more coupled, but possibly desired
class A(Base):
ID = "A"
通常我們爲此而不是超類使用單獨的工廠。爲什麼不使用更常見的** Factory **設計模式? – 2010-02-03 11:31:33
我認爲將工廠方法放在超類中會更清潔。另外,我不想爲classes/id選項進行硬編碼,我希望任何子類都是自包含的,這樣基類和工廠方法可以保持一個黑盒子。 – noio 2010-02-03 11:55:00
把工廠放在超類中遠非乾淨 - 正如你的問題所揭示的那樣。 「硬編碼的選項?」沒有意義。工廠和超級工廠一樣「黑匣子」,所以我不明白這一點。 – 2010-02-03 12:14:28