phpdesign-patternscyclomatic-complexity

Design pattern to reduce cyclomatic complexity



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 ?


Solution

  • Create an interface like SendInvoiceInterface with a send and a shouldSend method. Then create 3 implementations:

    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.