Package Modeling :: Module delegation
[show private | hide private]
[frames | no frames]

Module Modeling.delegation

delegation module.

The underlying philosophy

Suppose you are about to develop a reusable component offering a secure login service. This component might need two different sorts of features: say, a mandatory procedure to check login and password, and some optional others, e.g. a special procedure to be called when the maximal number of login attempts is reached. Your component implements a default procedure to check credentials against, say, the /etc/password file, and does not have any limit on the number of retries after failure.

However and since this is a reusable component you wish that the procedure that checks credentials, the maximum number of failures and the action to be taken can be conveniently changed at runtime.

One approach consists in making these parameters settable in your component ; this will work well, but this may not be really convenient:

The delegation offers you a general mechanism to solve these two issues. The DelegateWrapper class allows the following features:

[1]of course, you may run into cases where such a design is inaccurate. All this is obviously a general discussion which might be inadequate given a specific architecture or design philosophy!
[2]except if you enforce your own rules, such as "if the delegates provides methodOne it should provide methodTwo as well", but this an other story (see DelegateWrapper.respondsTo())

How to use

We will stick on the example exposed above to explain how delegation is achieved with this module.

First, define a general "interface": a class, which defines and documents the methods a delegate for your component might implement:

class LoginComponentDelegate:
  
'Delegate for the reusable LoginComponent'

  
def checkCredentials(self, login, password):
    
'''
    Implement this method if you want to replace the default checking
    mechanism by yours. This method should raise InvalidCredentials
    if parameters 'login' and 'password' do not match an valid user.

    Parameters: both 'login' and 'password' are strings.
    '''


  
def maximumNumberOfFailures(self):
    
'''
    Implement this method to ...
    A return value equal to '-1' means no limit.
    '''


  
def numberOfFailuresHasReachedItsMaximum(self):
    
'''
    ...
    '''

The delegation module supplies one class, DelegateWrapper, to handle delegate. Its role is to inspect a delegate's interface...

To use it, your reusable component's class will look like:

class LoginComponent:

  
def __init__(self):
    
'... Initialization  ...'
    
self._delegate=DelegateWrapper(LoginComponentDelegate)
    
self._nbOfFailures=0
    
  
def setDelegate(self, object):
    
'''Sets the login component's delegate'''
    
self._delegate.setDelegateObject(object)

  
def validateCredentials(self, login, password):
    
'''
    delegate's 
    '''

    
try:
      
if self._delegate.respondsTo('checkCredentials'):
         
# DelegateWrapper acts as a proxy for the wrapped object
        
self._delegate.checkCredentials(login, password)
      
else:
        
# the default mechanism, raising InvalidCredentials if no user
        
# can be found
    
except:
      
self._nbOfFailures+=1
      
if self._nbOfFailures>=self.maximumNumberOfFailures():
        
[...you get the idea...]
      
raise

  
def maximumNumberOfFailures(self):
    
'''
    Defaults to -1 (no limit). You can change this limit by implementing
    the delegate's method 'maximumNumberOfFailures'
    '''

    
if self._delegate.respondsTo('maximumNumberOfFailures'):
      
return self._delegate.maximumNumberOfFailures()
    
else:
      
return -1

  
[...]

The delegate itself, as it will be provided by the users of the reusable component, can be an instance of any class. You should have noticed in the sample code above that the DelegateWrapper acts as a proxy : it automatically forwards any function call it cannot service (see Gotchas, below) to the wrapped object --the real delegate). Note, however, that the DelegateWrapper is not an "absolute" proxy: the forwarded messages are __only__ those which are declared in the delegate's interface.

Gotchas

There are several points which you should be aware of when using this class, some of which should be indicated in your own documentation:

Licence

Copyright (c) 2001-2004, Sebastien Bigaret
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

    * Neither the name of the software's copyright holder, Sebastien
      Bigaret, nor the names of its contributors may be used to endorse or
      promote products derived from this software without specific prior
      written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

CVS information

$Id: delegation.py,v 1.5 2004/08/02 20:54:47 sbigaret Exp $

Classes
DelegateWrapper The DelegateWrapper:

Generated by Epydoc 2.1 on Sat Mar 4 13:36:24 2006 http://epydoc.sf.net