A Registry-Based Factory

Step 3: A Registry-Based Factory

The function from Step 2 works, but adding a channel means editing its body. A registry-based factory fixes that: channels register themselves into a lookup table, and the factory builds from the table. New channels plug in without touching factory code, the open/closed principle in action.

Build NotifierFactory around an internal dict:

  • register(channel, cls) stores the class cls under the channel key
  • create(channel) looks up the class and returns a new instance, or raises ValueError if the channel was never registered

Because create instantiates the stored class, any notifier. Even one defined later. Works as long as it has been registered.

Note: each test below builds its own fresh NotifierFactory, so no registration leaks between tests.

Hints
Starter code
class Notifier:
    def send(self, message):
        raise NotImplementedError


class EmailNotifier(Notifier):
    def send(self, message):
        return {"channel": "email", "message": message}


class SmsNotifier(Notifier):
    def send(self, message):
        return {"channel": "sms", "message": message}


class PushNotifier(Notifier):
    def send(self, message):
        return {"channel": "push", "message": message}


def notifier_for(channel):
    if channel == "email":
        return EmailNotifier()
    if channel == "sms":
        return SmsNotifier()
    if channel == "push":
        return PushNotifier()
    raise ValueError("unknown channel: " + str(channel))


class NotifierFactory:
    def __init__(self):
        self._registry = {}

    def register(self, channel, cls):
        """Store cls under channel so create can build it later."""
        # TODO: self._registry[channel] = cls
        pass

    def create(self, channel):
        """Build a fresh notifier from the registry, or raise ValueError."""
        # TODO:
        #   if channel not in self._registry: raise ValueError(...)
        #   return self._registry[channel]()
        pass
Write it on a laptop

The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.