1

我有一个代表一个非常复杂的对象的类。可以通过多种方式创建对象:增量构建、解析不同格式的文本字符串以及分析二进制文件。到目前为止,我的策略如下:

  • 让构造函数(__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

问题是班级变得非常庞大。不幸的是,我不熟悉 OOP 模式设计,但我认为对于这个问题有一个更优雅的解决方案。将生成器函数从类中取出(这样fromString(self, s)就成了superDuperFromString(s)一个好主意?

4

4 回答 4

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 建议的类方法,因为这(稍微)更复杂并且并不真正在那种情况下给你买很多,因为当这种情况发生时,你必须在这个计划中改变两个班级。当然,这也不会造成太大的伤害,因为您只需要再更改一项作业即可。

于 2010-10-14T08:02:47.070 回答
2

我不相信会这样,因为无论如何这些都与班级直接相关。

要做的是让构造函数接受参数来初始化字段(None当然默认为),然后将所有from*()方法转换为构造新对象并返回它们的类方法。

于 2010-10-14T07:56:42.937 回答
1

我不认为在类中有转换/创建方法是一个糟糕的设计。您总是可以将它移到一个单独的类中,然后您将拥有一个简单工厂,这是一种非常轻量级的设计模式。

不过我会把他们留在课堂上:)

于 2010-10-14T08:02:26.813 回答
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 解析器和文档对象模型通常是独立的对象。

于 2010-10-14T10:10:34.080 回答