亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

函數

函數

1. 函數參數(最好少于2個)

2. 函數應該只做一件事

3. 函數名應體現他做了什么事

4. 函數里應當只有一層抽象abstraction

5. 不要用flag作為函數的參數

6. 避免副作用

7. 不要寫全局函數

8. 不要使用單例模式

9. 封裝條件語句

10. 避免用反義條件判斷

11. 避免條件判斷

12. 避免類型檢查 (part 1)

13. 避免類型檢查 (part 2)

14. 移除僵尸代碼

1. 函數參數(最好少于2個)

限制函數參數個數極其重要,這樣測試你的函數容易點。有超過3個可選參數參數導致一個爆炸式組合增長,你會有成噸獨立參數情形要測試。

無參數是理想情況。1個或2個都可以,最好避免3個。再多就需要加固了。通常如果你的函數有超過兩個參數,說明他要處理的事太多了。 如果必須要傳入很多數據,建議封裝一個高級別對象作為參數。

壞:

 function createMenu(string $title, string $body, string $buttonText, bool $cancellable): void
{
    // ...
}

好:

 class MenuConfig
{
    public $title;
    public $body;
    public $buttonText;
    public $cancellable = false;
}
 
$config = new MenuConfig();
$config->title = 'Foo';
$config->body = 'Bar';
$config->buttonText = 'Baz';
$config->cancellable = true;
 
function createMenu(MenuConfig $config): void
{
    // ...
}

2. 函數應該只做一件事

這是迄今為止軟件工程里最重要的一個規(guī)則。當一個函數做超過一件事的時候,他們就難于實現、測試和理解。當你把一個函數拆分到只剩一個功能時,他們就容易被重構,然后你的代碼讀起來就更清晰。如果你光遵循這條規(guī)則,你就領先于大多數開發(fā)者了。

壞:

 function emailClients(array $clients): void
{
    foreach ($clients as $client) {
        $clientRecord = $db->find($client);
        if ($clientRecord->isActive()) {
            email($client);
        }
    }
}

好:

 function emailClients(array $clients): void
{
    $activeClients = activeClients($clients);
    array_walk($activeClients, 'email');
}
 
function activeClients(array $clients): array
{
    return array_filter($clients, 'isClientActive');
}
 
function isClientActive(int $client): bool
{
    $clientRecord = $db->find($client);
 
    return $clientRecord->isActive();
}

3. 函數名應體現他做了什么事

壞:

 class Email
{
    //...
 
    public function handle(): void
    {
        mail($this->to, $this->subject, $this->body);
    }
}
 
$message = new Email(...);
// 啥?handle處理一個消息干嘛了?是往一個文件里寫嗎?
$message->handle();

好:

 class Email
{
    //...
 
    public function send(): void
    {
        mail($this->to, $this->subject, $this->body);
    }
}
 
$message = new Email(...);
// 簡單明了
$message->send();

4. 函數里應當只有一層抽象abstraction

當你抽象層次過多時時,函數處理的事情太多了。需要拆分功能來提高可重用性和易用性,以便簡化測試。 (譯者注:這里從示例代碼看應該是指嵌套過多)

壞:

 function parseBetterJSAlternative(string $code): void
{
    $regexes = [
        // ...
    ];
 
    $statements = explode(' ', $code);
    $tokens = [];
    foreach ($regexes as $regex) {
        foreach ($statements as $statement) {
            // ...
        }
    }
 
    $ast = [];
    foreach ($tokens as $token) {
        // lex...
    }
 
    foreach ($ast as $node) {
        // parse...
    }
}

壞:

我們把一些方法從循環(huán)中提取出來,但是parseBetterJSAlternative()方法還是很復雜,而且不利于測試。

 function tokenize(string $code): array
{
    $regexes = [
        // ...
    ];
 
    $statements = explode(' ', $code);
    $tokens = [];
    foreach ($regexes as $regex) {
        foreach ($statements as $statement) {
            $tokens[] = /* ... */;
        }
    }
 
    return $tokens;
}
 
function lexer(array $tokens): array
{
    $ast = [];
    foreach ($tokens as $token) {
        $ast[] = /* ... */;
    }
 
    return $ast;
}
 
function parseBetterJSAlternative(string $code): void
{
    $tokens = tokenize($code);
    $ast = lexer($tokens);
    foreach ($ast as $node) {
        // 解析邏輯...
    }
}

好:

最好的解決方案是把 parseBetterJSAlternative()方法的依賴移除。

 class Tokenizer
{
    public function tokenize(string $code): array
    {
        $regexes = [
            // ...
        ];
 
        $statements = explode(' ', $code);
        $tokens = [];
        foreach ($regexes as $regex) {
            foreach ($statements as $statement) {
                $tokens[] = /* ... */;
            }
        }
 
        return $tokens;
    }
}
 
class Lexer
{
    public function lexify(array $tokens): array
    {
        $ast = [];
        foreach ($tokens as $token) {
            $ast[] = /* ... */;
        }
 
        return $ast;
    }
}
 
class BetterJSAlternative
{
    private $tokenizer;
    private $lexer;
 
    public function __construct(Tokenizer $tokenizer, Lexer $lexer)
    {
        $this->tokenizer = $tokenizer;
        $this->lexer = $lexer;
    }
 
    public function parse(string $code): void
    {
        $tokens = $this->tokenizer->tokenize($code);
        $ast = $this->lexer->lexify($tokens);
        foreach ($ast as $node) {
            // 解析邏輯...
        }
    }
}

這樣我們可以對依賴做mock,并測試BetterJSAlternative::parse()運行是否符合預期。

5. 不要用flag作為函數的參數

flag就是在告訴大家,這個方法里處理很多事。前面剛說過,一個函數應當只做一件事。 把不同flag的代碼拆分到多個函數里。

壞:

 function createFile(string $name, bool $temp = false): void
{
    if ($temp) {
        touch('./temp/'.$name);
    } else {
        touch($name);
    }
}
好:
 
function createFile(string $name): void
{
    touch($name);
}
 
function createTempFile(string $name): void
{
    touch('./temp/'.$name);
}

避免副作用

一個函數做了比獲取一個值然后返回另外一個值或值們會產生副作用如果。副作用可能是寫入一個文件,修改某些全局變量或者偶然的把你全部的錢給了陌生人。

 

現在,你的確需要在一個程序或者場合里要有副作用,像之前的例子,你也許需要寫一個文件。你想要做的是把你做這些的地方集中起來。不要用幾個函數和類來寫入一個特定的文件。用一個服務來做它,一個只有一個。

 

重點是避免常見陷阱比如對象間共享無結構的數據,使用可以寫入任何的可變數據類型,不集中處理副作用發(fā)生的地方。如果你做了這些你就會比大多數程序員快樂。

壞:

 // Global variable referenced by following function.
// If we had another function that used this name, now it'd be an array and it could break it.
$name = 'Ryan McDermott';
 
function splitIntoFirstAndLastName(): void
{
    global $name;
 
    $name = explode(' ', $name);
}
 
splitIntoFirstAndLastName();
 
var_dump($name); // ['Ryan', 'McDermott'];

好:

 function splitIntoFirstAndLastName(string $name): array
{
    return explode(' ', $name);
}
 
$name = 'Ryan McDermott';
$newName = splitIntoFirstAndLastName($name);
 
var_dump($name); // 'Ryan McDermott';
var_dump($newName); // ['Ryan', 'McDermott'];

6. 不要寫全局函數

在大多數語言中污染全局變量是一個壞的實踐,因為你可能和其他類庫沖突 并且調用你api的人直到他們捕獲異常才知道踩坑了。讓我們思考一種場景: 如果你想配置一個數組,你可能會寫一個全局函數config(),但是他可能 和試著做同樣事的其他類庫沖突。

壞:

 function config(): array
{
    return  [
        'foo' => 'bar',
    ]
}

好:

class Configuration
{
    private $configuration = [];
 
    public function __construct(array $configuration)
    {
        $this->configuration = $configuration;
    }
 
    public function get(string $key): ?string
    {
        return isset($this->configuration[$key]) ? $this->configuration[$key] : null;
    }
}
加載配置并創(chuàng)建 Configuration 類的實例
 
$configuration = new Configuration([
    'foo' => 'bar',
]);

現在你必須在程序中用 Configuration 的實例了

7. 不要使用單例模式

單例是一種 反模式. 以下是解釋:Paraphrased from Brian Button:

  1. 總是被用成全局實例。They are generally used as a global instance, why is that so bad? Because you hide the dependencies of your application in your code, instead of exposing them through the interfaces. Making something global to avoid passing it around is a code smell.

  2. 違反了單一響應原則They violate the single responsibility principle: by virtue of the fact that they control their own creation and lifecycle.

  3. 導致代碼強耦合They inherently cause code to be tightly coupled. This makes faking them out under test rather difficult in many cases.

  4. 在整個程序的生命周期中始終攜帶狀態(tài)。They carry state around for the lifetime of the application. Another hit to testing since you can end up with a situation where tests need to be ordered which is a big no for unit tests. Why? Because each unit test should be independent from the other.

  5. 這里有一篇非常好的討論單例模式的[根本問題((http://misko.hevery.com/2008/08/25/root-cause-of-singletons/)的文章,是Misko Hevery 寫的。

壞:

 class DBConnection
{
    private static $instance;
 
    private function __construct(string $dsn)
    {
        // ...
    }
 
    public static function getInstance(): DBConnection
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
 
        return self::$instance;
    }
 
    // ...
}
 $singleton = DBConnection::getInstance();

好:

 class DBConnection
{
    public function __construct(string $dsn)
    {
        // ...
    }
 
     // ...
}

創(chuàng)建 DBConnection 類的實例并通過 DSN 配置.

$connection = new DBConnection($dsn);

現在你必須在程序中 使用 DBConnection 的實例了

8. 封裝條件語句

壞:

 if ($article->state === 'published') {
    // ...
}

好:

 if ($article->isPublished()) {
    // ...
}

9. 避免用反義條件判斷

壞:

 function isDOMNodeNotPresent(\DOMNode $node): bool
{
    // ...
}
 
if (!isDOMNodeNotPresent($node))
{
    // ...
}

好:

 function isDOMNodePresent(\DOMNode $node): bool
{
    // ...
}
 
if (isDOMNodePresent($node)) {
    // ...
}

10. 避免條件判斷

這看起來像一個不可能任務。當人們第一次聽到這句話是都會這么說。 "沒有if語句我還能做啥?" 答案是你可以使用多態(tài)來實現多種場景 的相同任務。第二個問題很常見, “這么做可以,但為什么我要這么做?” 答案是前面我們學過的一個Clean Code原則:一個函數應當只做一件事。 當你有很多含有if語句的類和函數時,你的函數做了不止一件事。 記住,只做一件事。

壞:

 class Airplane
{
    // ...
 
    public function getCruisingAltitude(): int
    {
        switch ($this->type) {
            case '777':
                return $this->getMaxAltitude() - $this->getPassengerCount();
            case 'Air Force One':
                return $this->getMaxAltitude();
            case 'Cessna':
                return $this->getMaxAltitude() - $this->getFuelExpenditure();
        }
    }
}

好:

interface Airplane
{
    // ...
 
    public function getCruisingAltitude(): int;
}
 
class Boeing777 implements Airplane
{
    // ...
 
    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude() - $this->getPassengerCount();
    }
}
 
class AirForceOne implements Airplane
{
    // ...
 
    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude();
    }
}
 
class Cessna implements Airplane
{
    // ...
 
    public function getCruisingAltitude(): int
    {
        return $this->getMaxAltitude() - $this->getFuelExpenditure();
    }
}

11. 避免類型檢查 (part 1)

PHP是弱類型的,這意味著你的函數可以接收任何類型的參數。 有時候你為這自由所痛苦并且在你的函數漸漸嘗試類型檢查。 有很多方法去避免這么做。第一種是統(tǒng)一API。

壞:

 function travelToTexas($vehicle): void
{
    if ($vehicle instanceof Bicycle) {
        $vehicle->pedalTo(new Location('texas'));
    } elseif ($vehicle instanceof Car) {
        $vehicle->driveTo(new Location('texas'));
    }
}

好:

 function travelToTexas(Vehicle $vehicle): void
{
    $vehicle->travelTo(new Location('texas'));
}

12. 避免類型檢查 (part 2)

如果你正使用基本原始值比如字符串、整形和數組,要求版本是PHP 7+,不用多態(tài),需要類型檢測, 那你應當考慮類型聲明或者嚴格模式。 提供了基于標準PHP語法的靜態(tài)類型。 手動檢查類型的問題是做好了需要好多的廢話,好像為了安全就可以不顧損失可讀性。 保持你的PHP 整潔,寫好測試,做好代碼回顧。做不到就用PHP嚴格類型聲明和嚴格模式來確保安全。

壞:

 function combine($val1, $val2): int
{
    if (!is_numeric($val1) || !is_numeric($val2)) {
        throw new \Exception('Must be of type Number');
    }
 
    return $val1 + $val2;
}

好:

 function combine(int $val1, int $val2): int
{
    return $val1 + $val2;
}

13. 移除僵尸代碼

僵尸代碼和重復代碼一樣壞。沒有理由保留在你的代碼庫中。如果從來沒被調用過,就刪掉! 因為還在代碼版本庫里,因此很安全。

壞:

function oldRequestModule(string $url): void
{
    // ...
}
 
function newRequestModule(string $url): void
{
    // ...
}
 
$request = newRequestModule($requestUrl);
inventoryTracker('apples', $request, 'www.inventory-awesome.io');

好:

 function requestModule(string $url): void
{
    // ...
}
 
$request = requestModule($requestUrl);
inventoryTracker('apples', $request, 'www.inventory-awesome.io');