Take an order-confirmation service that builds its own email or SMS channel with an if-ladder and move the choice into a registry, so a new channel is one class and one line.
A shop's order-confirmation service tells a customer that an order has shipped, by email or by SMS. It works, but the service builds the channel itself with an if per channel, so every new channel edits the service. Replace the ladder with a registry of constructors that the service is handed, without changing a single message. Name it correctly: this is a simple factory, not the book's Factory Method (nothing is overridden) or Abstract Factory (there is no family). The follow-ups ask when you would use those instead.
The behaviour to keep, exactly:
| Thing | Value |
|---|---|
| Message text | Order <id> shipped, arriving Friday (33 characters for order 41) |
email |
email to <to>: <text> |
sms |
sms to <to>: <first 20 characters of the text>, so order 41 reads Order 41 shipped, ar |
| Any other name | an error whose message is unknown channel: <name> |
Build this API (names are Python's; JavaScript and TypeScript use camelCase where it differs):
EmailChannel() and SmsChannel(), each with send(to, text) returning the line above. They are given in the starter.ChannelRegistry() with register(name, maker), where maker is a function that takes no arguments and returns a new channel, and create(name), which calls the maker and returns what it builds. An unknown name raises ValueError (JavaScript and TypeScript: an Error) with the message above. Registering a name that already exists replaces it.ConfirmationService(registry) with confirm(channel_name, to, order_id): it asks the registry for the channel, sends the message through it and returns what send returned. It must not name any concrete channel class or channel name. JavaScript and TypeScript: confirm(channelName, to, orderId).build_registry(): the composition root for channels. It returns a new registry with "email" and "sms" registered. JavaScript and TypeScript: buildRegistry().A channel is anything with send(to, text), so tests pass their own. The tests check every message, that a new channel defined inside a test plugs in with no edit to the service, that the service really uses the registry it was handed, that two registries do not share anything, and that the ladder is gone from the service.
Write your code, then run 9 example tests and 4 hidden tests. Each run is limited to 8 seconds: an infinite loop gets stopped and the runtime restarts, your code stays.