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):
...

Complete example code

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)

Personal tools
Document Uploads/Links