Monkey

个人站

The master has failed more times than the beginner has tried


重构 第一个示例

  • 重构之前的代码
<?php
/**
 * Created by PhpStorm.
 * User: Monkey
 * Date: 2019-08-27
 * Time: 22:57
 */

$playsStr = '{
    "hamlet": {"name": "Hamlet", "type": "tragedy"},
    "as-like": {"name": "As You Like It", "type": "comedy"},
    "othello": {"name": "Othello", "type": "tragedy"}
}';

$invoicesStr =
    '[
        {
            "customer": "BigCo",
            "performances": [
                {
                    "playID": "hamlet",
                    "audience": 55
                },
                {
                    "playID": "as-like",
                    "audience": 35
                },
                {
                    "playID": "othello",
                    "audience": 40
                }
            ]
        }
    ]';


$plays = json_decode($playsStr, true);

$invoices = json_decode($invoicesStr, true);

/** 
 * @param $invoices
 * @param $plays
 * @return string
 */
function statement($invoices, $plays)
{
    $totalAmount   = 0;
    $volumeCredits = 0;
    $result = "Statement for {$invoices[0]['customer']} " . PHP_EOL;

    $perf = $invoices[0]['performances'];

    foreach ($perf as $key => $value) {
        
        $thisAmount = 0;
        switch ($plays[$value['playID']]['type']) {
            case 'tragedy' :
                $thisAmount = 40000;

                if ($value['audience'] > 30) {
                    $thisAmount += 1000 * ($value['audience'] - 30);
                }
                break;

            case 'comedy' :
                $thisAmount = 30000;
                if ($value['audience'] > 20) {
                    $thisAmount += 10000 + 500 * ($value['audience'] - 20);
                }
                $thisAmount += 300 * $value['audience'];
                break;

            default:

                echo("unknown type: {$plays[$value['playID']]['type']}");
        }
        // add volume credits
        $volumeCredits += max($value['audience'] - 30, 0);
        // add extra credit for every ten comedy attendees”

        if ("comedy" === $plays[$value['playID']]['type']) {
            $volumeCredits += floor($value['audience'] / 5);
        }

        // print line for this order
        $result .= " {" . $plays[$value['playID']]['name'] . "}:\$" . number_format($thisAmount / 100) . " ({$value['audience']} seats)" . PHP_EOL;

        $totalAmount += $thisAmount;
    }
    $result .= "Amount owed is \$" . number_format($totalAmount / 100) . PHP_EOL;
    $result .= "You earned {$volumeCredits} credits" . PHP_EOL;

    return $result;
}

echo statement($invoices, $plays);


  • 重构后
<?php

class Show
{
    /**
     * @var
     */
    public $plays;
    /**
     * @var
     */
    public $invoices;

    /**
     * Show constructor.
     * @param $invoices
     * @param $plays
     */
    public function __construct($invoices, $plays)
    {
        $this->invoices = $invoices;
        $this->plays = $plays;
    }

    /**
     * @return string
     */
    public function statement()
    {
        $result = "Statement for {$this->invoices[0]['customer']} " . "<br />";
        $perf = $this->invoices[0]['performances'];

        foreach ($perf as $key => $value) {
            $result .=  $this->playFor($value)['name'] . " :\$" . $this->usd($this->amountFor($value)) . " { ({$value['audience']} seats)  } ," . "<br />";
        }

        $result .= "Amount owed is \$" . $this->usd($this->totalAmount()) . "<br />";
        $result .= "You earned {$this->totalVolumeCredits()} credits" . "<br />";

        return $result;
    }

    /**
     * @return float|int|mixed
     */
    public function totalVolumeCredits()
    {
        $result = 0;
        foreach ($this->invoices[0]['performances'] as $item => $val) {
            $result += $this->volumeCreditsFor($val);
        }

        return $result;
    }

    /**
     * @param $number
     * @return string
     */
    public function usd($number)
    {
        return number_format($number / 100);
    }

    /**
     * @param $value
     * @return float|int|mixed
     */
    public function volumeCreditsFor($value)
    {
        $result = 0;

        $result += max($value['audience'] - 30, 0);

        if ("comedy" === $this->playFor($value)['type']) {
            $result += floor($value['audience'] / 5);
        }

        return $result;
    }

    /**
     * @param $value
     * @return float|int
     */
    public function amountFor($value)
    {
        $result = 0;
        switch ($this->playFor($value)['type']) {
            case 'tragedy' :
                $result = 40000;
                if ($value['audience'] > 30) {
                    $result += 1000 * ($value['audience'] - 30);
                }
                break;
            case 'comedy' :
                $result = 30000;
                if ($value['audience'] > 20) {
                    $result += 10000 + 500 * ($value['audience'] - 20);
                }
                $result += 300 * $value['audience'];
                break;
            default:
                echo("unknown type: {$this->playFor($value)['type']}");
        }

        return $result;
    }

    /**
     * @param $value
     * @return mixed
     */
    public function playFor($value)
    {
        return $this->plays[$value['playID']];
    }

    /**
     * @return float|int
     */
    private function totalAmount()
    {
        $result   = 0;
        foreach ($this->invoices[0]['performances'] as $key => $value) {
            $result += $this->amountFor($value);
        }

        return $result;
    }

}

$playsStr = '{
    "hamlet": {"name": "Hamlet", "type": "tragedy"},
    "as-like": {"name": "As You Like It", "type": "comedy"},
    "othello": {"name": "Othello", "type": "tragedy"}
}';

$invoicesStr =
    '[
        {
            "customer": "BigCo",
            "performances": [
                {
                    "playID": "hamlet",
                    "audience": 55
                },
                {
                    "playID": "as-like",
                    "audience": 35
                },
                {
                    "playID": "othello",
                    "audience": 40
                }
            ]
        }
    ]';

$plays = json_decode($playsStr, true);

$invoices = json_decode($invoicesStr, true);

$show = new Show($invoices, $plays);

echo $show->statement();
  • 增加一个新需求:将演出信息打印到 html
<?php
/**
 * Created by PhpStorm.
 * User: Monkey
 * Date: 2019-08-28
 * Time: 20:47
 */

class Show
{
    /**
     * @var
     */
    public $plays;
    /**
     * @var
     */
    public $invoices;

    /**
     * Show constructor.
     * @param $invoices
     * @param $plays
     */
    public function __construct($invoices, $plays)
    {
        $this->invoices = $invoices;
        $this->plays = $plays;
    }

    /**
     *  准备两个业务需要的数据;
     *
     *  这样不同业务只需要关系业务本身,不用关注数据问题;
     *
     * @return array
     */
    public function createStatementData()
    {
        $result = [];
        $result['customer'] = $this->invoices[0]['customer'];
        $result['performances'] = $this->getPerformances($this->invoices[0]['performances']);
        $result['totalAmount'] = $this->totalAmount($result);
        $result['totalVolumeCredits'] = $this->totalVolumeCredits($result);

        return $result;
    }

    /**
     * 准备 performances 数据
     *
     * @param $performanceses
     * @return array
     */
    public function getPerformances($performanceses)
    {
        return array_map(function ($result) {
            $result['play'] = $this->playFor($result);
            $result['amount'] = $this->amountFor($result);
            $result['volumeCredits'] = $this->volumeCreditsFor($result);

            return $result;
        }, $performanceses);
    }


    /**
     * 输出声明
     *
     * @return string
     */
    public function statement()
    {
        return $this->renderPlainText($this->createStatementData());
    }

    /**
     * 打印 html 声明
     * 
     * @return string
     */
    public function renderStatement()
    {
        return $this->renderHtml($this->createStatementData());
    }

    /**
     *  打印
     *
     * @param $data
     * @return string
     */
    public function renderHtml($data)
    {
        $result = "<h1>Statement for \${" . $data['customer'] . "}</h1>\n";
        $result .= "<table>\n";
        $result .= "<tr><th>play</th><th>seats</th><th>cost</th></tr>";

        foreach ($data['performances'] as $value) {
                $result .= " <tr><td>\${" . $value['play']['name'] . "}</td><td>\${" . $value['audience'] . "}</td>";
                $result .= "<td>\${" . $this->usd($this->amountFor($value)) . "}</td></tr>\n";
        }

        $result .= "</table>\n";
        $result .= "<p>Amount owed is <em>\${" . $this->usd($data['totalAmount']) . "}</em></p>\n";
        $result .= "<p>You earned <em>\${" . $data['totalVolumeCredits'] . "}</em> credits</p>\n";

        return $result;

    }

    /**
     *  输出
     * 
     * @param $statementData
     * @return string
     */
    public function renderPlainText($statementData)
    {
        $result = "Statement for {$this->invoices[0]['customer']} <br />" ;
        foreach ($statementData['performances'] as $key => $value) {
            $result .=  $value['play']['name'] . " :\$" . $this->usd($this->amountFor($value)) . " { ({$value['audience']} seats)  } ," . "<br />";
        }

        $result .= "Amount owed is \$" . $this->usd($this->totalAmount($statementData)) . "<br />";
        $result .= "You earned {$this->totalVolumeCredits($statementData)} credits" . "<br />";

        return $result;
    }

    /**
     *  总积分
     * 
     * @param $statementData
     * @return float|int|mixed
     */
    public function totalVolumeCredits($statementData)
    {
        $result = 0;
        foreach ($statementData['performances'] as $item => $val) {
            $result += $this->volumeCreditsFor($val);
        }

        return $result;
    }

    /**
     *  格式化金额
     *
     * @param $number
     * @return string
     */
    public function usd($number)
    {
        return number_format($number / 100);
    }

    /**
     *  积分
     *
     * @param $value
     * @return float|int|mixed
     */
    public function volumeCreditsFor($value)
    {
        $result = 0;

        $result += max($value['audience'] - 30, 0);

        if ("comedy" === $this->playFor($value)['type']) {
            $result += floor($value['audience'] / 5);
        }

        return $result;
    }

    /**
     *  金额
     * 
     * @param $value
     * @return float|int
     */
    public function amountFor($value)
    {
        $result = 0;
        switch ($this->playFor($value)['type']) {
            case 'tragedy' :
                $result = 40000;
                if ($value['audience'] > 30) {
                    $result += 1000 * ($value['audience'] - 30);
                }
                break;
            case 'comedy' :
                $result = 30000;
                if ($value['audience'] > 20) {
                    $result += 10000 + 500 * ($value['audience'] - 20);
                }
                $result += 300 * $value['audience'];
                break;
            default:
                echo("unknown type: {$this->playFor($value)['type']}");
        }

        return $result;
    }

    /**
     *  放映
     * 
     * @param $value
     * @return mixed
     */
    public function playFor($value)
    {
        return $this->plays[$value['playID']];
    }

    /**
     *  总金额
     * 
     * @param $statementData
     * @return float|int
     */
    private function totalAmount($statementData)
    {
        $result   = 0;
        foreach ($statementData['performances'] as $key => $value) {
            $result += $this->amountFor($value);
        }

        return $result;
    }

}

$playsStr = '{
    "hamlet": {"name": "Hamlet", "type": "tragedy"},
    "as-like": {"name": "As You Like It", "type": "comedy"},
    "othello": {"name": "Othello", "type": "tragedy"}
}';

$invoicesStr =
    '[
        {
            "customer": "BigCo",
            "performances": [
                {
                    "playID": "hamlet",
                    "audience": 55
                },
                {
                    "playID": "as-like",
                    "audience": 35
                },
                {
                    "playID": "othello",
                    "audience": 40
                }
            ]
        }
    ]';

$plays = json_decode($playsStr, true);

$invoices = json_decode($invoicesStr, true);

$show = new Show($invoices, $plays);

echo $show->statement();

echo $show->renderStatement();
  • 使用方法:
    • 使用拆分循环:分离出累加过程
    • 使用移动语句:将累加的声明与累加过程集中在一起
    • 使用提炼函数:提炼出计算总数的函数
    • 使用内联变量:完全移除中间的变量
  • 重构注意事项:
    • 重构需要小步进行,每次小步进行后进行测试,知道测试通过后再进行下一小步重构;

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦