资讯专栏INFORMATION COLUMN

PHP代码简洁之道——函数部分

crossoverJie / 3114人阅读

摘要:超过三个参数会导致参数之间的组合过多,你必须对每个多带带的参数测试大量不同的情况。拆分这些函数,可以让代码可重用性更高且更易测试。

函数参数不要超过两个

限制函数的参数数量是非常重要的,因为它使你的函数更容易测试。超过三个参数会导致参数之间的组合过多,你必须对每个多带带的参数测试大量不同的情况。

没有参数是最理想的情况,一个或两个参数是可以接受的,三个以上则是应该避免的。这很重要的。如果你有两个以上的参数,那么你的函数可能试图做的太多,如果不是,你可能需要将一个高级别的对象传当做参数传进去。

Bad:

function createMenu($title, $body, $buttonText, $cancellable)
{
    // ...
}

Good:

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)
{
    // ...
}
一个函数只做一件事

这是软件工程中一个重要的原则。这会让你的代码清晰易懂以及易于复用。

Bad:

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

Good:

function emailClients($clients)
{
    $activeClients = activeClients($clients);
    array_walk($activeClients, "email");
}

function activeClients($clients)
{
    return array_filter($clients, "isClientActive");
}

function isClientActive($client)
{
    $clientRecord = $db->find($client);

    return $clientRecord->isActive();
}
函数名要能说明它是做什么的

Bad:

class Email
{
    //...

    public function handle()
    {
        mail($this->to, $this->subject, $this->body);
    }
}

$message = new Email(...);
// 这是什么?一条消息的句柄?还是要写一个文件?(读者的疑问)
$message->handle();

Good:

class Email 
{
    //...

    public function send()
    {
        mail($this->to, $this->subject, $this->body);
    }
}

$message = new Email(...);
//  一目了然
$message->send();
函数应该只做一层抽象

当你有多个层次的抽象时,你的函数就已经做的太多了。拆分这些函数,可以让代码可重用性更高且更易测试。
Bad:

function parseBetterJSAlternative($code)
{
    $regexes = [
        // ...
    ];

    $statements = explode(" ", $code);
    $tokens = [];
    foreach ($regexes as $regex) {
        foreach ($statements as $statement) {
            // ...
        }
    }

    $ast = [];
    foreach ($tokens as $token) {
        // lex...
    }

    foreach ($ast as $node) {
        // parse...
    }
}

Bad too:
我们从函数中迁出去了一些工作,但是 parseBetterJSAlternative() 函数还是很复杂,不可测试。

function tokenize($code)
{
    $regexes = [
        // ...
    ];

    $statements = explode(" ", $code);
    $tokens = [];
    foreach ($regexes as $regex) {
        foreach ($statements as $statement) {
            $tokens[] = /* ... */;
        }
    }

    return $tokens;
}

function lexer($tokens)
{
    $ast = [];
    foreach ($tokens as $token) {
        $ast[] = /* ... */;
    }

    return $ast;
}

function parseBetterJSAlternative($code)
{
    $tokens = tokenize($code);
    $ast = lexer($tokens);
    foreach ($ast as $node) {
        // parse...
    }
}

Good:

最好的解决方案是移除 parseBetterJSAlternative 函数的依赖

class Tokenizer
{
    public function tokenize($code)
    {
        $regexes = [
            // ...
        ];

        $statements = explode(" ", $code);
        $tokens = [];
        foreach ($regexes as $regex) {
            foreach ($statements as $statement) {
                $tokens[] = /* ... */;
            }
        }

        return $tokens;
    }
}

class Lexer
{
    public function lexify($tokens)
    {
        $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($code)
    {
        $tokens = $this->tokenizer->tokenize($code);
        $ast = $this->lexer->lexify($tokens);
        foreach ($ast as $node) {
            // parse...
        }
    }
}

不要使用标志作为函数的参数

当你在函数中使用标志来作为参数时,你的函数就不是只做一件事情了,这与我们前面所讲的每个函数只做一件事的原则相违背,所以不要使用标志作为函数的参数。

Bad:

function createFile($name, $temp = false)
{
    if ($temp) {
        touch("./temp/".$name);
    } else {
        touch($name);
    }
}

Good:

function createFile($name)
{
    touch($name);
}

function createTempFile($name)
{
    touch("./temp/".$name);
}
避免副作用

如果一个函数做了“拿到一个值并返回一个值或者多个值”以外的事情,那么这个函数就有可能产生副作用,副作用可能是意外的写入了文件、修改了全局变量、或者打钱给了陌生人。

现在假如你确实要在函数中做一些有可能产生副作用的事情。 比如要写一个文件,你需要做的是将写文件的操作集中到一处,而不是在几个函数或者类里对同一个文件做操作,实现一个服务(函数或者类)去操作它,有且仅有一个。

关键是要能避免常见的陷阱:像是在没有结构的对象之间共享状态、使用可能被写入任何值的可变数据类型、 不集中处理有可能产生副作用的操作。 如果你能做到这些,你会比绝大多数程序员更快乐。

Bad:

// 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()
{
    global $name;

    $name = explode(" ", $name);
}

splitIntoFirstAndLastName();

var_dump($name); // ["Ryan", "McDermott"];

Good:

function splitIntoFirstAndLastName($name)
{
    return explode(" ", $name);
}

$name = "Ryan McDermott";
$newName = splitIntoFirstAndLastName($name);

var_dump($name); // "Ryan McDermott";
var_dump($newName); // ["Ryan", "McDermott"];

不要修改全局变量

在许多编程语言中污染全局是一种糟糕的做法,因为你的库可能会与另一个库冲突,但是你的库的用户却一无所知,直到在生产环境中爆发异常。让我们来考虑一个例子:如果你想要拿到配置数组怎么办?你可以编写全局函数,如config(),但是它可能与另一个试图做同样事情的库冲突。

Bad:

function config()
{
    return  [
        "foo" => "bar",
    ]
}

Good:

class Configuration
{
    private $configuration = [];

    public function __construct(array $configuration)
    {
        $this->configuration = $configuration;
    }

    public function get($key)
    {
        return isset($this->configuration[$key]) ? $this->configuration[$key] : null;
    }
}


$configuration = new Configuration([
    "foo" => "bar",
]);
避免条件判断

人们会问“如果不用 if 语句我该怎么做?”,答案是在许多情况下,你可以用多态来实现同样的效果。那这样做什么好处,还是那句话:“一个函数应该只做一件事”, 当你的类或函数中有了 if 语句,你的函数就不止是只做一件事情了。

Bad:

class Airplane
{
    // ...

    public function getCruisingAltitude()
    {
        switch ($this->type) {
            case "777":
                return $this->getMaxAltitude() - $this->getPassengerCount();
            case "Air Force One":
                return $this->getMaxAltitude();
            case "Cessna":
                return $this->getMaxAltitude() - $this->getFuelExpenditure();
        }
    }
}

Good:

interface Airplane
{
    // ...

    public function getCruisingAltitude();
}

class Boeing777 implements Airplane
{
    // ...

    public function getCruisingAltitude()
    {
        return $this->getMaxAltitude() - $this->getPassengerCount();
    }
}

class AirForceOne implements Airplane
{
    // ...

    public function getCruisingAltitude()
    {
        return $this->getMaxAltitude();
    }
}

class Cessna implements Airplane
{
    // ...

    public function getCruisingAltitude()
    {
        return $this->getMaxAltitude() - $this->getFuelExpenditure();
    }
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/25949.html

相关文章

  • PHP代码简洁之道——变量部分

    摘要:将代码写的简洁并且易读易懂是每一位优秀的所应该具备的基本功。前几天在上看到这个项目,感觉很有收获,于是在这里记录一下。 将代码写的简洁并且易读易懂是每一位优秀的coder所应该具备的基本功。 前几天在github上看到clean-code-php这个项目,感觉很有收获,于是在这里记录一下。 使用有意义并且可读的变量名称 Bad: $ymdstr = $moment->format(y-...

    mgckid 评论0 收藏0
  • PHP代码简洁之道——类和对象部分

    摘要:使用和在中,通过为属性或方法设置和关键字可以实现对属性或方法的可见性控制。你的继承表达了一个对等比如人类是动物的关系,不是包含的关系比如用户具有用户详情你能从基类中复用代码你想通过修改全局类来对所有派生类进行修改。 使用getter和setter 在 PHP 中,通过为属性或方法设置 public, protected 和 private 关键字可以实现对属性或方法的可见性控制。不过,...

    cyixlq 评论0 收藏0
  • PHP 代码规范简洁之道

    摘要:统一的编码规范编码规范往简单说其实就是三个方面换行空格变量命名放在里面,还有一些附加的地方,比如关键字大小写,语法糖的使用与等的问题。这些都是规范代码的重要手段。推广给你的队友团队项目中,队友的配合对整个代码的规范起着决定性的作用。 1. 统一的编码规范 编码规范往简单说其实就是三个方面: 换行 空格 变量命名 放在 PHP 里面,还有一些附加的地方,比如关键字大小写,语法糖的使用...

    BearyChat 评论0 收藏0
  • PHP代码简洁之道——SOLID原则

    摘要:是推荐的便于记忆的首字母简写,它代表了命名的最重要的五个面对对象编码设计原则单一职责原则开闭原则里氏替换原则接口隔离原则依赖反转原则单一职责原则修改一个类应该只为一个理由。别写重复代码这条原则大家应该都是比较熟悉了。 SOLID 是Michael Feathers推荐的便于记忆的首字母简写,它代表了Robert Martin命名的最重要的五个面对对象编码设计原则 S: 单一职责原则 ...

    PrototypeZ 评论0 收藏0
  • JavaScript 的简洁之道

    摘要:考虑到函数表示某种行为,函数名称应该是动词或短语,用以说明其背后的意图以及参数的意图。不好的方式好的方式使用条件简写。这可能微不足道,但值得一提。 为了保证可读性,本文采用的音译而非直意。 简介 如果你关注代码本身和代码的编写方式,而不是只关心它是否能工作,那么你写代码是有一定的水准。专业开发人员将为未来的自己和其他人编写代码,而不仅仅只编写当前能工作就行的代码。 在此基础上,简洁代码...

    wudengzan 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<