How to communicate between sibling objects in Python, when using Composition, not Inheritance
Question:
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?
Answers:
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")
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.
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?
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")
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.