Шаблон генератора объектов - PullRequest
1 голос
/ 14 октября 2010

У меня есть класс, который представляет довольно сложный объект. Объекты могут быть созданы многими способами: инкрементное построение, путем анализа текстовых строк в различных форматах и ​​анализа двоичных файлов. Пока что моя стратегия была следующей:

  • Пусть конструктор (в моем случае __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 ]

3 голосов
/ 14 октября 2010

Что может быть лучшей идеей в вашем случае - это внедрение зависимостей и инверсия управления. Идея состоит в том, чтобы создать еще один класс со всеми настройками, которые вы анализируете из всех этих различных источников. Затем подклассы могут определить метод для его фактического анализа. Затем, когда вы создаете экземпляр класса, передайте ему экземпляр класса параметров:

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))

Делать это таким образом имеет то преимущество, что следует более тесно придерживаться принципа единой ответственности, который гласит, что у класса должна быть только одна (вероятная) причина для изменения. Если вы измените способ хранения любой из этих строк, то класс должен измениться. Здесь мы выделяем это в один простой отдельный класс для каждого источника данных.

Если, с другой стороны, вы думаете, что данные, которые хранятся, с большей вероятностью изменятся, чем то, как они хранятся, вы можете использовать методы класса, как предлагает Игнасио, потому что это (немного) сложнее и в этом случае вам не очень дорого, потому что, когда это произойдет, вам придется поменять два класса в этой схеме. Конечно, это тоже не очень больно, потому что вам нужно всего лишь изменить еще одно назначение.

2 голосов
/ 14 октября 2010

Я не верю, что это так, поскольку все они имеют непосредственное отношение к классу.

Что бы я сделал бы , это заставил бы конструктор принимать аргументы для инициализации полей (конечно, по умолчанию None), а затем превратить все методы from*() в методы классов, которые создают новые объекты, и возвращать их .

1 голос
/ 14 октября 2010

Пусть эти функции возвращают вызывающему объекту новый измененный объект, чтобы мы могли сделать 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 и объектная модель документа, как правило, являются отдельными объектами.

1 голос
/ 14 октября 2010

Не думаю, что плохой дизайн - иметь методы преобразования / создания внутри класса.Вы всегда можете переместить его в отдельный класс, и тогда у вас будет Simple Factory, представляющий собой очень легкий шаблон проектирования.

Я бы оставил их в классе:)

...