查看: 1596|回复: 0

[PHP实例] PHP代码简洁之道——函数部分

发表于 2017-10-13 08:00:03
句号论坛
函数参数不要超过两个

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

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

Bad:

  1. function createMenu($title, $body, $buttonText, $cancellable)
  2. {
  3. // ...
  4. }
复制代码

Good:

  1. class MenuConfig
  2. {
  3. public $title;
  4. public $body;
  5. public $buttonText;
  6. public $cancellable = false;
  7. }
  8. $config = new MenuConfig();
  9. $config->title = 'Foo';
  10. $config->body = 'Bar';
  11. $config->buttonText = 'Baz';
  12. $config->cancellable = true;
  13. function createMenu(MenuConfig $config)
  14. {
  15. // ...
  16. }
复制代码
一个函数只做一件事

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

Bad:

  1. function emailClients($clients)
  2. {
  3. foreach ($clients as $client) {
  4. $clientRecord = $db->find($client);
  5. if ($clientRecord->isActive()) {
  6. email($client);
  7. }
  8. }
  9. }
复制代码

Good:

  1. function emailClients($clients)
  2. {
  3. $activeClients = activeClients($clients);
  4. array_walk($activeClients, 'email');
  5. }
  6. function activeClients($clients)
  7. {
  8. return array_filter($clients, 'isClientActive');
  9. }
  10. function isClientActive($client)
  11. {
  12. $clientRecord = $db->find($client);
  13. return $clientRecord->isActive();
  14. }
复制代码
函数名要能说明它是做什么的

Bad:

  1. class Email
  2. {
  3. //...
  4. public function handle()
  5. {
  6. mail($this->to, $this->subject, $this->body);
  7. }
  8. }
  9. $message = new Email(...);
  10. // 这是什么?一条消息的句柄?还是要写一个文件?(读者的疑问)
  11. $message->handle();
复制代码

Good:

  1. class Email
  2. {
  3. //...
  4. public function send()
  5. {
  6. mail($this->to, $this->subject, $this->body);
  7. }
  8. }
  9. $message = new Email(...);
  10. // 一目了然
  11. $message->send();
复制代码
函数应该只做一层抽象

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

  1. function parseBetterJSAlternative($code)
  2. {
  3. $regexes = [
  4. // ...
  5. ];
  6. $statements = explode(' ', $code);
  7. $tokens = [];
  8. foreach ($regexes as $regex) {
  9. foreach ($statements as $statement) {
  10. // ...
  11. }
  12. }
  13. $ast = [];
  14. foreach ($tokens as $token) {
  15. // lex...
  16. }
  17. foreach ($ast as $node) {
  18. // parse...
  19. }
  20. }
复制代码

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

  1. function tokenize($code)
  2. {
  3. $regexes = [
  4. // ...
  5. ];
  6. $statements = explode(' ', $code);
  7. $tokens = [];
  8. foreach ($regexes as $regex) {
  9. foreach ($statements as $statement) {
  10. $tokens[] = /* ... */;
  11. }
  12. }
  13. return $tokens;
  14. }
  15. function lexer($tokens)
  16. {
  17. $ast = [];
  18. foreach ($tokens as $token) {
  19. $ast[] = /* ... */;
  20. }
  21. return $ast;
  22. }
  23. function parseBetterJSAlternative($code)
  24. {
  25. $tokens = tokenize($code);
  26. $ast = lexer($tokens);
  27. foreach ($ast as $node) {
  28. // parse...
  29. }
  30. }
复制代码

Good:

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

  1. class Tokenizer
  2. {
  3. public function tokenize($code)
  4. {
  5. $regexes = [
  6. // ...
  7. ];
  8. $statements = explode(' ', $code);
  9. $tokens = [];
  10. foreach ($regexes as $regex) {
  11. foreach ($statements as $statement) {
  12. $tokens[] = /* ... */;
  13. }
  14. }
  15. return $tokens;
  16. }
  17. }
  18. class Lexer
  19. {
  20. public function lexify($tokens)
  21. {
  22. $ast = [];
  23. foreach ($tokens as $token) {
  24. $ast[] = /* ... */;
  25. }
  26. return $ast;
  27. }
  28. }
  29. class BetterJSAlternative
  30. {
  31. private $tokenizer;
  32. private $lexer;
  33. public function __construct(Tokenizer $tokenizer, Lexer $lexer)
  34. {
  35. $this->tokenizer = $tokenizer;
  36. $this->lexer = $lexer;
  37. }
  38. public function parse($code)
  39. {
  40. $tokens = $this->tokenizer->tokenize($code);
  41. $ast = $this->lexer->lexify($tokens);
  42. foreach ($ast as $node) {
  43. // parse...
  44. }
  45. }
  46. }
复制代码
不要使用标志作为函数的参数

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

Bad:

  1. function createFile($name, $temp = false)
  2. {
  3. if ($temp) {
  4. touch('./temp/'.$name);
  5. } else {
  6. touch($name);
  7. }
  8. }
复制代码

Good:

  1. function createFile($name)
  2. {
  3. touch($name);
  4. }
  5. function createTempFile($name)
  6. {
  7. touch('./temp/'.$name);
  8. }
复制代码
避免副作用

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

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

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

Bad:

  1. // Global variable referenced by following function.
  2. // If we had another function that used this name, now it'd be an array and it could break it.
  3. $name = 'Ryan McDermott';
  4. function splitIntoFirstAndLastName()
  5. {
  6. global $name;
  7. $name = explode(' ', $name);
  8. }
  9. splitIntoFirstAndLastName();
  10. var_dump($name); // ['Ryan', 'McDermott'];
复制代码

Good:

  1. function splitIntoFirstAndLastName($name)
  2. {
  3. return explode(' ', $name);
  4. }
  5. $name = 'Ryan McDermott';
  6. $newName = splitIntoFirstAndLastName($name);
  7. var_dump($name); // 'Ryan McDermott';
  8. var_dump($newName); // ['Ryan', 'McDermott'];
复制代码
不要修改全局变量

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

Bad:

  1. function config()
  2. {
  3. return [
  4. 'foo' => 'bar',
  5. ]
  6. }
复制代码

Good:

  1. class Configuration
  2. {
  3. private $configuration = [];
  4. public function __construct(array $configuration)
  5. {
  6. $this->configuration = $configuration;
  7. }
  8. public function get($key)
  9. {
  10. return isset($this->configuration[$key]) ? $this->configuration[$key] : null;
  11. }
  12. }
  13. $configuration = new Configuration([
  14. 'foo' => 'bar',
  15. ]);
复制代码
避免条件判断

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

Bad:

  1. class Airplane
  2. {
  3. // ...
  4. public function getCruisingAltitude()
  5. {
  6. switch ($this->type) {
  7. case '777':
  8. return $this->getMaxAltitude() - $this->getPassengerCount();
  9. case 'Air Force One':
  10. return $this->getMaxAltitude();
  11. case 'Cessna':
  12. return $this->getMaxAltitude() - $this->getFuelExpenditure();
  13. }
  14. }
  15. }
复制代码

Good:

  1. interface Airplane
  2. {
  3. // ...
  4. public function getCruisingAltitude();
  5. }
  6. class Boeing777 implements Airplane
  7. {
  8. // ...
  9. public function getCruisingAltitude()
  10. {
  11. return $this->getMaxAltitude() - $this->getPassengerCount();
  12. }
  13. }
  14. class AirForceOne implements Airplane
  15. {
  16. // ...
  17. public function getCruisingAltitude()
  18. {
  19. return $this->getMaxAltitude();
  20. }
  21. }
  22. class Cessna implements Airplane
  23. {
  24. // ...
  25. public function getCruisingAltitude()
  26. {
  27. return $this->getMaxAltitude() - $this->getFuelExpenditure();
  28. }
  29. }
复制代码


太阳http代理AD
回复

使用道具 举报

关闭

站长推荐上一条 /1 下一条