ためすう

外部メソッドの導入 (リファクタリング-p162)

2018-11-25

目的

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

今回は「外部メソッドの導入」について書きます。

変更前

<?php

date_default_timezone_set('Asia/Tokyo');
// サーバー側のコードという想定
class PreviousEnd
{
    private $_year;
    private $_month;
    private $_date;

    public function __construct($datetime)
    {
        $this->_year = $datetime->format('Y');
        $this->_month = $datetime->format('m');
        $this->_date = $datetime->format('d');
    }

    public function getYear()
    {
        return $this->_year;
    }

    public function getMonth()
    {
        return $this->_month;
    }

    public function getDate()
    {
        return $this->_date;
    }
}

// クライアント側
$targetDate = '2018/10/11';
$previousEnd = new PreviousEnd(new DateTime($targetDate));
$newStart = (new DateTime($targetDate))->setDate(
    $previousEnd->getYear(),
    $previousEnd->getMonth(),
    $previousEnd->getDate() + 1
);

echo $newStart->format('Y/m/d') . "\n";
// 本来はサーバー側のコードを修正したい

変更後

<?php

date_default_timezone_set('Asia/Tokyo');

// サーバー側のコードという想定
class PreviousEnd
{
    private $_year;
    private $_month;
    private $_date;

    public function __construct($datetime)
    {
        $this->_year = $datetime->format('Y');
        $this->_month = $datetime->format('m');
        $this->_date = $datetime->format('d');
    }

    public function getYear()
    {
        return $this->_year;
    }

    public function getMonth()
    {
        return $this->_month;
    }

    public function getDate()
    {
        return $this->_date;
    }
}

$targetDate = '2018/10/11';
$previousEnd = new PreviousEnd(new DateTime($targetDate));
$newStart = nextDay($targetDate, $previousEnd);

function nextDay($targetDate, $arg)
{
    return (new DateTime($targetDate))->setDate(
        $arg->getYear(),
        $arg->getMonth(),
        $arg->getDate() + 1
    );
}

echo $newStart->format('Y/m/d') . "\n";

PHP で Web API のレスポンスを扱う方法

2018-11-25

目的

外部の API を利用して、レスポンスを受け取る時に良い方法はないかと

現時点での考えをまとめました。

やってみる

方法1. 配列で扱う

<?php

// API を実行して、下記のレスポンスを取得するという想定
$json = '[
    {
        "date": "2018-11-12 00:00:00",
        "result": "item1"
    },
    {
        "date": "2018-11-12 11:23:00",
        "result": "item2"
    }
]';

var_dump(json_decode($json, true)) . "\n";

出力

array(2) {
  [0]=>
  array(2) {
    ["date"]=>
    string(19) "2018-11-12 00:00:00"
    ["result"]=>
    string(5) "item1"
  }
  [1]=>
  array(2) {
    ["date"]=>
    string(19) "2018-11-12 11:23:00"
    ["result"]=>
    string(5) "item2"
  }
}

方法2. stdClass で扱う

<?php

// API を実行して、下記のレスポンスを取得するという想定
$json = '[
    {
        "date": "2018-11-12 00:00:00",
        "result": "item1"
    },
    {
        "date": "2018-11-12 11:23:00",
        "result": "item2"
    }
]';

var_dump(json_decode($json)) . "\n";

出力

array(2) {
  [0]=>
  object(stdClass)#1 (2) {
    ["date"]=>
    string(19) "2018-11-12 00:00:00"
    ["result"]=>
    string(5) "item1"
  }
  [1]=>
  object(stdClass)#2 (2) {
    ["date"]=>
    string(19) "2018-11-12 11:23:00"
    ["result"]=>
    string(5) "item2"
  }
}

方法3. 独自のクラスにセットする

方法1、方法2ともに API の仕様が変わると意図しない動作をする可能性があります。

外部 API を利用している場合は、自作のクラスで API のレスポンスを入れるのが良いかもしれません。

<?php

// API を実行して、下記のレスポンスを取得するという想定
$json = '[
    {
        "date": "2018-11-12 00:00:00",
        "result": "item1"
    },
    {
        "date": "2018-11-12 11:23:00",
        "result": "item2"
    }
]';

class Item
{
    private $date;
    private $result;

    public function __construct($data)
    {
        $this->date = !isset($data['date']) ?: $data['date'];
        $this->result = !isset($data['result']) ?: $data['result'];
    }
}

class ItemCollection extends ArrayObject
{
    public function offsetSet($offset, $value)
    {
        if (!is_object($value) || get_class($value) !== 'Item') {
            throw new InvalidArgumentException;
        }
        return parent::offsetSet($offset, $value);
    }
}

$rows = json_decode($json, true);

$data = new ItemCollection();

foreach ($rows as $row) {
    $item = new Item($row);
    $data[] = $item;
}

var_dump($data);

出力

object(ItemCollection)#1 (1) {
  ["storage":"ArrayObject":private]=>
  array(2) {
    [0]=>
    object(Item)#2 (2) {
      ["date":"Item":private]=>
      string(19) "2018-11-12 00:00:00"
      ["result":"Item":private]=>
      string(5) "item1"
    }
    [1]=>
    object(Item)#3 (2) {
      ["date":"Item":private]=>
      string(19) "2018-11-12 11:23:00"
      ["result":"Item":private]=>
      string(5) "item2"
    }
  }
}

データ構造を固定できました。

参考

iOS アプリ開発まわりの語彙整理

2018-11-24

目的

Web アプリの開発を主にやってきましたが、iOS アプリの開発も少しやることになったので、iOS アプリ開発まわりの語彙を整理します。

パッケージマネージャー

CocoaPods

Swift と Objective-C の Cocoa プロジェクトのパッケージマネージャーです。

Carthage

分散型のパッケージマネージャーです。

Swift Package Manager

公式のパッケージマネージャーです。

fastlane

CI 環境の構築で利用します。

Fabric というサービスの中に取り込まれています。

Fabric

クラッシュレポート (Crashlytics) などで構成されているサービスです。

Bitrise

モバイルアプリに特化した CI サービスです。

Firebase

Firebaseとは、すばやく高品質のモバイルアプリを開発することができるプラットフォームで、開発に役立つ数多くの機能が用意されています。

Google が提供しているサービスです。

Google は Fabric の買収も行ったようです。

また、Google Analytics との連携も行うことが出来ます。

参考

Repro と F.O.X

2018-11-24

目的

モバイルアプリの開発をする上で、耳にする Repro と F.O.X について書きます。

Repro について

「アナリティクス」機能

ダウンロード数やアクセス数を計測する定量分析、ユーザーがアプリを使っている様子を観察する定性分析

「マーケティング」機能

ターゲットユーザーに対して、プッシュ通知やアプリ内メッセージ送信など

F.O.X について

スマートフォンアプリ向け広告効果計測

最後に

下記によれば、F.O.X で計測した広告効果をもとに

Repro のプッシュ通知、アプリ内メッセージなどが利用できるようです。

スマートフォン効果計測ツール「F.O.X」がモバイルアプリ向け分析・マーケティングツール「Repro」と連携 〜ユーザー分析に基づいたアプリ内マーケティングを支援〜 | CyberZ|スマートフォン広告マーケティング事業

参考

MySQL で月初と月末の日付を取得する方法

2018-11-18

目的

MySQL からデータを月次で抽出する必要がありました。

ある日にちから、月初と月末を取得する方法を調べました。

方法

SET @TARGET_DATE='2018/11/18';

-- @TARGET_DATE 月の月初
SELECT DATE_FORMAT(@TARGET_DATE, '%Y-%m-01');

-- @TARGET_DATE 月の月末
SELECT LAST_DAY(@TARGET_DATE);

出力結果

mysql> SET @TARGET_DATE='2018/11/18';
Query OK, 0 rows affected (0.00 sec)

mysql>
mysql> -- @TARGET_DATE 月の月初
mysql> SELECT DATE_FORMAT(@TARGET_DATE, '%Y-%m-01');
+---------------------------------------+
| DATE_FORMAT(@TARGET_DATE, '%Y-%m-01') |
+---------------------------------------+
| 2018-11-01                            |
+---------------------------------------+
1 row in set (0.00 sec)

mysql>
mysql> -- @TARGET_DATE 月の月末
mysql> SELECT LAST_DAY(@TARGET_DATE);
+------------------------+
| LAST_DAY(@TARGET_DATE) |
+------------------------+
| 2018-11-30             |
+------------------------+
1 row in set (0.00 sec)

また、1ヶ月後、2ヶ月後を計算するときは、

SET @TARGET_DATE='2018/11/18';

SELECT DATE_FORMAT(ADDDATE(@TARGET_DATE, INTERVAL 1 MONTH), '%Y-%m-01');

出力結果

mysql> SELECT DATE_FORMAT(ADDDATE(@TARGET_DATE, INTERVAL 1 MONTH), '%Y-%m-01')
    -> ;
+------------------------------------------------------------------+
| DATE_FORMAT(ADDDATE(@TARGET_DATE, INTERVAL 1 MONTH), '%Y-%m-01') |
+------------------------------------------------------------------+
| 2018-12-01                                                       |
+------------------------------------------------------------------+
1 row in set (0.00 sec)

参考

委譲の隠蔽 (リファクタリング-p157)

2018-11-18

目的

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

今回は「委譲の隠蔽」について書きます。

「委譲の隠蔽」 について

クライアントがあるオブジェクトの委譲クラスをコールしている。

カプセル化は、唯一とは言わないまでも、オブジェクト指向技術の鍵です。

変更前

<?php

class Person
{
    private $_department;

    private $_name;

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

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

    public function getDepartment()
    {
        return $this->_department;
    }

    public function setDepartment(Department $arg)
    {
        $this->_department = $arg;
    }
}

class Department
{
    private $_chargeCode;

    /* @var Person */
    private $_manager;

    public function __construct(Person $manager)
    {
        $this->_manager = $manager;
    }

    public function getManager()
    {
        return $this->_manager;
    }
}

$john = new Person('john');

$manager_aaa = new Person('manager_aaa');
$department = new Department($manager_aaa);

$john->setDepartment($department);
$manager = $john->getDepartment()->getManager();

echo $manager->getName() . "\n";

変更後

<?php

class Person2
{
    private $_department;

    private $_name;

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

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

    public function setDepartment(Department2 $arg)
    {
        $this->_department = $arg;
    }

    public function getManager()
    {
        return $this->_department->getManager();
    }
}

class Department2
{
    private $_chargeCode;

    /* @var Person */
    private $_manager;

    public function __construct(Person2 $manager)
    {
        $this->_manager = $manager;
    }

    public function getManager()
    {
        return $this->_manager;
    }
}

$john = new Person2('john');

$manager_aaa = new Person2('manager_aaa');
$department = new Department2($manager_aaa);

$john->setDepartment($department);
$manager = $john->getManager();

echo $manager->getName() . "\n";

クラスの抽出 (リファクタリング-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";