The Factory Function

Step 2: The Factory Function

Right now a caller who wants an email notifier must write EmailNotifier(). It hard-codes the class. Change the channel and you edit every call site. The Factory pattern removes that coupling: callers name a channel as a string, and one function decides which class to build.

Add a third concrete notifier, PushNotifier (send returns {"channel": "push", "message": message}), then write the factory function.

  • notifier_for(channel) returns a fresh EmailNotifier for "email", SmsNotifier for "sms", PushNotifier for "push"
  • For any other channel it raises ValueError

Each call must return a new instance, and unknown channels must fail loudly rather than returning None.

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):
    """Return a fresh notifier matching channel, or raise ValueError."""
    # TODO:
    #   if channel == "email": return EmailNotifier()
    #   if channel == "sms":   return SmsNotifier()
    #   if channel == "push":  return PushNotifier()
    #   raise ValueError for anything else
    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.