views:

89

answers:

3

My situation is something like this:

class AbstractClass:
    def __init__(self, property_a):
        self.property_a = property_a

    @property
    def some_value(self):
        """Code here uses property_a but not property_b to determine some_value"""

    @property
    def property_a(self):
        return self.property_a

    @property
    def property_b(self):
        """Has to be implemented in subclass."""
        raise NotImplementedError


class Concrete1(AbstractClass):
    """Code here including an implementation of property_b"""


class Concrete2(AbstractClass):
    """Code here including an implementation of property_b"""

There is also a condition that if property_b is less than property_a, then property_a is invalid and thus the result of some_value is also invalid.

What I mean is this... if at any time during the object's lifetime, calling property_b would yield a number lower than calling property_a, there's a problem. However, property_b is not a field. It is determined dynamically based on n fields, where n >= 1. It is impossible to check this condition while setting property_b because property_b itself is never set. Really, setters are not anticipated to be used anywhere here. All fields are likely to be set in the constructors and then left alone. This means that property_a will be known in the constructor for AbstractClass and property_b only after evaluating the constructor for the concrete classes.

<update>
The fundamental problem is this: I need to check property_a for validity, but when property_a is set (the most intuitive place to check it), property_b is undefined.
</update>

I want to ensure that property_b is never less than property_a. How should I handle it?

Check property_a against property_b in...

  1. AbstractClass.__init__. This is actually impossible because property_b hasn't been defined yet.
  2. AbstractClass.property_a. This seems problematic because I would be throwing an exception in a getter.
  3. Each concrete implementation of property_b. Not only would I be throwing an exception in a getter, I would be duplicating code. Also property_b does not logically depend on property_a.
  4. AbstractClass.some_value. This is still throwing an exception in a getter. Also, it is logically impossible for property_b to be less than property_a all the time, not just when trying to determine some_value. Further, if subclasses decide to add other properties that depend on property_a, they may forget to check it against property_b.
  5. Concrete setters for property_b. These don't exist. property_b is sometimes determined from a value set in the constructor, sometimes calculated from multiple values. Also, code duplication.
  6. Concrete class __init__ methods. Code duplication. Someone may forget.
  7. ???

UPDATE

I think what is causing confusion is that property_b is not simply a field. property_b relies on calculations. It is really more a function than a property, if it helps to think about it that way.

A: 

I suggest that you don't raise raise NotImplementedError but call a method instead which raises this error. Subclasses then have to override that method (instead of property_b). In property_b, you call the method and then verify the result.

Rationale: You should check the value as soon as possible (which is when someone changes it). Otherwise, an illegal value could be set and cause a problem much later in the code when no one can say how it got there.

Alternatively, you could store the value and a stack trace. When the value is used, you can then check the value and print the original stack trace as "value was changed here".

Aaron Digulla
I think perhaps I was somewhat unclear. This goes to case 3. Property b is not the one that really needs checked. The validity of property b does not depend on property a at all.
Daniel Straight
Also, when property a is set, there is no way to check it. Property b has to be defined before property a can be checked.
Daniel Straight
You must do the check as soon as you have both properties. Create a validate method and call it from both setters as soon as both properties are valid. Do this in the base class and have the derived classes implement helper methods in order to avoid duplicate code.
Aaron Digulla
+3  A: 

Add a method _validate_b(self, b) (single leading underscore to indicate "protected", i.e., callable from derived classes but not by general client code) that validates the value of b (which only subclasses know) vs the value of a (which the abstract superclass does know).

Make subclasses responsible for calling the validation method whenever they're doing something that could change their internal value for b. In your very general case, the superclass cannot identify when b's value changes; since the responsibility of that change lies entirely with the subclasses, then the responsibility for triggering validation must also be with them (the superclass can perform validation, given the proposed new value of b, but it cannot know when validity must be checked). So, document that clearly.

If most subclasses fall into broad categories in terms of the strategies they use to affect their b's values, you can factor that out into either intermediate abstract classes (inheriting from the general one and specializing the general approach to "determining b", including the validation call), or (better, if feasible) some form of Strategy design pattern (typically implemented via either composition, or mix-in inheritance). But this has more to do with convenience (for concrete-subclass authors) than with "guaranteeing correctness", since a given concrete subclass might bypass such mechanisms.

If you need to, you can offer a "debug/test mode" where properties are validated redundantly on access (probably not advisable in production use, but for debugging and testing it would help catch errant subclasses that are not properly calling validation methods).

Alex Martelli
I'm going with this. Thanks.
Daniel Straight
@Daniel, you're welcome! I wish there was a way to automate this better, but I do think this more explicit approach is going to be sounder for your purposes.
Alex Martelli
+1  A: 

The golden rule is to "encapsulate" property_b so that the subclass provides part of the implementation, but not all of it.

class AbstractClass:
    def __init__(self, property_a):
        self._value_of_a = property_a

    @property
    def get_b( self ):
       self.validate_a()
       self._value_of_b = self.compute_b()
       self.validate_other_things()
       return self._value_of_b

   def compute_b( self ):
       raise NotImplementedError

It's hard to say precisely what's supposed to happen when you have two classes and you're asking about allocation of responsibility.

It appears that you want the superclass to be responsible for some aspect of the relationship between a and b

It appears that you want the the subclass to be responsible for some other aspect of computing b, and not responsible for the relationship.

If this is what you want, then your design must assign responsibility by decomposing things into the part the superclass is responsible for and the part the subclass is responsible for.

S.Lott
This gives me some other stuff to think about. Thanks for sticking with it despite my unclear explanation.
Daniel Straight
The unclear explanation (sometimes) a symptom of something simple being buried under confusion or useless complexity. 90% of the time, complexity is the result of failing to assign responsibility correctly. This is often because responsibilities or actors aren't specified simply and clearly. As you simplify and clarify your explanation, you strip away useless complexity. Keep simplifying.
S.Lott