ためすう

サブクラスによるタイプコードの置き換え (リファクタリング-p223)

2018-12-16

目的

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

今回は「サブクラスによるタイプコードの置き換え」について書きます。

「サブクラスによるタイプコードの置き換え」 について

クラスの振る舞いに影響を与える不変のタイプコードがある。

変更前

<?php

class Employee1
{
    private $_type;

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

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

変更後

<?php

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

    abstract public function getType();

    public static function create($type)
    {
        switch ($type) {
            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 Employee2
{
    public function getType()
    {
        return Employee2::ENGINEER;
    }
}

class Salesman extends Employee2
{
    public function getType()
    {
        return Employee2::SALESMAN;
    }
}

class Manager extends Employee2
{
    public function getType()
    {
        return Employee2::MANAGER;
    }
}

$e2 = Employee2::create(1);
echo $e2->getType() . "\n";

クラスによるタイプコードの置き換え (リファクタリング-p218)

2018-12-16

目的

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

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

「クラスによるタイプコードの置き換え」 について

振る舞いに影響しない数字のタイプコードを持つクラスがある。

この数字をクラスで置き換えれば、コンパイラはそのクラスについて型チェックができます。

変更前

<?php

class Person
{
    private $_bloodGroup;

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

    public function getBloodGroup()
    {
        return $this->_bloodGroup;
    }
}

// 呼び出し
$person = new Person(0);
echo $person->getBloodGroup() . "\n";

$person = new Person(2);
echo $person->getBloodGroup() . "\n";

変更後

<?php

class BloodGroup
{
    const O = 0;
    const A = 1;
    const B = 2;
    const AB = 3;

    private static $_values = [
        self::O,
        self::A,
        self::B,
        self::AB,
    ];

    private $_code;

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

    public function getCode()
    {
        return $this->_code;
    }

    private static function code($arg)
    {
        return $this->_values[$arg];
    }
}

class Person2
{
    private $_bloodGroup;

    public function __construct(BloodGroup $bloodGroup)
    {
        $this->_bloodGroup = $bloodGroup;
    }

    public function setBloodGroup(BloodGroup $arg)
    {
        $this->_bloodGroup = $arg;
    }

    public function getBloodGroupCode()
    {
        return $this->_bloodGroup->getCode();
    }
}

// 呼び出し
// PHP ではメンバ変数にクラスを設定できない
// BloodGroup クラスの呼び出し方を検討する
//
// Person クラスに BloodGroup クラスを注入する
$blood = new BloodGroup(BloodGroup::A);
$person = new Person2($blood);
echo $person->getBloodGroupCode() . "\n";

$blood = new BloodGroup(BloodGroup::B);
$person = new Person2($blood);
echo $person->getBloodGroupCode() . "\n";
<?php

データクラスによるレコードの置き換え (リファクタリング-p217)

2018-12-15

目的

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

今回は「データクラスによるレコードの置き換え」について書きます。

「データクラスによるレコードの置き換え」 について

この外部要素を扱うインターフェースを持つクラスを作ることが有効です。

見出し1 見出し2 見出し3
item1 center right
item2 abc def

<?php

class Dummy
{
    private $items = [];

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

    public function getItems()
    {
        return $this->items;
    }
}

// 古いプログラミング環境のレコード構造
$data = [
    [
        'item1', 'center', 'right'
    ],
    [
        'item2', 'abc', 'def',
    ],
];

// Dummy クラスを介してやり取りする
$dummy = new Dummy($data);
print_r($dummy->getItems());

Swift の第一引数がある関数を Objective-C で呼び出すときの注意

2018-12-09

目的

Swift で第一引数がある関数を定義して、 Objective-C で呼び出すとき何度か嵌ったのでメモしておきます。

嵌った例

Swift の関数

@objcMembers class Calc: NSObject {
    static func add(a:Int, b:Int) -> Int {
        return a + b
    }
}

Objective-C 側

[Calc add:2 b:3];

下記のようなエラーが発生しました。

No visible @interface for 'Calc' declares the selector 'add:b:'

解決した方法

Swift の関数の第一引数にラベルをつけない指定をしました。

Objective-C の場合は第一引数にラベルをつけないため、_ を指定する必要があります。

@objcMembers class Calc: NSObject
    static func add(_ a:Int, b:Int) -> Int {
        return a + b
    }
}

Objective-C 側は変えずに下記で呼び出せます

[Calc add:2 b:3];

@objcMembers について

class の前の @objcMembers は、 Swift のコードを Objective-C のコードを呼び出すために必要です。

参考

Objective-C の import について

2018-12-09

目的

iOS アプリを開発しているとき、import の記述方法が2つあるので調べました。

例えば、 Hoge というモジュールを読み込みたいとします。

@import について

@import Hoge;

Hoge 配下のモジュールが利用可能になります。

プロジェクト > Build Settings > Enable Modules が Yes になっている必要があります。

#import について

#import "<Hoge/Moge.h>"

Hoge 配下の Moge を読み込みます。

紛らわしいですが、下記でも呼べます。

@import Hoge.Moge;

参考

フィールドのカプセル化 (リファクタリング-p206)

2018-12-09

目的

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

今回は「フィールドのカプセル化」について書きます。

「フィールドのカプセル化」 について

データを公開すると、それを所有しているオブジェクトが知らないうちに、他のオブジェクトから値を変更されたりアクセスされたりします。

データが振る舞いから分離されてしまいます。

アクセサしか持たないクラスは、オブジェクト指向の利点を真に活かしていない無意味なクラスであり、オブジェクト指向技術は単に浪費するだけの恐ろしいものになってしまいます。

変更前

<?php

class SampleBefore
{
    public $_name;
}

変更後

<?php

class SampleAfter
{
    private $_name;

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

    public function setName($name)
    {
        $this->_name = $name;
    }
}

$c1 = new SampleBefore();
$c1->_name = 'name1';
echo $c1->_name . "\n";

$c2 = new SampleAfter();
$c2->setName('name2');
echo $c2->getName() . "\n";

Redshift のデータを CSV 出力する方法

2018-12-09

目的

Redshift のデータを CSV ファイル出力します。

方法

SQL を実行する

$ psql -U "[USER]" -h [xxxx.redshift.amazonaws.com] -p 5439 -d [DBNAME] -c "SELECT NOW();"

外部ファイルの SQL を実行する

SQL ファイルを用意する

$ vim ~/test.sql

SELECT NOW();
$ psql -U "[USER]" -h [xxxx.redshift.amazonaws.com] -p 5439 -d [DBNAME] -c ~/test.sql

ファイルに出力する

$ psql -U "[USER]" -h [xxxx.redshift.amazonaws.com] -p 5439 -d [DBNAME] -c ~/test.sql > ./test-result.csv

参考

単方向関連の双方向への変更 (リファクタリング-p197)

2018-12-08

目的

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

今回は「単方向関連の双方向への変更」について書きます。

「単方向関連の双方向への変更」 について

2つのクラスが互いにその特性を使う必要があるが、単方向のリンクしかない。

このイディオムは、これでいいと思えるまで何度もテストしなければならないので、扱いにくいと思います。

変更前

<?php

class Order
{
    /* @var Customer */
    public $_customer;

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

    public function setCustomer(Customer $customer)
    {
        $this->_customer = $customer;
    }
}

class Customer
{
}

$c = new Customer();
$o = new Order();
$o->setCustomer($c);

echo get_class($o->getCustomer()) . "\n";

変更後

<?php

class Order2
{
    /* @var Customer2 */
    public $_customer;

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

    public function setCustomer(Customer2 $customer)
    {
        if (!is_null($this->_customer)) {
            // このコードは動かない 擬似コード
            // $this->_customer->friendOrders()->remove($this);
        }
        $this->_customer = $customer;
        if (!is_null($this->_customer)) {
            // このコードは動かない 擬似コード
            // $this->_customer->friendOrders()->add($this);
        }
    }
}

class Customer2
{
    private $_orders = [];

    public function friendOrders()
    {
        // 関連を変更するときは、Orderオブジェクトだけがこれを使うこと
        return $this->_orders;
    }
}
$c = new Customer2();
$o = new Order2();
$o->setCustomer($c);

echo get_class($o->getCustomer()) . "\n";

オブジェクトによる配列の置き換え (リファクタリング-p186)

2018-12-08

目的

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

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

「オブジェクトによる配列の置き換え」 について

配列の各要素が、それぞれ異なる意味を持っている。

配列はデータを組織するための一般的な構造ですが、ある順で並んだ同種のオブジェクトのコレクションを保持するときだけに使うべきです。

変更前

<?php

$row = [];
$row[0] = 'Liverpool';
$row[1] = 15;

$name = $row[0];
$wins = $row[1];

echo 'name: ' . $name . "\n";
echo 'wins: ' . $wins . "\n";

変更後

<?php

class Performance
{
    private $_data = [];

    private $_name;
    private $_wins;

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

    public function setName($arg)
    {
        $this->_name = $arg;
    }

    public function getWins()
    {
        return $this->_wins;
    }

    public function setWins($arg)
    {
        $this->_wins = $arg;
    }
}

$row = new Performance();
$row->setName('Liverpool');
$row->setWins(15);

$name = $row->getName();
$wins = $row->getWins();

echo 'name: ' . $name . "\n";
echo 'wins: ' . $wins . "\n";

参照から値への変更 (リファクタリング-p183)

2018-12-02

目的

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

今回は「参照から値への変更」について書きます。

「参照から値への変更」 について

小さくて、不変で、コントロールが煩わしい参照オブジェクトがある。

値オブジェクトは、特に分散並列処理システムで有効です。

変更前

<?php

class Currency
{
    private $_code;

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

    public function getCode()
    {
        return $this->_code;
    }
}

$r = (new Currency('USD')) === (new Currency('USD'));
var_dump($r);

変更後

<?php

class Currency2
{
    private $_code;

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

    public function getCode()
    {
        return $this->_code;
    }

    public function equals($arg)
    {
        if (!($arg instanceOf Currency2)) {
            return false;
        }

        $other = $arg;
        return $this->_code === $other->_code;
    }
}

$r = (new Currency2('USD'))->equals(new Currency2('USD'));
var_dump($r);