I have to send invoices to cutomers through the related customer’s API.
Each customer chooses how to be contacted (email, fax, sms…).
My problem is that each time I add a new “contact type” I increase the cyclomatic complexity of my script.
Here is my code :
<?php interface ExternalApi { public function sendByEmail(); public function sendByFax(); public function sendBySMS(); // more sending modes ... } class Api1 implements ExternalApi { public function sendByEmail() { echo __CLASS__." sending email<br>n"; } public function sendByFax() { echo __CLASS__." sending fax<br>n"; } public function sendBySMS() { echo __CLASS__." sending SMS<br>n"; } } class Customer { public const EMAIL = 'email'; public const FAX = 'fax'; public const SMS = 'sms'; public const EMAIL_AND_FAX = 'email_and_fax'; public const PHONE = 'phone'; public const PLANE = 'plane'; public const BOAT = 'boat'; public const SATELITE = 'satelite'; public const CAB = 'cab'; // more contact types... public ?string $contactType; } class Invoice { public Customer $customer; public function __construct(Customer $customer) { $this->customer = $customer; } } class InvoiceSender { private ExternalApi $api; public function __construct(ExternalApi $api) { $this->api = $api; } public function send(Invoice $invoice) { switch($invoice->customer->contactType) { case Customer::EMAIL : $this->api->sendByEmail(); break; case Customer::FAX : $this->api->sendByFax(); break; case Customer::SMS : $this->api->sendBySMS(); break; case Customer::EMAIL_AND_FAX: $this->api->sendByEmail(); $this->api->sendByFax(); break; // more cases ... } } } $customer = new Customer(); $customer->contactType = Customer::EMAIL_AND_FAX; $invoice = new Invoice($customer); $api = new Api1(); $invoiceSender = new InvoiceSender($api); $invoiceSender->send($invoice);
As you can see the switch statement of InvoiceSender::send is increased each time I add a new “contact type”.
Do you know which design pattern could solve this problem ?
Advertisement
Answer
Create an interface like SendInvoiceInterface
with a send
and a shouldSend
method. Then create 3 implementations:
EmailInvoiceSender
FaxInvoiceSender
SMSInvoiceSender
Next inject these all into your InvoiceSender
as an array. Then instead of the switch-case you can loop over all the senders and ‘ask’ them if they shouldSend
. If that evaluates to true you can call send
on them.
This way the logic stays inside each sender itself instead of the growing switch-case.