ためすう

ヌルオブジェクトの導入 (リファクタリング-p260)

2019-02-28

目的

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

今回は「ヌルオブジェクトの導入」について書きます。

「ヌルオブジェクトの導入」 について

null値のチェックが繰り返し現れる。

そのnull値をヌルオブジェクトで置き換える。

ポリモーフィズムの真髄は、オブジェクトのタイプを尋ねてその答えに基づいて振る舞いを呼び出すというものではなく、タイプを尋ねずにその振る舞いをただ呼ぶだけですませようというものです。

こうした振る舞いの移動は、ほとんどのクライアントが同じ応答を要求しているときにのみ意味があるということを忘れないでください。

変更前

<?php

class Site
{
    /* @var Customer */
    private $_customer;

    public function getCustomer()
    {
        return $this->_customer;
    }
}

class Customer
{
    public function getName()
    {
        // 空実装
    }

    public function getPlan()
    {
        // 空実装
    }

    public function getHistory()
    {
        // 空実装
    }
}

class PaymentHistory
{
    public function getWeeksDelingquentInLastYear()
    {
        return 7000;
    }
}

class BillingPlan
{
    private $_amount = 1000;

    private function __construct($amount)
    {
        $this->_amount = $amount;
    }

    public static function basic()
    {
        return new self(1000);
    }
}

$site = new Site();

$customer = $site->getCustomer();
if (is_null($customer)) {
    $plan = BillingPlan::basic();
} else {
    $plan = $customer->getPlan();
}

if (is_null($customer)) {
    $customerName = 'occupant';
} else {
    $customerName = $customer->getName();
}

if (is_null($customer)) {
    $weekDelinquent = 0;
} else {
    $weekDelinquent = $customer->getHistory()->getWeeksDelingquentInLastYear();
}

echo 'customerName: ' . $customerName . "\n";
echo 'weekDelinquent: ' . $weekDelinquent . "\n";

変更後

<?php

class Site2
{
    /* @var Customer */
    private $_customer;

    public function getCustomer()
    {
        return is_null($this->_customer) ?
            Customer2::newNull() :
            $this->_customer;
    }
}

class Customer2
{
    protected function __construct()
    {
    }

    public function getName()
    {
        // 空実装
    }

    public function getPlan()
    {
        // 空実装
    }

    public function getHistory()
    {
        // 空実装
    }

    public function newNull()
    {
        return new NullCustomer2();
    }
}

class NullCustomer2 extends Customer2
{
    public function getName()
    {
        return 'occupant';
    }

    public function getPlan()
    {
        return BillingPlan2::basic();
    }

    public function getHistory()
    {
        return PaymentHistory2::newNull();
    }
}

class PaymentHistory2
{
    public function getWeeksDelingquentInLastYear()
    {
        return 7000;
    }

    public static function newNull()
    {
        return new NullPaymentHistory2();
    }
}

class NullPaymentHistory2 extends PaymentHistory2
{
    public function getWeeksDelingquentInLastYear()
    {
        return 0;
    }
}

class BillingPlan2
{
    private $_amount = 1000;

    private function __construct($amount)
    {
        $this->_amount = $amount;
    }

    public static function basic()
    {
        return new self(1000);
    }
}

$site = new Site2();

$customer = $site->getCustomer();
$plan = $customer->getPlan();

$customerName = $customer->getName();

$weekDelinquent = $customer->getHistory()->getWeeksDelingquentInLastYear();

echo 'customerName: ' . $customerName . "\n";
echo 'weekDelinquent: ' . $weekDelinquent . "\n";

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

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";

ガード節による入れ子条件記述の置き換え (リファクタリング-p250)

2019-02-14

目的

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

今回は「ガード節による入れ子条件記述の置き換え」について書きます。

「ガード節による入れ子条件記述の置き換え」 について

メソッドに正常ルールが不明確な条件つき振る舞いがある。

逆にガード節は「めったに起きないが、起きたときには、何もしないで出て行く」ことを伝えます。

変更前

<?php

class Sample
{
    private $_isDead;
    private $_isSeparated;
    private $_isRetired;

    public function __construct($isDead, $isSeparated, $isRetired)
    {
        $this->_isDead = $isDead;
        $this->_isSeparated = $isSeparated;
        $this->_isRetired = $isRetired;
    }

    public function getPayAmount()
    {
        $result = 0;

        if ($this->_isDead) {
            $result = $this->deadAmount();
        } else {
            if ($this->_isSeparated) {
                $result = $this->separatedAmount();
            } else {
                if ($this->_isRetired) {
                    $result = $this->retiredAmount();
                } else {
                    $result = $this->normalPayAmount();
                }
            }
        }

        return $result;
    }

    private function deadAmount()
    {
        return 9000;
    }

    private function separatedAmount()
    {
        return 8000;
    }

    private function retiredAmount()
    {
        return 7000;
    }

    private function normalPayAmount()
    {
        return 6000;
    }
}

$s = new Sample(false, false, false);
echo $s->getPayAmount() . "\n";

変更後

<?php

class Sample2
{
    private $_isDead;
    private $_isSeparated;
    private $_isRetired;

    public function __construct($isDead, $isSeparated, $isRetired)
    {
        $this->_isDead = $isDead;
        $this->_isSeparated = $isSeparated;
        $this->_isRetired = $isRetired;
    }

    public function getPayAmount()
    {
        if ($this->_isDead) {
            return $this->deadAmount();
        }

        if ($this->_isSeparated) {
            return $this->separatedAmount();
        }

        if ($this->_isRetired) {
            return $this->retiredAmount();
        }

        return $this->normalPayAmount();
    }

    private function deadAmount()
    {
        return 9000;
    }

    private function separatedAmount()
    {
        return 8000;
    }

    private function retiredAmount()
    {
        return 7000;
    }

    private function normalPayAmount()
    {
        return 6000;
    }
}

$s2 = new Sample2(false, false, false);
echo $s2->getPayAmount() . "\n";

制御フラグの削除 (リファクタリング-p245)

2019-02-13

目的

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

今回は「制御フラグの削除」について書きます。

「制御フラグの削除」 について

一連の論理型の式に対して制御フラグとして機能する1つの変数がある。

代わりにbreakかreturnを使う。

変更前

<?php

function sendAlert()
{
    echo 'sendAlert' . "\n";
}

function checkSecurity($people)
{
    $found = false;
    for ($i = 0; $i < count($people); $i++) {
        if (!$found) {
            if ($people[$i] === 'Don') {
                sendAlert();
                $found = true;
            }
            if ($people[$i] === 'John') {
                sendAlert();
                $found = true;
            }
        }
    }
}

$people = [
    'BBB' , 'John', 'CCC', 'Don',
];
checkSecurity($people);

$people = [
    'BBB' , 'CCC',
];
checkSecurity($people);

変更後 (breakによる簡単な制御フラグの置き換え)

<?php

function checkSecurity2($people)
{
    for ($i = 0; $i < count($people); $i++) {
        if ($people[$i] === 'Don') {
            sendAlert();
            break;
        }
        if ($people[$i] === 'John') {
            sendAlert();
            break;
        }
    }
}

$people = [
    'BBB' , 'John', 'CCC', 'Don',
];
checkSecurity2($people);

$people = [
    'BBB' , 'CCC',
];
checkSecurity2($people);

変更後 (制御フラグの結果を伴うreturnの使用)

<?php

function checkSecurity3($people)
{
    $found = foundMiscreant3($people);

    // someLaterCode($found);
}

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

    return '';
}

$people = [
    'BBB' , 'John', 'CCC', 'Don',
];
checkSecurity3($people);

$people = [
    'BBB' , 'CCC',
];
checkSecurity3($people);

重複した条件記述の断片の統合 (リファクタリング-p243)

2019-02-12

目的

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

今回は「重複した条件記述の断片の統合」について書きます。

「重複した条件記述の断片の統合」 について

条件式のすべての分岐に同じコードの断片がある。

上記の場合に適用します。

変更前

<?php

function send()
{
    echo "send\n";
}

function isSpecialDeal()
{
    return true;
}

function main($price)
{
    if (isSpecialDeal()) {
        $total = $price * 0.95;
        send();
    } else {
        $total = $price * 0.98;
        send();
    }

    echo $total . "\n";
}

main(1000);

変更後

<?php

function send2()
{
    echo "send2\n";
}

function isSpecialDeal2()
{
    return true;
}

function main2($price)
{
    if (isSpecialDeal2()) {
        $total = $price * 0.95;
    } else {
        $total = $price * 0.98;
    }

    send2();

    echo $total . "\n";
}

main2(1000);

条件記述の統合 (リファクタリング-p240)

2019-02-11

目的

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

今回は「条件記述の統合」について書きます。

「条件記述の統合」 について

条件記述のコードを統合することが重要である理由は2つあります。

1つは、他の条件判定とorで繋げることで実際は単一の条件判定を行うことを示して、 その条件判定を明確にするためです。

もう1つの理由は、このリファクタリングはしばしば「メソッドの抽出」の下準備としての役割を持つためです。

変更前

<?php

class Dummy
{
    private $_seniority;
    private $_monthsDisabled;
    private $_isPartTime;

    public function __construct($seniority, $monthsDisabled, $isPartTime)
    {
        $this->_seniority = $seniority;
        $this->_monthsDisabled = $monthsDisabled;
        $this->_isPartTime = $isPartTime;
    }

    public function disabilityAmount()
    {
        if ($this->_seniority < 2) return 0;
        if ($this->_monthsDisabled > 12) return 0;
        if ($this->_isPartTime) return 0;

        // 後続の処理
        return 1;
    }
}

$d1 = new Dummy(2, 11, 0);
echo (int)$d1->disabilityAmount() . "\n";

変更後

<?php

class Dummy2
{
    private $_seniority;
    private $_monthsDisabled;
    private $_isPartTime;

    public function __construct($seniority, $monthsDisabled, $isPartTime)
    {
        $this->_seniority = $seniority;
        $this->_monthsDisabled = $monthsDisabled;
        $this->_isPartTime = $isPartTime;
    }

    public function disabilityAmount()
    {
        if ($this->isNotEligibleForDisability()) {
            return 0;
        }

        // 後続の処理
        return 1;
    }

    private function isNotEligibleForDisability()
    {
        return (($this->_seniority < 2) ||
            ($this->_monthsDisabled > 12) ||
            ($this->_isPartTime));
    }
}

$d2 = new Dummy2(2, 11, 0);
echo (int)$d2->disabilityAmount() . "\n";

JavaScript の正規表現

2019-02-11

目的

JavaScript の正規表現を使い、ユーザーエージェントから iPad を判別します

やってみる

iPadのユーザーエージェント を抽出する方法です。

var ipad_log = "Mozilla/5.0 (iPad; CPU OS 8_0_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12A405 Safari/600.1.4";
var a = ipad_log.match(/ipad/i);
console.log(a);

// 実行結果
["iPad", index: 13, input: "Mozilla/5.0 (iPad; CPU OS 8_0_2 like Mac OS X) App…e Gecko) Version/8.0 Mobile/12A405 Safari/600.1.4"]

/ipad/i の i は大文字小文字の区別をなくすオプションです

AWS の ELB について

2019-02-10

目的

ロードバランサについて知ることです

ELB(Elastic Load Balanning)とは

複数のAmazon EC2インスタンス間で、アプリケーショントラフィックの負荷を自動的に拡散してくれる、ロードバランサのPaaSです。

ELBの特徴

  • スケーラブル
  • 高可用性
  • トラフィックの料金が低い

Saas、Paas、IaaS とは

  • Saas

SaaSとは、「Software as a Service」の頭文字を取った略語で「サース」と読みます。 これまでパッケージ製品として提供されていたソフトウェアを、インターネット経由でサービスとして提供・利用する形態のことを指します。

  • Paas

PaaSは「Platform as a Service」の頭文字を取った略語で「パース」と読みます。 アプリケーションソフトが稼動するためのハードウェアやOSなどのプラットフォーム一式を、インターネット上のサービスとして提供する形態のことを指します。

  • IaaS

IaaSは「Infrastructure as a Service」の頭文字を取った略語で「イァース」と読みます。 情報システムの稼動に必要な仮想サーバをはじめとした機材やネットワークなどのインフラを、インターネット上のサービスとして提供する形態のことを指します。

参考

Swift で Optional 型をアンラップする方法

2019-02-10

目的

Swift で Optional 型をアンラップする方法をまとめました。

方法1. Optional Binding

var str: String? = "Hello, playground"
var strNil: String? = nil

// nil の場合は空文字を返却する
func message(argStr:String?) -> String {
    if let unwrappedStr = argStr {
        return unwrappedStr.uppercased()
    }
    return ""
}

print(message(argStr:str))
print(message(argStr:strNil))

方法2. Optional Chaining

var str: String? = "Hello, playground"
var strNil: String? = nil

print(str?.uppercased())
print(strNil?.uppercased())

方法3. Forced Unwrapping

強制的にオプショナル型をアンラップします。

これを使用するときは、nil ではないことを保証しないとエラーが発生します。

print(str!.uppercased())

// 注意!!! エラーが出る
// Fatal error: Unexpectedly found nil while unwrapping an Optional value
// print(strNil!.uppercased())

方法4 ImplicitlyUnwrappedOptional

var implicitlyStr: String! = "Hello, playground"
var implicitlyStrNil: String! = nil

print(implicitlyStr.uppercased())

// 注意!!! エラーが出る
// Fatal error: Unexpectedly found nil while unwrapping an Optional value
// print(Int(implicitlyStrNil)!)

参考

Swift の guard を使ってみる

2019-02-03

目的

Swift の guard を使ってみました。

関数呼び出し時に後続の処理にいくかどうかを判定している処理を書いてみました。

  • register() guard 文を利用
  • register2() if 文を利用
var isEnalbed = true

func register() {
    guard isEnalbed else {
        print("not registered")
        return
    }

    print("register")
}

func register2() {
    if (!isEnalbed) {
        print("not registered2")
        return
    }

    print("register2")
}

register()
register2()

出力

register
register2

メリット

guard 文は return を書かないといけないので、書き忘れを防止できます。

参考