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.
Cheers,
Edward