ためすう

クラスの抽出 (リファクタリング-p149)

2018-11-18

目的

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

今回は「クラスの抽出」について書きます。

「クラスの抽出」 について

2つのクラスでなされるべき作業を1つのクラスで行なっている。

変更前

<?php

class Person
{
    private $_name;
    private $_officeAreaCode;
    private $_officeNumber;

    public function getName()
    {
        return $this->_name;
    }

    public function getTelephoneNumber()
    {
        return '(' . $this->_officeAreaCode . ')' . $this->_officeNumber;;
    }

    public function getOfficeAreaCode()
    {
        return $this->_officeAreaCode;
    }

    public function getOfficeNumber()
    {
        return $this->_officeNumber;
    }

    public function setOfficeAreaCode($arg)
    {
        $this->_officeAreaCode = $arg;
    }

    public function setOfficeNumber($arg)
    {
        $this->_officeNumber = $arg;
    }
}

$p = new Person();
$p->setOfficeAreaCode('AreaCode');
$p->setOfficeNumber('xxxxx-xxxx-xxxx');

echo $p->getTelephoneNumber() . "\n";

変更後

<?php

class TelephoneNumber2 {
    private $_number;
    private $_areaCode;

    public function getTelephoneNumber()
    {
        return '(' . $this->_areaCode . ')' . $this->_number;
    }

    public function getAreaCode()
    {
        return $this->_areaCode;
    }

    public function setAreacode($arg)
    {
        $this->_areacode = $arg;
    }

    public function getNumber()
    {
        return $this->_number;
    }

    public function setNumber($arg)
    {
        $this->_number = $arg;
    }
}

class Person2
{
    private $_name;
    private $_officeTelephone;

    public function __construct()
    {
        $this->_officeTelephone = new TelephoneNumber2();
    }

    public function getName()
    {
        return $this->_name;
    }

    public function getTelephoneNumber()
    {
        return $this->_officeTelephone->getTelephoneNumber();
    }

    public function getOfficeNumber()
    {
        $this->_officeTelephone->getNumber();
    }

    public function setOfficeNumber($arg)
    {
        $this->_officeTelephone->setNumber($arg);
    }

    public function setOfficeAreaCode($arg)
    {
        $this->_officeTelephone->setAreaCode($arg);
    }

    public function getOfficeAreaCode()
    {
        return $this->_officeTelephone->getAreaCode();
    }
}

$p = new Person2();
$p->setOfficeAreaCode('AreaCode');
$p->setOfficeNumber('xxxxx-xxxx-xxxx');

echo $p->getTelephoneNumber() . "\n";

フィールドの移動 (リファクタリング-p146)

2018-11-18

目的

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

今回は「フィールドの移動」について書きます。

「フィールドの移動」 について

クラス間で状態や振る舞いを移動するのは、リファクタリングの本質です。

フィールドを移動することを考えるのは、そのクラスのメソッドよりも別クラスのメソッドの方がそのフィールドを多く使っているのがわかったときです。

変更前

<?php

class AccountType
{
}

class Account
{
    private $_type;
    private $_interestRate;

    public function __construct(AccountType $type, $interestRate)
    {
        $this->_type = $type;
        $this->_interestRate = $interestRate;
    }

    public function interestForAmountDays($amount, $days)
    {
        return $this->_interestRate * $amount * $days / 365;
    }
}

$at = new AccountType();
$a = new Account($at, 0.03);
echo $a->interestForAmountDays(20000, 120) . "\n";

変更後

<?php

class AccountType2
{
    private $_interestRate;

    public function setInterestRate($arg)
    {
        $this->_interestRate = $arg;
    }

    public function getInterestRate()
    {
        return $this->_interestRate;
    }
}

class Account2
{
    private $_type;

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

    public function interestForAmountDays($amount, $days)
    {
        return $this->_type->getInterestRate() * $amount * $days / 365;
    }
}

$at = new AccountType2();
$at->setInterestRate(0.03);
$a = new Account2($at);
echo $a->interestForAmountDays(20000, 120) . "\n";

メソッドの移動 (リファクタリング-p142)

2018-11-17

目的

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

今回は「メソッドの移動」について書きます。

「メソッドの移動」 について

私は、クラスの振る舞いが多すぎる場合や、クラス間のやり取りが多く、結合度が高すぎる場合にメソッドを移動します

変更前

<?php

class AccountType
{
    public function isPremium()
    {
        return true;
    }
}

class Account
{
    private $_type;
    private $_daysOverdrawn;

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

    public function overdraftCharge()
    {
        if ($this->_type->isPremium()) {
            $result = 10;
            if ($this->_daysOverdrawn > 7) {
                $result += ($this->_daysOverdrawn - 7) * 0.05;
                return $result;
            }

            return $result;
        }

        return $this->_daysOverdrawn * 1.75;
    }

    public function bankCharge()
    {
        $result = 4.5;
        if ($this->_daysOverdrawn > 0) {
            $result += $this->overdraftCharge();
        }

        return $result;
    }
}

$at = new AccountType();
$a = new Account($at, 10);
echo $a->bankCharge() . "\n";

変更後

<?php

class AccountType2
{
    public function isPremium()
    {
        return true;
    }

    public function overdraftCharge($daysOverdrawn)
    {
        if ($this->isPremium()) {
            $result = 10;
            if ($daysOverdrawn > 7) {
                $result += ($daysOverdrawn - 7) * 0.05;
                return $result;
            }

            return $result;
        }

        return $daysOverdrawn * 1.75;
    }
}

class Account2
{
    private $_type;
    private $_daysOverdrawn;

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

    public function bankCharge()
    {
        $result = 4.5;
        if ($this->_daysOverdrawn > 0) {
            $result += $this->_type->overdraftCharge($this->_daysOverdrawn);
        }

        return $result;
    }
}

$at = new AccountType2();
$a = new Account2($at, 10);
echo $a->bankCharge() . "\n";

アルゴリズムの取り替え (リファクタリング-p139)

2018-11-11

目的

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

今回は「アルゴリズムの取り替え」について書きます。

「アルゴリズムの取り替え」 について

今回は本からの引用はありません。

変更前

<?php

function foundPerson($people)
{
    for ($i = 0; $i < count($people); $i++) {
        if ($people[$i] === 'Don') {
            return 'Don';
        }
        if ($people[$i] === 'John') {
            return 'John';
        }
        if ($people[$i] === 'Kent') {
            return 'Kent';
        }
    }

    return '';
}

echo foundPerson($people) . "\n";

変更後

<?php

function foundPersonVersion2($people)
{
    $candidates = ['Don', 'John', 'Kent'];

    for ($i = 0; $i < count($people); $i++) {
        if (in_array($people[$i], $candidates)) {
            return $people[$i];
        }
    }

    return '';
}

$people = ['Jon', 'jon', 'kent', 'John'];

echo foundPersonVersion2($people) . "\n";

メソッドオブジェクトによるメソッドの置き換え (リファクタリング-p135)

2018-11-11

目的

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

今回は「メソッドオブジェクトによるメソッドの置き換え」について書きます。

「メソッドオブジェクトによるメソッドの置き換え」 について

メソッドの分解を困難にするのはローカル変数です。

変更前

class Account
{
    public function gamma($inputVal, $quantity, $yearToDate)
    {
        $importantValue1 = ($inputVal * $quantity) + $this->delta();
        $importantValue2 = ($inputVal * $yearToDate) + 100;

        if (($yearToDate - $importantValue1) > 100) {
            $importantValue2 -= 20;
        }
        $importantValue3 = $importantValue2 * 7;

        return $importantValue3 - 2 * $importantValue1;
    }

    private function delta()
    {
        return 200;
    }
}

$a1 = new Account();
echo $a1->gamma(100, 3, 22) . "\n";

変更後

class Account2
{
    public function gamma($inputVal, $quantity, $yearToDate)
    {
        return (new Gamma($this, $inputVal, $quantity, $yearToDate))->compute();
    }

    public function delta()
    {
        return 200;
    }
}

class Gamma
{
    private $_account;
    private $inputVal;
    private $quantity;
    private $yearToDate;
    private $importantValue1;
    private $importantValue2;
    private $importantValue3;

    public function __construct(Account2 $source, $inputVal, $quantity, $yearToDate)
    {
        $this->_account = $source;
        $this->inputVal = $inputVal;
        $this->quantity = $quantity;
        $this->yearToDate = $yearToDate;
    }

    public function compute()
    {
        $importantValue1 = ($this->inputVal * $this->quantity) + $this->_account->delta();
        $importantValue2 = ($this->inputVal * $this->yearToDate) + 100;

        if (($this->yearToDate - $importantValue1) > 100) {
            $importantValue2 -= 20;
        }
        $importantValue3 = $importantValue2 * 7;

        return $importantValue3 - 2 * $importantValue1;
    }
}

$a2 = new Account2();
echo $a2->gamma(100, 3, 22) . "\n";

パラメータへの代入の除去 (リファクタリング-p131)

2018-11-11

目的

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

今回は「パラメータへの代入の除去」について書きます。

「パラメータへの代入の除去」 について

引数への代入が行われている。

一時変数を使う

変更前

function discountVersion1($inputVal, $quantity, $yearToDate)
{
    if ($inputVal > 50) $inputVal -= 2;

    return $inputVal;
}

echo discountVersion1(100, 2, 2018) . "\n";

変更後

function discountVersion2($inputVal, $quantity, $yearToDate)
{
    $result = $inputVal;
    if ($inputVal > 50) $result -= 2;

    return $result;
}

echo discountVersion2(100, 2, 2018) . "\n";

一時変数の分離 (リファクタリング-p128)

2018-11-11

目的

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

今回は「一時変数の分離」について書きます。

「一時変数の分離」 について

複数回代入される一時変数があるが、それは、ループ変数でも一時変数を集める変数でもない

1つの変数が2つのことに使われていると、コードを読む人が非常に混乱します。

変更前

$_height = 10;
$_width = 8;

$temp = 2 * ($_height * $_width);
echo $temp . "\n";

$temp = $_height * $_width;
echo $temp . "\n";

変更後

$_height = 10;
$_width = 8;

$perimeter = 2 * ($_height * $_width);
echo $perimeter . "\n";

$area = $_height * $_width;
echo $area . "\n";

説明用変数の導入 (リファクタリング-p124)

2018-11-10

目的

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

今回は「説明用変数の導入」について書きます。

「説明用変数の導入」 について

その式の結果または部分的な結果をその目的を説明する名前をつけた一時変数に代入する

「メソッドの抽出(110)」が適用できるときは、そっちの方を選びます

変更前

$platform = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0';
$browser = 'IE';
$resize = 1;

function wasInitialized()
{
    return true;
}

if ((strpos(strtoupper($platform), 'MAC') > -1) &&
    (strpos(strtoupper($browser), 'IE') > -1) &&
    wasInitialized() && $resize > 0) {
    // 略
    echo '変更前' . "\n";
}

変更後

$platform = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0';
$browser = 'IE';
$resize = 1;

function wasInitialized()
{
    return true;
}

$isMacOs = strpos(strtoupper($platform), 'MAC') > -1;
$isIEBrowser = strpos(strtoupper($browser), 'IE') > -1;
$wasResized = $resize > 0;

if ($isMacOs && $isIEBrowser && wasInitialized() && $wasResized) {
    // 略
    echo '変更後' . "\n";
}

問い合わせによる一時変数の置き換え (リファクタリング-p120)

2018-11-10

目的

今回は「問い合わせによる一時変数の置き換え」について書きます。

※ コードはありません。

「問い合わせによる一時変数の置き換え」について

一時変数を使って式の結果を保持している。

新たなメソッドが他のメソッドでも使えるようになる

一時変数は長いメソッドの誘因となります。

外部ストレージのアクセスがある場合は、一時変数に入れた方が良いかもしれないです。

関数の役割を絞れば、再利用しやすいというメリットはありそうです。

一時変数のインライン化 (リファクタリング-p119)

2018-10-29

目的

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

今回は「一時変数のインライン化」について書きます。

「一時変数のインライン化」 について

簡単な式によって1度だけ代入される一時変数があり、それが他のリファクタリングの障害となっている。

「一時変数のインライン化」はほとんどの場合、「問い合わせによる一時変数の置き換え(120)」の一部として使われます。

共通

class anOrder
{
    private $base_price;

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

    public function basePrice()
    {
        return $this->base_price;
    }
}

変更前

function hoge()
{
    $anOrder = new anOrder(500);
    $basePrice = $anOrder->basePrice();

    return ($basePrice > 1000);
}

var_dump(hoge());

変更後

function hogeNew()
{
    $anOrder = new anOrder(500);

    return ($anOrder->basePrice() > 1000);
}

var_dump(hogeNew());