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

函數(shù)

函數(shù)

1. 函數(shù)參數(shù)(最好少于2個(gè))

2. 函數(shù)應(yīng)該只做一件事

3. 函數(shù)名應(yīng)體現(xiàn)他做了什么事

4. 函數(shù)里應(yīng)當(dāng)只有一層抽象abstraction

5. 不要用flag作為函數(shù)的參數(shù)

6. 避免副作用

7. 不要寫全局函數(shù)

8. 不要使用單例模式

9. 封裝條件語(yǔ)句

10. 避免用反義條件判斷

11. 避免條件判斷

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

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

14. 移除僵尸代碼

1. 函數(shù)參數(shù)(最好少于2個(gè))

限制函數(shù)參數(shù)個(gè)數(shù)極其重要,這樣測(cè)試你的函數(shù)容易點(diǎn)。有超過(guò)3個(gè)可選參數(shù)參數(shù)導(dǎo)致一個(gè)爆炸式組合增長(zhǎng),你會(huì)有成噸獨(dú)立參數(shù)情形要測(cè)試。

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

壞:

 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. 函數(shù)應(yīng)該只做一件事

這是迄今為止軟件工程里最重要的一個(gè)規(guī)則。當(dāng)一個(gè)函數(shù)做超過(guò)一件事的時(shí)候,他們就難于實(shí)現(xiàn)、測(cè)試和理解。當(dāng)你把一個(gè)函數(shù)拆分到只剩一個(gè)功能時(shí),他們就容易被重構(gòu),然后你的代碼讀起來(lái)就更清晰。如果你光遵循這條規(guī)則,你就領(lǐng)先于大多數(shù)開(kāi)發(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. 函數(shù)名應(yīng)體現(xiàn)他做了什么事

壞:

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

好:

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

4. 函數(shù)里應(yīng)當(dāng)只有一層抽象abstraction

當(dāng)你抽象層次過(guò)多時(shí)時(shí),函數(shù)處理的事情太多了。需要拆分功能來(lái)提高可重用性和易用性,以便簡(jiǎn)化測(cè)試。 (譯者注:這里從示例代碼看應(yīng)該是指嵌套過(guò)多)

壞:

 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)中提取出來(lái),但是parseBetterJSAlternative()方法還是很復(fù)雜,而且不利于測(cè)試。

 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) {
            // 解析邏輯...
        }
    }
}

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

5. 不要用flag作為函數(shù)的參數(shù)

flag就是在告訴大家,這個(gè)方法里處理很多事。前面剛說(shuō)過(guò),一個(gè)函數(shù)應(yīng)當(dāng)只做一件事。 把不同flag的代碼拆分到多個(gè)函數(shù)里。

壞:

 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);
}

避免副作用

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

 

現(xiàn)在,你的確需要在一個(gè)程序或者場(chǎng)合里要有副作用,像之前的例子,你也許需要寫一個(gè)文件。你想要做的是把你做這些的地方集中起來(lái)。不要用幾個(gè)函數(shù)和類來(lái)寫入一個(gè)特定的文件。用一個(gè)服務(wù)來(lái)做它,一個(gè)只有一個(gè)。

 

重點(diǎn)是避免常見(jiàn)陷阱比如對(duì)象間共享無(wú)結(jié)構(gòu)的數(shù)據(jù),使用可以寫入任何的可變數(shù)據(jù)類型,不集中處理副作用發(fā)生的地方。如果你做了這些你就會(huì)比大多數(shù)程序員快樂(lè)。

壞:

 // 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. 不要寫全局函數(shù)

在大多數(shù)語(yǔ)言中污染全局變量是一個(gè)壞的實(shí)踐,因?yàn)槟憧赡芎推渌悗?kù)沖突 并且調(diào)用你api的人直到他們捕獲異常才知道踩坑了。讓我們思考一種場(chǎng)景: 如果你想配置一個(gè)數(shù)組,你可能會(huì)寫一個(gè)全局函數(shù)config(),但是他可能 和試著做同樣事的其他類庫(kù)沖突。

壞:

 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 類的實(shí)例
 
$configuration = new Configuration([
    'foo' => 'bar',
]);

現(xiàn)在你必須在程序中用 Configuration 的實(shí)例了

7. 不要使用單例模式

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

  1. 總是被用成全局實(shí)例。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. 違反了單一響應(yīng)原則They violate the single responsibility principle: by virtue of the fact that they control their own creation and lifecycle.

  3. 導(dǎo)致代碼強(qiáng)耦合They inherently cause code to be tightly coupled. This makes faking them out under test rather difficult in many cases.

  4. 在整個(gè)程序的生命周期中始終攜帶狀態(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. 這里有一篇非常好的討論單例模式的[根本問(wèn)題((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 類的實(shí)例并通過(guò) DSN 配置.

$connection = new DBConnection($dsn);

現(xiàn)在你必須在程序中 使用 DBConnection 的實(shí)例了

8. 封裝條件語(yǔ)句

壞:

 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. 避免條件判斷

這看起來(lái)像一個(gè)不可能任務(wù)。當(dāng)人們第一次聽(tīng)到這句話是都會(huì)這么說(shuō)。 "沒(méi)有if語(yǔ)句我還能做啥?" 答案是你可以使用多態(tài)來(lái)實(shí)現(xiàn)多種場(chǎng)景 的相同任務(wù)。第二個(gè)問(wèn)題很常見(jiàn), “這么做可以,但為什么我要這么做?” 答案是前面我們學(xué)過(guò)的一個(gè)Clean Code原則:一個(gè)函數(shù)應(yīng)當(dāng)只做一件事。 當(dāng)你有很多含有if語(yǔ)句的類和函數(shù)時(shí),你的函數(shù)做了不止一件事。 記住,只做一件事。

壞:

 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是弱類型的,這意味著你的函數(shù)可以接收任何類型的參數(shù)。 有時(shí)候你為這自由所痛苦并且在你的函數(shù)漸漸嘗試類型檢查。 有很多方法去避免這么做。第一種是統(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)

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

壞:

 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. 移除僵尸代碼

僵尸代碼和重復(fù)代碼一樣壞。沒(méi)有理由保留在你的代碼庫(kù)中。如果從來(lái)沒(méi)被調(diào)用過(guò),就刪掉! 因?yàn)檫€在代碼版本庫(kù)里,因此很安全。

壞:

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');