State/Strategyによるタイプコードの置き換え (リファクタリング-p227)
リファクタリング
Published: 2018-12-23

目的

「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。

今回は「State/Strategyによるタイプコードの置き換え」について書きます。

「State/Strategyによるタイプコードの置き換え」 について

クラスの振る舞いに影響するタイプコードがあるが、サブクラス化はできない。

タイプコードがそのオブジェクトの生存期間中に変化するか、サブクラス化が不都合な理由が別にあるときに適用できます。

変更前

<?php

class Employee
{
    private $_type;

    private $_monthlySalary;
    private $_commission;
    private $_bonus;

    const ENGINEER = 0;
    const SALESMAN = 1;
    const MANAGER = 2;

    public function __construct($type)
    {
        $this->_type = $type;
    }

    public function payAmount()
    {
        $this->_monthlySalary = 2000;
        $this->_commision = 1000;
        $this->_bonus = 3000;

        switch ($this->_type) {
            case self::ENGINEER:
                return $this->_monthlySalary;
            case self::SALESMAN:
                return $this->_monthlySalary + $this->_commission;
            case self::MANAGER:
                return $this->_monthlySalary + $this->_bonus;
            default:
                throw new Exception('不正な従業員');
        }
    }
}

$e1 = new Employee(2);
echo $e1->payAmount() . "\n";

変更後

<?php

abstract class EmployeeType
{
    const ENGINEER = 0;
    const SALESMAN = 1;
    const MANAGER = 2;

    abstract function getTypeCode();

    public static function newType($code)
    {
        switch ($code) {
            case self::ENGINEER:
                return new Engineer();
            case self::SALESMAN:
                return new Salesman();
            case self::MANAGER:
                return new Manager();
            default:
                throw new Exception('不正な従業員コード');
        }
    }
}

class Engineer extends EmployeeType
{
    public function getTypeCode()
    {
        return self::ENGINEER;
    }
}

class Salesman extends EmployeeType
{
    public function getTypeCode()
    {
        return self::SALESMAN;
    }
}

class Manager extends EmployeeType
{
    public function getTypeCode()
    {
        return self::MANAGER;
    }
}

class Employee2
{
    private $_type;

    private $_monthlySalary;
    private $_commission;
    private $_bonus;

    public function __construct($type)
    {
        $this->setType($type);
    }

    public function getType()
    {
        return $this->_type->getTypeCode();
    }

    public function setType($type)
    {
        $this->_type = EmployeeType::newType($type);
    }

    public function payAmount()
    {
        $this->_monthlySalary = 2000;
        $this->_commision = 1000;
        $this->_bonus = 3000;

        switch ($this->getType()) {
            case EmployeeType::ENGINEER:
                return $this->_monthlySalary;
            case EmployeeType::SALESMAN:
                return $this->_monthlySalary + $this->_commission;
            case EmployeeType::MANAGER:
                return $this->_monthlySalary + $this->_bonus;
            default:
                throw new Exception('不正な従業員');
        }
    }
}

$e2 = new Employee2(2);
echo $e2->payAmount() . "\n";