对象生成器模式

1 投票
4 回答
1340 浏览
提问于 2025-04-16 05:28

我有一个类,用来表示一个比较复杂的对象。这个对象可以通过很多种方式创建,比如逐步构建、解析不同格式的文本字符串,或者分析二进制文件。到目前为止,我的策略是这样的:

  • 在构造函数(在我这里是__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 个回答

1

让这些函数返回新的、修改过的对象给调用者,这样我们就可以用 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解析器和文档对象模型通常是两个独立的对象。

2

我觉得这不太可能,因为这些都直接和类有关。

我会做的是让构造函数接收参数来初始化字段(当然默认值是None),然后把所有的from*()方法改成类方法,这样可以创建新的对象并返回它们。

3

在你的情况下,使用依赖注入和控制反转可能是个更好的主意。简单来说,就是创建一个新的类,这个类里包含了你从不同来源解析出来的所有设置。然后,子类可以定义具体的解析方法。当你创建这个类的实例时,把设置类的实例传给它:

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的建议使用类方法。这样做虽然稍微复杂一点,但在这种情况下并没有太大好处,因为一旦数据改变,你就得同时修改两个类。当然,这样做也不会造成太大麻烦,因为你只需要多改一个赋值。

撰写回答