ポリモーフィズムによる条件記述の置き換え (リファクタリング-p255)
リファクタリング
Published: 2019-02-16

目的

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

今回は「ポリモーフィズムによる条件記述の置き換え」について書きます。

「ポリモーフィズムによる条件記述の置き換え」 について

オブジェクトのタイプによって異なる振る舞いを選択する条件記述がある。

条件記述の各アクション部をサブクラスのオーバーライドメソッドに移動する。

元のメソッドはabstractにする

ポリモーフィズムの真髄は、オブジェクトの振る舞いがその型によって変わるとき、 明示的な条件記述を書かなくてもいいようにすることです。

変更前

<?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 Employee
{
    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('不正な従業員');
        }
    }
}

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

変更後

<?php

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

    abstract function getTypeCode();

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

    abstract public function payAmount(Employee2 $emp);
}

class Engineer2 extends EmployeeType2
{
    public function getTypeCode()
    {
        return self::ENGINEER;
    }

    public function payAmount(Employee2 $emp)
    {
        return $emp->getMonthlySalary();
    }
}

class Salesman2 extends EmployeeType2
{
    public function getTypeCode()
    {
        return self::SALESMAN;
    }

    public function payAmount(Employee2 $emp)
    {
        return $emp->getMonthlySalary() + $emp->getCommission();
    }
}

class Manager2 extends EmployeeType2
{
    public function getTypeCode()
    {
        return self::MANAGER;
    }

    public function payAmount(Employee2 $emp)
    {
        return $emp->getMonthlySalary() + $emp->getBonus();
    }
}

class Employee2
{
    private $_type;

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

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

        // 動作確認用にセット
        $this->_monthlySalary = 2000;
        $this->_commision = 1000;
        $this->_bonus = 3000;
    }

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

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

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

    public function getMonthlySalary()
    {
        return $this->_monthlySalary;
    }

    public function getCommission()
    {
        return $this->_commision;
    }

    public function getBonus()
    {
        return $this->_bonus;
    }
}

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