冗长的方法

在某些情况下,PHP 方法可能会过于复杂;例如,在下面的类中,我故意省略了一些有意义的注释,并使构造函数过长:

<?php

class TaxiMeter
{

    const MIN_RATE = 2.50;
    const secondsInDay = 60 * 60 * 24;
    const MILE_RATE = 0.2;
    private $timeOfDay;
    private $baseRate;
    private $miles;
    private $dob;

    /**
    * TaxiMeter constructor.
    * @param int $timeOfDay
    * @param float $baseRate
    * @param string $driverDateOfBirth
    * @throws Exception
    */
    public function __construct(int $timeOfDay, float $baseRate, string $driverDateOfBirth)
    {
        if ($timeOfDay > self::SECONDS_IN_DAY) {
            throw new Exception('There can only be ' . self::SECONDS_IN_DAY . 'seconds in a day.');
        } else if ($timeOfDay < 0) {
            throw new Exception('Value cannot be negative.');
        } else {
            $this->timeOfDay = $timeOfDay;
        }

        if ($baseRate < self::MIN_RATE) {
            throw new Exception('Base rate below minimum.');
        } else {
            $this->baseRate = $baseRate;
        }

        $dateArr = explode('/', $driverDateOfBirth);
        if (count($dateArr) == 3) {
            if ((checkdate($dateArr[0], $dateArr[1], $dateArr[2])) !== true) {
                throw new Exception('Invalid date, please use mm/dd/yyyy.');
            }
        } else {
            throw new Exception('Invalid date formatting, please use simple mm/dd/yyyy.');
        }

        $this->dob = $driverDateOfBirth;
        $this->miles = 0;
    }

    /**
    * @param int $miles
    * @return bool
    */
    public function addMilage(int $miles): bool
    {
        $this->miles += $miles;
        return true;
    }

    /**
    * @return float
    * @throws Exception
    */
    public function getRate(): float
    {
        $dynamicRate = $this->miles * self::MILE_RATE;

        $totalRate = $dynamicRate + $this->baseRate;

        if (is_numeric($totalRate)) {
            return $totalRate;
        } else {
            throw new Exception('Invalid rate output.');
        }
    }
}

现在,让我们只做两个小改动:将我们的一些方法提取到它们自己的函数中,并添加一些 DocBlock 注释。虽然这还不够完美,但请注意其中的差别:

<?php
class TaxiMeter
{
    const MIN_RATE = 2.50;
    const SECONDS_IN_DAY = 60 * 60 * 24;
    const MILE_RATE = 0.2;
    private $timeOfDay;
    private $baseRate;
    private $miles;

    /**
    * TaxiMeter constructor.
    * @param int $timeOfDay
    * @param float $baseRate
    * @param string $driverDateOfBirth
    * @throws Exception
    */
    public function __construct(int $timeOfDay, float $baseRate, string $driverDateOfBirth)
    {
        $this->setTimeOfDay($timeOfDay);
        $this->setBaseRate($baseRate);
        $this->validateDriverDateOfBirth($driverDateOfBirth);
        $this->miles = 0;
    }

    /**
    * Set timeOfDay class variable.
    * Only providing it doesn't exceed the maximum seconds in a day (const secondsInDay) and is greater than 0.
    * @param $timeOfDay
    * @return bool
    * @throws Exception
    */
    private function setTimeOfDay($timeOfDay): bool
    {
        if ($timeOfDay > self::SECONDS_IN_DAY) {
            throw new Exception('There can only be ' . self::SECONDS_IN_DAY . 'seconds in a day.');
        } else if ($timeOfDay < 0) {
            throw new Exception('Value cannot be negative.');
        } else {
            $this->timeOfDay = $timeOfDay;
            return true;
        }
    }

    /**
    * Sets the base rate variable providing it's over the MIN_RATE class constant.
    * @param $baseRate
    * @return bool
    * @throws Exception
    */
    private function setBaseRate($baseRate): bool
    {
        if ($baseRate < self::MIN_RATE) {
            throw new Exception('Base rate below minimum.');
        } else {
            $this->baseRate = $baseRate;
            return true;
        }
    }

    /**
    * Validates
    * @param $driverDateOfBirth
    * @return bool
    * @throws Exception
    */
    private function validateDriverDateOfBirth($driverDateOfBirth): bool
    {
        $dateArr = explode('/', $driverDateOfBirth);
        if (count($dateArr) == 3) {
            if ((checkdate($dateArr[0], $dateArr[1], $dateArr[2])) !== true) {
                throw new Exception('Invalid date, please use mm/dd/yyyy.');
            }
        } else {
            throw new Exception('Invalid date formatting, please use simple mm/dd/yyyy.');
        }

        return true;
    }

    /**
    * Adds given milage to the milage class variable.
    * @param int $miles
    * @return bool
    */
    public function addMilage(int $miles): bool
    {
        $this->miles += $miles;
        return true;
    }

    /**
    * Calculates rate of trip.
    * Times class constant mileRate against the current milage in miles class variables and adds the base rate.
    * @return float
    * @throws Exception
    */
    public function getRate(): float
    {
        $dynamicRate = $this->miles * self::MILE_RATE;

        $totalRate = $dynamicRate + $this->baseRate;

        if (is_numeric($totalRate)) {
            return $totalRate;
        } else {
            throw new Exception('Invalid rate output.');
        }
    }
}

冗长的方法是代码气味的一个指标;它们指的是代码中的一个症状,其根源可能是更深层次的问题。其他例子还包括重复代码和矫揉造作的复杂性(使用高级设计模式,而简单的方法就足够了)。