ためすう
コンストラクタ本体の引き上げ (リファクタリング-p325)
2019-03-15目的
「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。
今回は「コンストラクタ本体の引き上げ」について書きます。
「コンストラクタ本体の引き上げ」 について
複数のサブクラスに内容がほとんど同一のコンストラクタがある。
しかしコンストラクタでは、この共通の振る舞いがしばしば生成処理なのです。
例
変更前
<?php
class Employee
{
protected $_name;
protected $_id;
}
class Manager extends Employee
{
private $_grade;
public function __construct($name, $id, $grade)
{
$this->_name = $name;
$this->_id = $id;
$this->_grade = $grade;
}
public function getGrade()
{
return $this->_grade;
}
}
$m = new Manager('name', 456, 20);
echo $m->getGrade() . "\n";
変更後
<?php
class Employee2
{
protected $_name;
protected $_id;
protected function __construct($name, $id)
{
$this->_name = $name;
$this->_id = $id;
}
}
class Manager2 extends Employee2
{
private $_grade;
public function __construct($name, $id, $grade)
{
parent::__construct($name, $id);
$this->_grade = $grade;
}
public function getGrade()
{
return $this->_grade;
}
}
$m = new Manager2('name', 456, 20);
echo $m->getGrade() . "\n";
条件判定による例外の置き換え (リファクタリング-p315)
2019-03-13目的
「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。
今回は「条件判定による例外の置き換え」について書きます。
「条件判定による例外の置き換え」 について
例外を発生させているが、本来は呼び出し側が先にチェックすべきである。
例外は、例外的な振る舞い、すなわち予期しないエラーに対して使用すべきです。
条件判定の代わりとして使うべきではありません。
例
変更前
<?php
class ResourcePool
{
private $_available = [];
private $_allocated = [];
public function getResource()
{
$result = [];
try {
// null が返却されてもエラーにならない
$result = array_pop($this->_available);
array_push($this->_allocated, $result);
return $result;
} catch (Exception $ex) {
array_push($this->_allocated, 'resource');
return $result;
}
}
}
$rp = new ResourcePool();
var_dump($rp->getResource());
変更後 (その1)
<?php
class ResourcePool2
{
private $_available = [];
private $_allocated = [];
public function getResource()
{
$result = [];
if (empty($this->_available)) {
$result = ['resource'];
array_push($this->_allocated, $result);
return $result;
}
try {
$result = array_pop($this->_available);
array_push($this->_allocated, $result);
return $result;
} catch (Exception $ex) {
array_push($this->_allocated, 'resource');
return $result;
}
}
}
$rp2 = new ResourcePool2();
var_dump($rp2->getResource());
変更後 (その2)
<?php
class ResourcePool3
{
private $_available = [];
private $_allocated = [];
public function getResource()
{
$result = [];
if (empty($this->_available)) {
$result = ['resource'];
} else {
$result = array_pop($this->_available);
}
array_push($this->_allocated, $result);
return $result;
}
}
$rp3 = new ResourcePool3();
var_dump($rp3->getResource());
例外によるエラーコードの置き換え (リファクタリング-p310)
2019-03-11目的
「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。
今回は「例外によるエラーコードの置き換え」について書きます。
「例外によるエラーコードの置き換え」 について
メソッドがエラーを示す特別なコードをリターンしている。
代わりに例外を発生させる
例
変更前
<?php
class Account
{
private $_balance;
public function __construct($balance)
{
$this->_balance = $balance;
}
public function withdraw($amount)
{
if ($amount > $this->_balance) {
return -1;
} else {
$this->_balance -= $amount;
return 0;
}
}
}
$a1 = new Account(1000);
echo $a1->withdraw(500) . "\n";
echo $a1->withdraw(1500) . "\n";
変更後 (チェックされない例外)
<?php
// 呼び出し側でチェック
class Account2
{
private $_balance;
public function __construct($balance)
{
$this->_balance = $balance;
}
public function withdraw($amount)
{
if ($amount > $this->_balance) {
throw new Exception('出金額が多すぎる');
}
$this->_balance -= $amount;
}
public function getBalance()
{
return $this->_balance;
}
}
$a2 = new Account2(1000);
$a2->withdraw(500);
echo $a2->getBalance() . "\n";
// 例外発生
// $a2->withdraw(1500);
変更後 (チェックされる例外)
<?php
class BalanceException extends Exception {}
class Account3
{
private $_balance;
public function __construct($balance)
{
$this->_balance = $balance;
}
public function withdraw($amount)
{
if ($amount > $this->_balance) {
throw new BalanceException();
}
$this->_balance -= $amount;
}
public function getBalance()
{
return $this->_balance;
}
}
$a3 = new Account3(1000);
$a3->withdraw(300);
echo $a3->getBalance() . "\n";
try {
$a3->withdraw(800);
// doTheUsualThin();
} catch (BalanceException $ex) {
echo '例外発生: BalanceException' . "\n";
// handleOverdrawn();
}
目的
「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。
今回は「FactoryMethod によるコンストラクタの置き換え」について書きます。
「FactoryMethod によるコンストラクタの置き換え」 について
オブジェクトを生成する際に、単純な生成以上のことをしたい。
例
変更前
<?php
class Employee
{
const ENGINEER = 0;
const SALESMAN = 1;
const MANAGER = 2;
private $_type;
public function __construct($type)
{
$this->_type = $type;
}
public function getType()
{
return $this->_type;
}
}
$e1 = new Employee(1);
echo $e1->getType() . "\n";
変更後
<?php
class Employee2
{
const ENGINEER = 0;
const SALESMAN = 1;
const MANAGER = 2;
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();
}
throw new Exception('不正なタイプコード');
}
}
class Salesman extends Employee2
{
public function __consturct() {}
}
$e2 = Employee2::create(1);
echo get_class($e2) . "\n";
setメソッドの削除 (リファクタリング-p300)
2019-03-09目的
「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。
今回は「setメソッドの削除」について書きます。
「setメソッドの削除」 について
setメソッドを提供するということは、そのフィールドが変更される可能性を示しています。
例
変更前
<?php
class Account
{
private $_id;
public function __construct($id)
{
$this->setId($id);
}
public function setId($arg)
{
$this->_id = 'ZZ' . $arg;
}
public function getId()
{
return $this->_id;
}
}
$a = new Account(10);
echo $a->getId() . "\n";
変更後
<?php
class Account2
{
private $_id;
public function __construct($id)
{
$this->initializeId($id);
}
private function initializeId($arg)
{
$this->_id = 'ZZ' . $arg;
}
public function getId()
{
return $this->_id;
}
}
$a = new Account(10);
echo $a->getId() . "\n";
引数オブジェクトの導入 (リファクタリング-p295)
2019-03-08目的
「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。
今回は「引数オブジェクトの導入」について書きます。
「引数オブジェクトの導入」 について
本来まとめて扱うべき一連の引数がある。
このような引数群をオブジェクトにすることは、データをグループ化してまとめる点で価値があります。またこのリファクタリングは、引数リストを短くできる点でも有益です。
例
変更前
<?php
date_default_timezone_set('Asia/Tokyo');
class Entry
{
private $_value;
private $_chargeDate;
public function __construct($value, DateTime $chargeDate)
{
$this->_value = $value;
$this->_chargeDate = $chargeDate;
}
public function getDate()
{
return $this->_chargeDate;
}
public function getValue()
{
return $this->_value;
}
}
class Account
{
private $_entiries = [];
public function __construct()
{
$e1 = new Entry(10, new DateTime('2018/10/14'));
$e2 = new Entry(20, new DateTime('2018/10/18'));
$e3 = new Entry(30, new DateTime('2018/10/20'));
$e4 = new Entry(40, new DateTime('2018/10/23'));
$e5 = new Entry(50, new DateTime('2018/11/14'));
$this->_entiries = [
$e1,
$e2,
$e3,
$e4,
$e5,
];
}
public function getFlowBetween(DateTime $start, DateTime $end)
{
$result = 0;
foreach ($this->_entiries as $entry) {
if ($entry->getDate()->getTimeStamp() >= $start->getTimeStamp() &&
$entry->getDate()->getTimeStamp() <= $end->getTimeStamp()) {
$result += $entry->getValue();
}
}
return $result;
}
}
$anAccount = new Account();
$startDate = new DateTime('2018/10/14');
$endDate = new DateTime('2018/10/20');
echo $anAccount->getFlowBetween($startDate, $endDate) . "\n";
変更後
<?php
date_default_timezone_set('Asia/Tokyo');
class Entry2
{
private $_value;
private $_chargeDate;
public function __construct($value, DateTime $chargeDate)
{
$this->_value = $value;
$this->_chargeDate = $chargeDate;
}
public function getDate()
{
return $this->_chargeDate;
}
public function getValue()
{
return $this->_value;
}
}
class Account2
{
private $_entiries = [];
public function __construct()
{
$e1 = new Entry2(10, new DateTime('2018/10/14'));
$e2 = new Entry2(20, new DateTime('2018/10/18'));
$e3 = new Entry2(30, new DateTime('2018/10/20'));
$e4 = new Entry2(40, new DateTime('2018/10/23'));
$e5 = new Entry2(50, new DateTime('2018/11/14'));
$this->_entiries = [
$e1,
$e2,
$e3,
$e4,
$e5,
];
}
public function getFlowBetween(DateRange $range)
{
$result = 0;
foreach ($this->_entiries as $entry) {
if ($range->includes($entry->getDate())) {
$result += $entry->getValue();
}
}
return $result;
}
}
class DateRange
{
private $_start;
private $_end;
public function __construct(DateTime $start, DateTime $end)
{
$this->_start = $start;
$this->_end = $end;
}
public function getStart()
{
return $this->_start;
}
public function getEnd()
{
return $this->_end;
}
public function includes(DateTime $arg)
{
return ($arg->getTimeStamp() >= $this->_start->getTimeStamp()) &&
($arg->getTimeStamp() <= $this->_end->getTimeStamp())
;
}
}
$anAccount = new Account2();
$startDate = new DateTime('2018/10/14');
$endDate = new DateTime('2018/10/20');
$range = new DateRange($startDate, $endDate);
echo $anAccount->getFlowBetween($range) . "\n";
メソッドによる引数の置き換え (リファクタリング-p292)
2019-03-07目的
「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。
今回は「メソッドによる引数の置き換え」について書きます。
「メソッドによる引数の置き換え」 について
あるオブジェクトがメソッドを呼び出し、その戻り値を別のメソッドの引数として渡している。受信側は、そのメソッドを呼び出し可能である。
もしメソッドが、引数で渡される値を別の方法で取得できるならば、そのように修正すべきです。
例
変更前
<?php
class Sample
{
private $_quantity;
private $_itemPrice;
public function __construct($quantity, $itemPrice)
{
$this->_quantity = $quantity;
$this->_itemPrice = $itemPrice;
}
public function getPrice()
{
$basePrice = $this->_quantity * $this->_itemPrice;
$discountLevel = 1;
if ($this->_quantity > 100) {
$discountLevel = 2;
}
return $this->discountedPrice($basePrice, $discountLevel);
}
private function discountedPrice($basePrice, $discountLevel)
{
if ($discountLevel === 2) {
return $basePrice * 0.1;
}
return $basePrice * 0.05;
}
}
$s = new Sample(10, 2000);
echo $s->getPrice() . "\n";
変更後
<?php
class Sample2
{
private $_quantity;
private $_itemPrice;
public function __construct($quantity, $itemPrice)
{
$this->_quantity = $quantity;
$this->_itemPrice = $itemPrice;
}
public function getPrice()
{
return $this->discountedPrice();
}
private function getBasePrice()
{
return $this->_quantity * $this->_itemPrice;
}
private function getDiscountLevel()
{
$discountLevel = 1;
if ($this->_quantity > 100) {
$discountLevel = 2;
}
return $discountLevel;
}
private function discountedPrice()
{
$basePrice = $this->getBasePrice();
if ($this->getDiscountLevel() === 2) {
return $basePrice * 0.1;
}
return $basePrice * 0.05;
}
}
$s = new Sample2(10, 2000);
echo $s->getPrice() . "\n";
オブジェクトそのものの受け渡し (リファクタリング-p288)
2019-03-05目的
「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。
今回は「オブジェクトそのものの受け渡し」について書きます。
「オブジェクトそのものの受け渡し 」 について
あるオブジェクトから複数の値を取得し、それらの値をメソッド呼び出しの引数として渡している。
「オブジェクトそのものの受け渡し」は、引数リストを変更に対して頑強にするだけでなく、しばしばコードを読みやすくします。
例
変更前
<?php
class TempRange
{
private $_low;
private $_high;
public function __construct($low, $high)
{
$this->_low = $low;
$this->_high = $high;
}
public function getLow()
{
return $this->_low;
}
public function getHigh()
{
return $this->_high;
}
}
class Room
{
public function withinPlan(HeatingPlan $plan)
{
$low = $this->daysTempRange()->getLow();
$high = $this->daysTempRange()->getHigh();
return $plan->withinRange($low, $high);
}
// 現在までの気温範囲
private function daysTempRange()
{
return new TempRange(10, 50);
}
}
class HeatingPlan
{
/* @var TempRange */
private $_range;
public function __construct($range)
{
$this->_range = $range;
}
public function withinRange($low, $high)
{
return (
$low >= $this->_range->getLow() &&
$high <= $this->_range->getHigh()
);
}
}
// 暖房計画の気温範囲
$temp_range = new TempRange(10, 70);
$hp = new HeatingPlan($temp_range);
$r = new Room();
echo (int)$r->withinPlan($hp) . "\n";
変更後
<?php
class TempRange2
{
private $_low;
private $_high;
public function __construct($low, $high)
{
$this->_low = $low;
$this->_high = $high;
}
public function getLow()
{
return $this->_low;
}
public function getHigh()
{
return $this->_high;
}
public function includes(TempRange2 $arg)
{
return (
$arg->getLow() >= $this->getLow() &&
$arg->getHigh() <= $this->getHigh()
);
}
}
class Room2
{
public function withinPlan(HeatingPlan2 $plan)
{
return $plan->withinRange($this->daysTempRange());
}
// 現在までの気温範囲
private function daysTempRange()
{
return new TempRange2(10, 50);
}
}
class HeatingPlan2
{
/* @var TempRange2 */
private $_range;
public function __construct($range)
{
$this->_range = $range;
}
public function withinRange(TempRange2 $room_range)
{
return $this->_range->includes($room_range);
}
}
// 暖房計画の気温範囲
$temp_range = new TempRange2(10, 70);
$hp2 = new HeatingPlan2($temp_range);
$r2 = new Room2();
echo (int)$r2->withinPlan($hp2) . "\n";
明示的なメソッド群による引数の置き換え (リファクタリング-p285)
2019-03-04目的
「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。
今回は「明示的なメソッド群による引数の置き換え」について書きます。
「明示的なメソッド群による引数の置き換え」 について
引数の特定の値によって異なるコードが実行されるメソッドがある。
引数の値に対応する別々のメソッドを作成する
例
変更前
<?php
class Engineer extends Employee
{
public function __consturct() {}
}
class Employee
{
const ENGINEER = 0;
const SALESMAN = 1;
const MANAGER = 2;
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();
}
throw new Exception('不正なタイプコード');
}
}
$e = Employee::create(0);
echo get_class($e) . "\n";
変更後
<?php
class Employee2
{
const ENGINEER = 0;
const SALESMAN = 1;
const MANAGER = 2;
public static function createEngineer()
{
return new Engineer();
}
}
$e = Employee2::createEngineer();
echo get_class($e) . "\n";
メソッドのパラメータ化 (リファクタリング-p283)
2019-03-03目的
「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。
今回は「メソッドのパラメータ化」について書きます。
「メソッドのパラメータ化」 について
複数のメソッドが、異なる値に対してよく似た振る舞いをしている。
例
変更前
<?php
class Employee
{
private $salary;
public function __construct($salary)
{
$this->salary = $salary;
}
public function tenPercentRaise()
{
$this->salary *= 1.1;
}
public function fivePercentRaise()
{
$this->salary *= 1.05;
}
public function getSalary()
{
return $this->salary;
}
}
$e1 = new Employee(1000);
$e1->tenPercentRaise();
echo $e1->getSalary() . "\n";
変更後
<?php
class Employee2
{
private $salary;
public function __construct($salary)
{
$this->salary = $salary;
}
public function raise($factor)
{
$this->salary *= (1 + $factor);
}
public function getSalary()
{
return $this->salary;
}
}
$e2 = new Employee2(1000);
$e2->raise(0.1);
echo $e2->getSalary() . "\n";