[Tutor] domain logic and avoiding setters and setters.

Kent Johnson kent37 at tds.net
Tue Jul 12 14:19:16 CEST 2005


David Driver wrote:
> So I have been trying to figure out how to get around doing getters
> and setters and still have an oo way to inherit and apply business
> rules. This is what I have some up with so far. Is there any better
> way?

Hmm. I'm not sure what this has to do with getters and setters...maybe I would have to see the way you *didn't* show us to understand...

The only thing that I don't like about the code below is the need for subclasses to add rules to self._rules in the __init__() method. IMO this is fragile - it is too easy to forget to add or remove a rule from the list when the code changes.

An alternative is to use some form of auto-discovery of the rules. If you use a naming convention for the rule methods this is pretty easy. You can see an example in the unittest module; it finds all TestCase methods that start with 'test' and runs them. The code to find the tests is in Lib\unittest.py in the function getTestCaseNames().

Kent
 
> 
> class RuleViolationError(Exception):
>     def __init__(self, msg):
>         self.msg = msg
>     def __str__(self):
>         return 'Rule violated in rule object: %s' %self.msg
>     
> class someObject(object):
>     def __init__(self,a):
>         self._voilatedRules = {}
>         self._rules = [self.aRuleForA]
>         self.a = a
>         
>     def applyRules(self):
>         self._voilatedRules.clear()
>         for rule in self._rules:
>             try:
>                 rule()
>             except RuleViolationError, err:
>                 self._voilatedRules[rule.__name__] = err.msg
>         if self._voilatedRules:
>             # do something dramatic to deal with all of the
>             # voilated business rules
>             print 'error(s): '
>             print self._voilatedRules
>             
>     def aRuleForA(self):
>         try:
>             self.a = int(self.a)
>         except ValueError:
>             raise RuleViolationError('a is not an integer')
> 
> class someObjectChild(someObject):
>     def __init__(self, a,b):
>         someObject.__init__(self,a)
>         self.b = b
>         self._rules.extend((self.aRuleForB,
>                            self.anotherRuleForB))
>         
>     def aRuleForB(self):
>         # apply some business logic for d
>         # check that it starts with b, very silly
>         if not self.b.startswith('b'):
>             raise RuleViolationError('b does not start with b')
>         
>     def anotherRuleForB(self):
>         #test string for length constraints
>         min,max,l=2,15,len(self.b)
>         print min,max,l
>         if not(l>=min and l<=max):
>             raise RuleViolationError('b is out of min or max')
>         
> class someObjectChild2(someObjectChild):
>     #j ust change one of the rules for b
>     def aRuleForB(self):
>         # apply some business logic for d
>         # check that it starts with d, very silly
>         if not self.b.startswith('d'):
>             raise RuleViolationError('b does not start with d')
> 
> 
> x = someObjectChild(123,'bob jones')
> 
> # this should return nothing
> x.applyRules()
> x.b = 'obladiobladalifegoesonOOOO'
> 
> # this should fail twice on b
> x.applyRules()
> 
> y = someObjectChild2(123,'bob jones')
> y.applyRules()
> 
> z = someObjectChild('happydance','bob jones')
> z.applyRules()
> 
> This isn't near complete, but I think it gets the idea across.



More information about the Tutor mailing list