mailRe: The singleton design pattern for the old 'self.relax.data' data structure.


Others Months | Index by Date | Thread Index
>>   [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Header


Content

Posted by Chris MacRaild on March 08, 2007 - 11:13:
On Thu, 2007-03-08 at 14:03 +1100, Edward d'Auvergne wrote:
On 3/7/07, Chris MacRaild <c.a.macraild@xxxxxxxxxxx> wrote:
On Wed, 2007-03-07 at 19:06 +1100, Edward d'Auvergne wrote:

Hi,

After careful thought about design patterns, I've decided to try to
use the singleton pattern for the old 'self.relax.data' data
structure.  See http://en.wikipedia.org/wiki/Singleton_pattern for
more information about this design pattern.  I'll try to use the
second simple example under the heading 'Python Borg pattern'.  The
benefit of this pattern is that each module can use the code:

from data import Data
relax_data = Data()

As Data will be a singleton if two modules used by relax instantiate
the Data class then the global 'relax_data' in both modules will be
the same instance.  Therefore if a method from the
'special_fns.model_free' module modifies the data structure, all the
other relax modules using the singleton will see the changes.  The
benefit of this pattern is that the data structure is similar in
concept to a global variable but only modules utilising it will have
it in one of their namespaces.  Also 'self.relax.data' will not need
to be passed around inside the program, simplifying the code.  What do
you think of the idea?

One issue here, identified on the wikipedia page, is that __init__() is
called for each call of Singleton(). Therefore all of the standard
__init__() stuff - inialising variables and empty containers - will
happen every time the Singleton instance is sought. This is clearly not
what we want. Ofcourse there are many ways around that by cleverly
hiding the initialisation stuff, but its starting to look like a complex
solution to what should be a simple problem.

I am aware of the execution of __init__() but this won't be an issue
in the final construction of the relax data storage object.  The
object will be multilayered.  The first layer, equivalent to the
current top level Data class, will be a dictionary type and it's
__init__() method will be empty.  There may be a few variables
(current_run, etc.) and methods defined inside this object but these
will be in the scope of the class rather than the __init__() method.

The second layer will be data containers identified by the keys of the
top level dictionary type object.  This layer's __init__() method will
not be called each time.  The current Data.__init__() method will be
shifted to this layer (actually I'll probably rename Data to
RunContainer and then create a new Data object).  I'm still unsure but
I think I'll try to leave all __init__() functions empty and define
initial variables and objects in the scope of the class, eg:

class RunContainer:
    # The molecule-residue-spin object.
    mol = MoleculeList()

    # The diffusion tensor object.
    diff = DiffContainer()

    # Molecular structure.
    structure = StructureContainer()

    def __init__():
        pass


The third layer will be data containers for specific data types.  For
example the diffusion tensor data structure, the Scientific PDB data
structures (and future molecular structure data structures), and the
molecule-residue-spin multi-object structure (each of these three
layers will consist of two objects - a list type object whereby each
element is a specific data storage object).


Something like:

class Data:
    ...
Data = Data()

in the data module, then everywhere else:

from data import Data as relax_data


By rebinding the name 'Data' with an instance of the class, we
effectively prevent accidental creation of additional instances, and the
import makes that instance availible wherever we need it.

I do think that the relax data storage object is the absolute ideal
situation for the implementation of the Singleton design pattern.
There is only one instance of this object ever and all parts of relax
should worship it - relax is designed around this object.  It is the
pillar that props up all of the program.  And the relax saved stated
is a copy of this object.

The Singleton construction is also very powerful for relax scripts.
It allows the script access to the data storage object without needing
to pass the object to the script.  Importantly it also allows the unit
test framework access to the structure without invoking relax itself.
And it is also very easy to implement cleanly - just have a look at
the current 1.3 line code base.  The relax data storage object is now
implemented as a Singleton and almost all of the relax test suite
passes.  The only failure is in one functional test whereby a saved
program state is loaded - this is because the state.load() user
function is not working yet.

I agree with all of this, I'm just pointing out that we can get the
desired behaviour out of a very simple idiom, without having to rely on
a more complex implimentation of the design pattern. Admittedly it's not
that complex, but we should always be aiming for the simplest possible.

Alex Martelli has written some good stuff on these on these topics:
http://www.aleax.it/python_mat_en.html and especially
http://www.aleax.it/Python/5ep.html [not coincidentally, the credit for
the idiom I suggest also goes to Martelli, this time the Python
Cookbook]


Chris



Cheers,

Edward





Related Messages


Powered by MHonArc, Updated Fri Mar 09 08:40:25 2007