2

I have a parent object which is composed of 2 (different) child objects. The 2 child instances need to communicate. For example, assume child1 needs to send something to child2:

import children

class Parent:
    def __init__(self):
        self.child1 = children.Child1(self.q)
        self.child2 = children.Child2(self.q)

parent = Parent()
parent.child1.send("string")

Is there a recommended pattern to achieve this?


The best I could come up with was to create a Queue between the 2 objects. This works, but it requires the receiving object to run a thread. For example:

parent.py:

import children
import queue
import time

class Parent:
    def __init__(self):
        
        self.q = queue.Queue()
        self.child1 = children.Child1(self.q)
        self.child2 = children.Child2(self.q)

parent = Parent()
parent.child1.send("string")
time.sleep(1)

children.py:

import threading

class Child1:

    def __init__(self, q):
        self.q = q

    def send(self, item):
        self.q.put(item)

class Child2:

    def __init__(self, q):
        self.q = q
        self.receiver = threading.Thread(target=self.worker, daemon=True).start()

    def worker(self):
        """Process the queue"""
        while True:
            item = self.q.get()
            print(f"{item} received")

In practice, the "items" I send in the queue are a function name and an argument list. This is basically the Command pattern as described here. But I don't like the need for receiver threads.

I would prefer if it was possible to allow one object to directly call a method in the other object. If there was an inheritance relationship between them, with a common parent, I could maybe use super() for this:

class Child1:

    def send(self, function, arguments):
        super().child2.function(arguments)

But there is no inheritance in my case: just composition. Is there a better way?

banjaxed
  • 243
  • 1
  • 6

2 Answers2

4

Just construct the children with a reference back to the parent:

class Child1:
    def __init__(self, parent):
        self.parent = parent

    def send(self, msg):
        self.parent.child2.print_out(msg)

class Child2:
    def __init__(self, parent):
        self.parent = parent

    def print_out(self, msg):
        print(msg)

class Parent:
    def __init__(self):
        self.child1 = Child1(self)
        self.child2 = Child2(self)

parent = Parent()
parent.child1.send("foo")
Aplet123
  • 33,825
  • 1
  • 29
  • 55
  • Wow, that was fast! Thank you. – banjaxed Mar 21 '21 at 15:26
  • This will be very tightly coupled, no? A `Parent` needs to know that it has a `Child1` and a `Child2`; a `Child1` needs to know it belongs to a `Parent` that also has a `Child2`; and a `Child2` needs to know it belongs to a `Parent` that also has a `Child1`. Since one of the major selling features of composition is loose coupling, won't this defeat the purpose of using composition? – ibonyun Dec 30 '22 at 20:38
0

I believe what you're looking for is the Façade Pattern.

Presumably, Parent is more than just a namespace; it knows things and does stuff. Having Child1 send something to Child2 seems like it would be a behaviour of Parent, the implementation of which is abstracted away, hidden behind the façade.

class Foo:

    def send_msg(self):
        return f'This is a message sent from {self.__class__.__name__}.'


class Bar:

    def receive_msg(self, msg):
        print(self.__class__.__name__, 'has received the following message:'
        print('\t', msg)


class Facade:

    def __init__(self):
        self.foo = Foo()
        self.bar = Bar()

    def foo_the_bar(self):
        self.bar.receive_msg(self.foo.send_msg())
>>> facade = Facade()
>>> facade.foo_the_bar()
Bar has received the following message:
     This is a message sent from Foo.
ibonyun
  • 425
  • 3
  • 11