对象生成器模式
我有一个类,用来表示一个比较复杂的对象。这个对象可以通过很多种方式创建,比如逐步构建、解析不同格式的文本字符串,或者分析二进制文件。到目前为止,我的策略是这样的:
在构造函数(在我这里是
__init__
)中,把所有内部变量初始化为None
提供不同的成员函数来填充这个对象
让这些函数返回新的、修改过的对象给调用者,这样我们就可以像这样使用:
sd = SuperDuper().fromString(s)
举个例子:
class SuperDuper:
def __init__(self):
self.var1 = None
self.var2 = None
self.varN = None
## Generators
def fromStringFormat1(self, s):
#parse the string
return self
def fromStringFormat2(self, s):
#parse the string
return self
def fromAnotherLogic(self, *params):
#parse params
return self
## Modifiers (for incremental work)
def addThis(self, p):
pass
def addThat(self, p):
pass
def removeTheOtherOne(self, p):
pass
问题是这个类变得非常庞大。不幸的是,我对面向对象编程的设计模式不太熟悉,但我觉得应该有更优雅的解决方案。把生成器函数从类中拿出来(这样fromString(self, s)
就变成superDuperFromString(s)
)是个好主意吗?
4 个回答
让这些函数返回新的、修改过的对象给调用者,这样我们就可以用
sd = SuperDuper().fromString(s)
这样的方式来使用。
这样做通常不是个好主意。虽然有些Python库的类是这样做的,但这并不是最佳的方法。
一般来说,你应该这样做。
class SuperDuper( object ):
def __init__(self, var1=None, var2=None, var3=None):
self.var1 = var1
self.var2 = var2
self.varN = var3
def addThis(self, p):
pass
def addThat(self, p):
pass
def removeTheOtherOne(self, p):
pass
class ParseString( object ):
def __init__( self, someString ):
pass
def superDuper( self ):
pass
class ParseString_Format1( ParseString ):
pass
class ParseString_Format2( ParseString ):
pass
def parse_format1( string ):
parser= ParseString_Format1( string )
return parser.superDuper()
def parse_format2( string ):
parser= ParseString_Format2( string )
return parser.superDuper()
def fromAnotherLogic( **kw ):
return SuperDuper( **kw )
这里有两个不相关的责任:一个是对象本身,另一个是对象的字符串表示。
不要把对象和字符串表示混为一谈。
对象和解析过程必须分开。毕竟,编译器并不是生成的代码的一部分。XML解析器和文档对象模型通常是两个独立的对象。
我觉得这不太可能,因为这些都直接和类有关。
我会做的是让构造函数接收参数来初始化字段(当然默认值是None
),然后把所有的from*()
方法改成类方法,这样可以创建新的对象并返回它们。
在你的情况下,使用依赖注入和控制反转可能是个更好的主意。简单来说,就是创建一个新的类,这个类里包含了你从不同来源解析出来的所有设置。然后,子类可以定义具体的解析方法。当你创建这个类的实例时,把设置类的实例传给它:
class Settings(object):
var1 = None
var2 = None
var3 = None
def configure_superduper(self, superduper):
superduper.var1 = self.var1
# etc
class FromString(Settings):
def __init__(self, string):
#parse strings and set var1, etc.
class SuperDuper(object):
def __init__(self, settings): # dependency injection
settings.configure_superduper(self) # inversion of control
# other initialization stuff
sup = SuperDuper(object, FromString(some_string))
这样做的好处是更符合单一职责原则,也就是说一个类应该只有一个主要的原因去改变。如果你改变了存储这些字符串的方式,那么这个类也得跟着改变。在这里,我们把每个数据来源的内容隔离到一个简单的、独立的类里。
另一方面,如果你觉得存储的数据更可能会改变,而不是存储的方式,那么你可能想按照Ignacio的建议使用类方法。这样做虽然稍微复杂一点,但在这种情况下并没有太大好处,因为一旦数据改变,你就得同时修改两个类。当然,这样做也不会造成太大麻烦,因为你只需要多改一个赋值。