Changing the state of other objects in the constructor: design no-no?

I was refactoring the code and found this (simplified of course, but general idea):

class Variable:
    def __init__(self):
        self.__constraints = []

    def addConstraint(self, c):
        self.__constraints.append(c)

class Constraint:
    def __init__(self, variables):
        for v in variables:
            v.addConstraint(self)

      

The fact that the Constraint constructor modifies other object states and not its own smells, a little scared to me. What other people think is normal, or is this a prime candidate for refactoring?

Edit: I'm not worried about the parent / child relationship, but that it's linked inside the constructor and not in a separate method.

+2


source to share


5 answers


I see it as a self registration template. "Hi, I'm new here, please let me join."



I'd rather have a different named method to make the purpose clearer, but I really really love this approach.

+4


source


I totally agree with @ djna's answer that the specific use case is completely finished - here it is an example of an object that should register itself with the specified set of registries "at birth".

A very sharp and extremely common subchannel of this object would be an observer object that exists strictly for the purpose of observing a given observable - great to pass the observable to the observer initializer and exactly the right way to provide class invariant "instances of this observer class are always associated with one observable" that is not would be set at birth if registration was only done after initialization was complete.

Other similar cases include, for example, a widget object that must always exist in the container window: it would be weird to implement it differently than having the widget as a parent as an initializer argument and telling the parent to "hello, me" your new child! ".



At least in the 1-many cases that you could imagine, force the parent or observable to have a method that creates and registers a new object. In many cases like this, the somewhat lesser character of this approach is found — since the constraint must be logged by several variables, it would be against the grain to ask one of them to create the constraint. The code you supply from the other side is completely natural.

Only for cases that cannot reasonably be formulated as a new "enregistering yourself" object, I would be in some doubt (there are a few other legitimate ones, such as objects that create and register other birth aids, but they are nowhere near the general).

+2


source


I agree with you. This is in the opposite direction. Maybe there is a good reason why, but this is fuzzy programming and will probably bite someone if the foot sooner or later.

0


source


This is a common use when you have two objects that are closely related (i.e. only one of them doesn't make sense). Most common case: parenting with children. When you add a child to the parent (i.e. parent.children.append(child)

), you also update the pointer frequently child.parent

.

0


source


I personally don't necessarily mind this, but ...

I would pick one usage pattern and stick with it. In your case, since Variable already has a pure addConstraint method, my preference would be to use that.

Otherwise, you will need to add a good check so that the user cannot create the Constraint, and then add it to the Variable class (thereby adding it twice).

If I say something like Constraint, I probably wouldn't do it. A constraint is like a conceptually independent entity from a variable. I see no logical reason that the same constraint could not be added to two separate variables. I'll just make it so that you build the constraint and then add them manually, specifically for this reason.

0


source







All Articles