2010-10-14 15 views
1

J'ai une classe qui représente un objet assez complexe. Les objets peuvent être créés de différentes manières: construction incrémentale, en analysant des chaînes de texte dans différents formats et en analysant des fichiers binaires. Jusqu'à présent, ma stratégie était la suivante:Modèle de générateur d'objets

  • Demandez au constructeur (__init__, dans mon cas) initialiser toutes les variables internes à None

  • alimentation différentes fonctions membres pour remplir l'objet

  • Ces fonctions renvoient-elles le nouvel objet modifié à l'appelant pour que nous puissions le faire? sd = SuperDuper().fromString(s)

Par exemple:

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 

Le problème est que la classe devient très grand. Malheureusement, je ne suis pas familier avec les modèles de POO, mais je suppose qu'il existe une solution plus élégante pour ce problème. Prend les fonctions de générateur de la classe (de sorte que fromString(self, s) devient superDuperFromString(s) une bonne idée?

Répondre

3

Ce qui pourrait être une meilleure idée dans votre cas est l'injection de dépendance et l'inversion de contrôle. L'idée est de créer une autre classe qui possède tous les paramètres que vous analysez de toutes ces sources différentes. Alors les sous-classes peuvent définir la méthode pour l'analyser. Ensuite, lorsque vous instancier la classe, passer une instance de paramètres de classe à elle:

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

En procédant ainsi, a l'avantage d'adhérer plus étroitement à la seule responsabilité principale qui dit qu'une classe doit avoir un seul (susceptible de se produire) raison de changer. Si vous modifiez la façon dont vous stockez l'une de ces chaînes, la classe doit changer. Ici, nous isolons cela en une seule classe distincte pour chaque source de données. Si d'un autre côté, vous pensez que les données stockées sont plus susceptibles de changer que la façon dont elles sont stockées, vous pouvez choisir des méthodes de classe comme Ignacio le suggère car c'est (un peu) plus compliqué et ne vous achète pas vraiment beaucoup dans ce cas parce que quand cela arrive, vous devez changer deux classes dans ce régime. Bien sûr, cela ne fait pas trop mal non plus parce que vous n'aurez qu'à changer une affectation de plus.

2

Je ne crois pas que ce serait, puisque les tous se rapportent directement à la classe quel que soit.

Ce que je serait faire est le constructeur prendre des arguments pour initialiser les champs (par défaut à None bien sûr), puis tournez toutes les méthodes from*() en classmethods qui construisent de nouveaux objets et les retournent.

+0

est-ce que les méthodes from *() devraient également modifier l'objet existant, ou simplement en créer un nouveau? Si le premier est le cas, alors mon code ci-dessus est exactement ce que vous voulez dire, à l'exception de la partie '__init__' –

+0

Ils seront classmethods, donc il n'y aura aucun objet existant à modifier. –

+0

Oh, maintenant j'ai la partie de la classe –

1

Je ne pense pas que ce soit une mauvaise conception avoir des méthodes de conversion/création à l'intérieur de la classe. ve à une classe distincte et vous auriez une usine simple qui est un modèle de conception très léger.

Je les garde dans la classe :) mais

1

ont ces fonctions renvoient le nouveau objet modifié à l'appelant afin que nous puissions faire sd = SuperDuper().fromString(s)

est-ce une bonne idée rare. Alors que certaines classes de bibliothèque Python le font, ce n'est pas la meilleure approche.

Généralement, vous voulez le faire.

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) 

Il y a deux responsabilités sans rapport: l'objet et les représentations de chaîne de l'objet.

Ne pas confondre les objets et les représentations de chaînes.

Les objets et l'analyse doivent être conservés séparément. Après tout, le compilateur ne fait pas partie du code produit. Un analyseur XML et le Document Object Model sont généralement des objets distincts.