我是Python的新手,需要一些建议来实现下面的场景.
我在两个不同的注册商处有两个用于管理域名的课程.两者都具有相同的界面,例如
class RegistrarA(Object):
def __init__(self, domain):
self.domain = domain
def lookup(self):
...
def register(self, info):
...
Run Code Online (Sandbox Code Playgroud)
和
class RegistrarB(object):
def __init__(self, domain):
self.domain = domain
def lookup(self):
...
def register(self, info):
...
Run Code Online (Sandbox Code Playgroud)
我想创建一个Domain类,给定一个域名,根据扩展名加载正确的注册器类,例如
com = Domain('test.com') #load RegistrarA
com.lookup()
biz = Domain('test.biz') #load RegistrarB
biz.lookup()
Run Code Online (Sandbox Code Playgroud)
我知道这可以使用工厂函数来完成(见下文),但这是最好的方法吗?还是有更好的方法使用OOP功能?
def factory(domain):
if ...:
return RegistrarA(domain)
else:
return RegistrarB(domain)
Run Code Online (Sandbox Code Playgroud)
Ale*_*mas 75
我认为使用函数很好.
更有趣的问题是如何确定要加载哪个注册商?一种选择是拥有一个抽象的基础注册器类,具体实现子类,然后迭代__subclasses__()
调用一个is_registrar_for()
类方法:
class Registrar(object):
def __init__(self, domain):
self.domain = domain
class RegistrarA(Registrar):
@classmethod
def is_registrar_for(cls, domain):
return domain == 'foo.com'
class RegistrarB(Registrar):
@classmethod
def is_registrar_for(cls, domain):
return domain == 'bar.com'
def Domain(domain):
for cls in Registrar.__subclasses__():
if cls.is_registrar_for(domain):
return cls(domain)
raise ValueError
print Domain('foo.com')
print Domain('bar.com')
Run Code Online (Sandbox Code Playgroud)
这将允许您透明地添加新的Registrar
s并将每个支持的域的决定委托给它们.
Jef*_*uer 23
假设您需要为不同的注册商提供单独的类(虽然在您的示例中并不明显),但您的解决方案看起来还不错,尽管RegistrarA和RegistrarB可能共享功能并且可以从Abstract Base Class派生.
作为您的factory
函数的替代方法,您可以指定一个dict,映射到您的注册器类:
Registrar = {'test.com': RegistrarA, 'test.biz': RegistrarB}
Run Code Online (Sandbox Code Playgroud)
然后:
registrar = Registrar['test.com'](domain)
Run Code Online (Sandbox Code Playgroud)
一个狡辩:你不是在这里做一个类工厂,因为你正在返回实例而不是类.
bia*_*lix 11
在Python中,您可以直接更改实际的类:
class Domain(object):
def __init__(self, domain):
self.domain = domain
if ...:
self.__class__ = RegistrarA
else:
self.__class__ = RegistrarB
Run Code Online (Sandbox Code Playgroud)
接下来会有效.
com = Domain('test.com') #load RegistrarA
com.lookup()
Run Code Online (Sandbox Code Playgroud)
我成功地使用了这种方法.
您可以创建一个"包装器"类并重载其__new__()
方法以返回专用子类的实例,例如:
class Registrar(object):
def __new__(self, domain):
if ...:
return RegistrarA(domain)
elif ...:
return RegistrarB(domain)
else:
raise Exception()
Run Code Online (Sandbox Code Playgroud)
另外,为了处理非互斥条件,在其他答案中提出的问题,首先要问自己的问题是你是否想要扮演调度员角色的包装类来管理条件,或者它会将它委托给专门的类.我可以建议一个共享机制,其中专门的类定义它们自己的条件,但是包装器执行验证,就像这样(假设每个专用类公开一个类方法来验证它是否是特定域的注册器,is_registrar_for(. ..)如其他答案所示):
class Registrar(object):
registrars = [RegistrarA, RegistrarB]
def __new__(self, domain):
matched_registrars = [r for r in self.registrars if r.is_registrar_for(domain)]
if len(matched_registrars) > 1:
raise Exception('More than one registrar matched!')
elif len(matched_registrars) < 1:
raise Exception('No registrar was matched!')
else:
return matched_registrars[0](domain)
Run Code Online (Sandbox Code Playgroud)