Recombinant graphs: having two components use the same instance of a child component
From DANSE
| Table of contents |
Intro
The question has come up: how can two components in an application get the same component as a child? For example, suppose I have an application called simple, and it has two components, componentA (of class A), and componentB (of class B). Further, suppose componentA has a subcomponent, or child, componentC (of class C), and componentB also has a child, componentC. In ASCII art:
simple
/ \
/ \
compA compB
| |
| |
compC' compC''
In the naive pyre world, declaring A and B to each have a facility with factory C leads to the situation depicted above: A & B will have separate instances of class C. The question is, how can we make compA and compB have the same instance of class C? Diagramatically:
simple
/ \
/ \
compA compB
\ /
\ /
compC
As I understand it, there is no way to do this from fiddling with the inventory of the various components or the application. There are other ways to accomplish this.
Solution 1: Singleton
One is to make C a Singleton (a Singleton is a class which can only have one or none instances; when the user tries to create a second instance of a Singleton, they just get a reference to the first instance cf. Gang of Four.).
from pyre.components.Component import Component from pyre.util.Singleton import Singleton class C( Singleton, Component): ...
Solution 2: Factory implements Singleton behavior
Making C a Singleton is not the only way to solve this problem. One can also use factories creatively. For example, suppose we have an application like the one above, with C replaced by D. We can make A & B get the same instance of D by going through a third party DFactory:
class DFactory( object):
dInstance = None
def __call__( self, phrase='', *args):
if self.dInstance:
d = self.dInstance
else:
from D import D
d = D()
self.dInstance = d
return d
A & B can take advantage of this by using DFactory in inventory:
class A(Component):
class Inventory(Component.Inventory):
import pyre.inventory as inv
from DFactory import DFactory
dfactory = DFactory()
d = inv.facility("D", factory = dfactory)
# end of A's inventory
...
class B(Component):
class Inventory(Component.Inventory):
import pyre.inventory as inv
from DFactory import DFactory
dfactory = DFactory()
d = inv.facility("D", factory = dfactory)
#end of B's Inventory
Solution 3: More nuanced factory
One can get fairly fancy with the factories. Here's an example of DFactory that looks at user input to decide whether to create a new instance:
class DFactory( object):
dInstances = {}
def __call__( self, phrase='', *args):
from D import D
if phrase:
print "DFactory.__call__: phrase = %s" % phrase
if phrase in self.dInstances.keys():
d = self.dInstances[phrase]
else:
d = D()
self.dInstances[phrase] = d
else:
if 'default' in self.dInstances.keys():
d = self.dInstances['default']
else:
d = D()
self.dInstances['default'] = d
return d
In this example, the first time a user passes a particular phrase to the factory, the factory creates a D() instance, and it records that that instance goes with the phrase. The next time the user passes that particular phrase to the factory, they get back the instance associated with that phrase. To pass that phrase to the factory, use the args keyword in the facility declaration:
class A(Component):
class Inventory(Component.Inventory):
import pyre.inventory as inv
from DFactory import DFactory
dfactory = DFactory()
d = inv.facility("D", factory = dfactory)#, args=['hallo!'])
# end of inventory
...
class B(Component):
class Inventory(Component.Inventory):
import pyre.inventory as inv
from DFactory import DFactory
dfactory = DFactory()
d = inv.facility("D", factory = dfactory, args=[ 'hallo!'])
#end of Inventory
...
Warning, questions
I don't know how robust these proposed solution are. Comments? --Tim 11:28, 11 Apr 2005 (PDT)
