原文地址:https://github.com/jupeter/cl...
译文地址:https://github.com/nineyang/c...,欢迎star
。php
不友好的:html
$ymdstr = $moment->format('y-m-d');友好的:node
$currentDate = $moment->format('y-m-d');
不友好的:git
getUserInfo(); getUserData(); getUserRecord(); getUserProfile();友好的:github
getUser();
咱们读的代码量远比咱们写过的多。所以,写出可阅读和便于搜索的代码是及其重要的。在咱们的程序中写出一些难以理解的变量名
到最后甚至会让本身很是伤脑筋。
所以,让你的名字便于搜索吧。ajax不友好的:json
// 这里的448表明什么意思呢? $result = $serializer->serialize($data, 448);友好的:数组
$json = $serializer->serialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | >JSON_UNESCAPED_UNICODE);
不友好的promise
$address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches[1], $matches[2]);不至于那么糟糕的:安全
稍微好一些,可是这取决于咱们对正则的熟练程度。$address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(.+?)\s*(\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); [, $city, $zipCode] = $matches; saveCityZipCode($city, $zipCode);友好的:
经过对子模式的重命名减小了咱们对正则的熟悉和依赖程度。$address = 'One Infinite Loop, Cupertino 95014'; $cityZipCodeRegex = '/^[^,]+,\s*(?<city>.+?)\s*(?<zipCode>\d{5})$/'; preg_match($cityZipCodeRegex, $address, $matches); saveCityZipCode($matches['city'], $matches['zipCode']);
太多的
if else
嵌套会让你的代码难以阅读和维护。更加直白的代码会好不少。
- demo1
不友好的:
function isShopOpen($day): bool { if ($day) { if (is_string($day)) { $day = strtolower($day); if ($day === 'friday') { return true; } elseif ($day === 'saturday') { return true; } elseif ($day === 'sunday') { return true; } else { return false; } } else { return false; } } else { return false; } }友好的:
function isShopOpen(string $day): bool { if (empty($day)) { return false; } $openingDays = [ 'friday', 'saturday', 'sunday' ]; return in_array(strtolower($day), $openingDays, true); }
- demo2
不友好的:
function fibonacci(int $n) { if ($n < 50) { if ($n !== 0) { if ($n !== 1) { return fibonacci($n - 1) + fibonacci($n - 2); } else { return 1; } } else { return 0; } } else { return 'Not supported'; } }友好的:
function fibonacci(int $n): int { if ($n === 0 || $n === 1) { return $n; } if ($n > 50) { throw new \Exception('Not supported'); } return fibonacci($n - 1) + fibonacci($n - 2); }
别让其余人去猜你的变量名的意思。
更加直白的代码会好不少。不友好的:
$l = ['Austin', 'New York', 'San Francisco']; for ($i = 0; $i < count($l); $i++) { $li = $l[$i]; doStuff(); doSomeOtherStuff(); // ... // ... // ... // 等等,这个$li是什么意思? dispatch($li); }友好的:
$locations = ['Austin', 'New York', 'San Francisco']; foreach ($locations as $location) { doStuff(); doSomeOtherStuff(); // ... // ... // ... dispatch($location); }
若是你的类或对象的名字已经传达了一些信息,那么请别在变量名中重复。
不友好的
class Car { public $carMake; public $carModel; public $carColor; //... }友好的
class Car { public $make; public $model; public $color; //... }
不友好的
这里不太合理,由于变量
$breweryName
有多是NULL
。function createMicrobrewery($breweryName = 'Hipster Brew Co.'): void { // ... }不至于那么糟糕的
这种写法要比上一版稍微好理解一些,可是若是能控制变量值获取会更好。
function createMicrobrewery($name = null): void { $breweryName = $name ?: 'Hipster Brew Co.'; // ... }友好的
若是你仅支持
PHP 7+
,那么你可使用类型约束而且保证$http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration
变量不会为NULL
。function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void { // ... }
限制函数的参数对于在对函数作测试来讲至关重要。有超过三个可选的参数会给你的测试工做量带来倍速增加。
最理想的状况是没有参数。1-2个参数也还凑合,可是三个参数就应该避免了。参数越多,咱们须要维护的就越多。一般,若是你的函>数有超过2个的参数,那么你的这个函数须要处理的事情就太多了。若是的确须要这么多参数,那么在大多数状况下, 用一个对象来处理可能会更合适。
不友好的:
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 { // ... }
在软件工程行业,这是最重要的准则。当函数所处理的事情超过一件,他就会变得难以实现,测试和理解。当你能让一个函数仅仅负责一个事情,他们就会变得容易重构而且理解起来越清晰。光是执行这样一条原则就能让你成为开发者中的佼佼者了。
不友好的:
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(); }
不友好的:
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();
当你的函数有超过一层的抽象时便意味着这个函数作了太多事情。解耦这个函数导致其变得可重用和更易测试。
不友好的:
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... } }一样不太友好:
咱们已经从函数中拆分除了一些东西出来,可是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) { // parse... } }友好的
最优解就是把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) { // parse... } } }
flag
相关的参数当你使用
flag
时便意味着你的函数作了超过一件事情。前面咱们也提到了,函数应该只作一件事情。若是你的代码取决于一个boolean
,那么仍是把这些内容拆分出来吧。不友好的
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); }
当函数有数据的输入和输出时可能会产生反作用。这个反作用可能被写入一个文件,修改一些全局变量,或者意外的把你的钱转给一个陌生人。
此刻,你可能有时候会须要这些反作用。正如前面所说,你可能须要写入到一个文件中。你须要注意的是把这些你所作的东西在你的掌控之下。别让某些个别函数或者类写入了一个特别的文件。对于全部的都应该一视同仁。有且仅有一个结果。
重要的是要避免那些譬如共享无结构的对象,使用能够写入任何类型的可变数据,不对反作用进行集中处理等常见的陷阱。若是你能够作到,你将会比大多数程序猿更加轻松。
不友好的
// 全局变量被下面的函数引用了。 // 若是咱们在另一个函数中使用了这个`$name`变量,那么可能会变成一个数组或者程序被打断。 $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'];
在大多数语言中,全局变量被污染都是一个不太好的实践,由于当你引入另外的包时会起冲突而且使用你的
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; } }经过建立Configuration
类的实例来引入配置$configuration = new Configuration([ 'foo' => 'bar', ]);至此,你就能够是在你的项目中使用这个配置了。
单例模式是一种反模式。为何不建议使用:
- 他们一般使用一个全局实例,为何这么糟糕?由于你隐藏了依赖关系在你的项目的代码中,而不是经过接口暴露出来。你应该有意识的去避免那些全局的东西。
- 他们违背了单一职责原则:他们会本身控制本身的生命周期。
- 这种模式会天然而然的使代码耦合在一块儿。这会让他们在测试中,不少状况下都理所固然的不一致。
- 他们持续在整个项目的生命周期中。另一个严重的打击是当你须要排序测试的时候,在单元测试中这会是一个不小的麻烦。为何?由于每一个单元测试都应该依赖于另一个。
不友好的
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) { // ... } // ... }使用 DSN配置来建立一个DBConnection
类的单例。$connection = new DBConnection($dsn);此时,在你的项目中必须使用DBConnection
的单例。
不友好的
if ($article->state === 'published') { // ... }友好的
if ($article->isPublished()) { // ... }
不友好的
function isDOMNodeNotPresent(\DOMNode $node): bool { // ... } if (!isDOMNodeNotPresent($node)) { // ... }友好的
function isDOMNodePresent(\DOMNode $node): bool { // ... } if (isDOMNodePresent($node)) { // ... }
这彷佛是一个不可能的任务。不少人的脑海中可能会在第一时间萦绕“若是没有
if
条件我还能作什么呢?”。答案就是,在大多数状况下,你可使用多态去处理这个难题。此外,可能有人又会说了,“即便多态能够作到,可是咱们为何要这么作呢?”,对此咱们的解释是,一个函数应该只作一件事情,这也正是咱们在前面所提到的让代码更加整洁的原则。当你的函数中使用了太多的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(); } }
PHP
是一门弱类型语言,这意味着你的函数可使用任何类型的参数。他在给予你无限的自由的同时又让你困扰,由于有有时候你须要作类型检测。这里有不少方式去避免这种事情,第一种方式就是统一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(Traveler $vehicle): void { $vehicle->travelTo(new Location('texas')); }
若是你正使用诸如字符串、整型和数组等基本类型,且要求版本是PHP 7+,不能使用多态,须要类型检测,那你应当考虑类型声明或者严格模式。它提供了基于标准PHP语法的静态类型。手动检查类型的问题是作好了须要好多的废话,好像为了安全就能够不顾损失可读性。保持你的
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; }
没有再使用的代码就比如重复代码同样糟糕。在你的代码库中彻底没有必要保留。若是肯定再也不使用,那就把它删掉吧!若是有一天你要使用,你也能够在你的版本记录中找到它。
不友好的:
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');
在
PHP
中你能够设置public
,protected
,和private
关键词来修饰你的方法。当你使用它们,你就能够在一个对象中控制这些属性的修改权限了。
- 当你想要对对象的属性进行除了“获取”以外的操做时,你没必要再去浏览并在代码库中修改权限。
- 当你要作一些修改属性的操做时,你更易于在代码中作逻辑验证。
- 封装内部表示。
- 当你在作获取和设置属性的操做时,更易于添加
log
或error
的操做。- 当其余
class
继承了这个基类,你能够重写默认的方法。- 你能够为一个服务延迟的去获取这个对象的属性值。
不太友好的:
class BankAccount { public $balance = 1000; } $bankAccount = new BankAccount(); // Buy shoes... $bankAccount->balance -= 100;友好的:
class BankAccount { private $balance; public function __construct(int $balance = 1000) { $this->balance = $balance; } public function withdraw(int $amount): void { if ($amount > $this->balance) { throw new \Exception('Amount greater than available balance.'); } $this->balance -= $amount; } public function deposit(int $amount): void { $this->balance += $amount; } public function getBalance(): int { return $this->balance; } } $bankAccount = new BankAccount(); // Buy shoes... $bankAccount->withdraw($shoesPrice); // Get balance $balance = $bankAccount->getBalance();
private/protected
限定
public
修饰的方法和属性同上来讲被修改是比较危险的,由于一些外部的代码能够轻易的依赖于他们而且你没办法控制哪些代码依赖于他们。对于全部用户的类来讲,在类中能够修改是至关危险的。protected
修饰器和public
一样危险,由于他们在继承链中一样能够操做。两者的区别仅限于权限机制,而且封装保持不变。对于全部子类来讲,在类中修改也是至关危险的。private
修饰符保证了代码只有在本身类的内部修改才是危险的。所以,当你在须要对外部的类设置权限时使用
private
修饰符去取代public/protected
吧。若是须要了解更多信息你能够读Fabien Potencier写的这篇文章
不太友好的:
class Employee { public $name; public function __construct(string $name) { $this->name = $name; } } $employee = new Employee('John Doe'); echo 'Employee name: '.$employee->name; // Employee name: John Doe友好的:
class Employee { private $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } } $employee = new Employee('John Doe'); echo 'Employee name: '.$employee->getName(); // Employee name: John Doe
正如
the Gang of Four
在著名的Design Patterns中所说,你应该尽量的使用组合而不是继承。无论是使用组合仍是继承都有不少的优势。最重要的一个准则在于当你本能的想要使用继承时,不妨思考一下组合是否能让你的问题解决的更加优雅。在某些时候确实如此。你可能会这么问了,“那到底何时我应该使用继承呢?”这彻底取决你你手头上的问题,下面正好有一些继承优于组合的例子:
- 你的继承表达了“是一个”而不是“有一个”的关系(Human->Animal vs. User->UserDetails)。
- 你可能会重复的使用基类的代码(Humans can move like all animals)。
- 你渴望在修改代码的时候经过基类来统一调度(Change the caloric expenditure of all animals when they move)。
不友好的:
class Employee { private $name; private $email; public function __construct(string $name, string $email) { $this->name = $name; $this->email = $email; } // ... } // 这里不太合理的缘由在于并不是全部的职员都有`tax`这个特征。 class EmployeeTaxData extends Employee { private $ssn; private $salary; public function __construct(string $name, string $email, string $ssn, string $salary) { parent::__construct($name, $email); $this->ssn = $ssn; $this->salary = $salary; } // ... }友好的:
class EmployeeTaxData { private $ssn; private $salary; public function __construct(string $ssn, string $salary) { $this->ssn = $ssn; $this->salary = $salary; } // ... } class Employee { private $name; private $email; private $taxData; public function __construct(string $name, string $email) { $this->name = $name; $this->email = $email; } public function setTaxData(string $ssn, string $salary) { $this->taxData = new EmployeeTaxData($ssn, $salary); } // ... }
在使用一些链式方法时,这种连贯接口能够不断地指向当前对象让咱们的代码显得更加清晰可读。
一般状况下,咱们在构建对象时均可以利用他的上下文这一特征,由于这种模式能够减小代码的冗余,不过在PHPUnit Mock Builder或者Doctrine Query Builder所说起的,有时候这种方式会带来一些麻烦:
若是须要了解更多信息你能够读Marco Pivetta写的这篇文章
友好的:
class Car { private $make = 'Honda'; private $model = 'Accord'; private $color = 'white'; public function setMake(string $make): self { $this->make = $make; // NOTE: Returning this for chaining return $this; } public function setModel(string $model): self { $this->model = $model; // NOTE: Returning this for chaining return $this; } public function setColor(string $color): self { $this->color = $color; // NOTE: Returning this for chaining return $this; } public function dump(): void { var_dump($this->make, $this->model, $this->color); } } $car = (new Car()) ->setColor('pink') ->setMake('Ford') ->setModel('F-150') ->dump();不友好的:
class Car { private $make = 'Honda'; private $model = 'Accord'; private $color = 'white'; public function setMake(string $make): void { $this->make = $make; } public function setModel(string $model): void { $this->model = $model; } public function setColor(string $color): void { $this->color = $color; } public function dump(): void { var_dump($this->make, $this->model, $this->color); } } $car = new Car(); $car->setColor('pink'); $car->setMake('Ford'); $car->setModel('F-150'); $car->dump();
SOLID最开始是由Robert Martin提出的五个准则,并最后由Michael Feathers命名的简写,这五个是在面对对象设计中的五个基本原则。
- S: 职责单一原则 (SRP)
- O: 开闭原则 (OCP)
- L: 里氏替换原则 (LSP)
- I: 接口隔离原则 (ISP)
- D: 依赖反转原则 (DIP)
正如Clean Code所述,“修改类应该只有一个理由”。咱们老是喜欢在类中写入太多的方法,就像你在飞机上塞满你的行李箱。在这种状况下你的类没有高内聚的概念而且留下了不少能够修改的理由。尽量的减小你须要去修改类的时间是很是重要的。若是在你的单个类中有太多的方法而且你常常修改的话,那么若是其余代码库中有引入这样的模块的话会很是难以理解。
不友好的:
class UserSettings { private $user; public function __construct(User $user) { $this->user = $user; } public function changeSettings(array $settings): void { if ($this->verifyCredentials()) { // ... } } private function verifyCredentials(): bool { // ... } }友好的:
class UserAuth { private $user; public function __construct(User $user) { $this->user = $user; } public function verifyCredentials(): bool { // ... } } class UserSettings { private $user; private $auth; public function __construct(User $user) { $this->user = $user; $this->auth = new UserAuth($user); } public function changeSettings(array $settings): void { if ($this->auth->verifyCredentials()) { // ... } } }
正如Bertrand Meyer所说,“软件开发应该对扩展开发,对修改关闭。”这是什么意思呢?这个原则的意思大概就是说你应该容许其余人在不修改已经存在的功能的状况下去增长新功能。
不友好的
abstract class Adapter { protected $name; public function getName(): string { return $this->name; } } class AjaxAdapter extends Adapter { public function __construct() { parent::__construct(); $this->name = 'ajaxAdapter'; } } class NodeAdapter extends Adapter { public function __construct() { parent::__construct(); $this->name = 'nodeAdapter'; } } class HttpRequester { private $adapter; public function __construct(Adapter $adapter) { $this->adapter = $adapter; } public function fetch(string $url): Promise { $adapterName = $this->adapter->getName(); if ($adapterName === 'ajaxAdapter') { return $this->makeAjaxCall($url); } elseif ($adapterName === 'httpNodeAdapter') { return $this->makeHttpCall($url); } } private function makeAjaxCall(string $url): Promise { // request and return promise } private function makeHttpCall(string $url): Promise { // request and return promise } }友好的:
interface Adapter { public function request(string $url): Promise; } class AjaxAdapter implements Adapter { public function request(string $url): Promise { // request and return promise } } class NodeAdapter implements Adapter { public function request(string $url): Promise { // request and return promise } } class HttpRequester { private $adapter; public function __construct(Adapter $adapter) { $this->adapter = $adapter; } public function fetch(string $url): Promise { return $this->adapter->request($url); } }
这自己是一个很是简单的原则却起了一个不太容易理解的名字。这个原则一般的定义是“若是S是T的一个子类,那么对象T能够在没有任何警告的状况下被他的子类替换(例如:对象S可能代替对象T)一些更合适的属性。”好像更难理解了。
最好的解释就是说若是你有一个父类和子类,那么你的父类和子类能够在原来的基础上任意交换。这个可能仍是难以理解,咱们举一个正方形-长方形的例子吧。在数学中,一个矩形属于长方形,可是若是在你的模型中经过继承使用了“is-a”的关系就不对了。
不友好的:
class Rectangle { protected $width = 0; protected $height = 0; public function render(int $area): void { // ... } public function setWidth(int $width): void { $this->width = $width; } public function setHeight(int $height): void { $this->height = $height; } public function getArea(): int { return $this->width * $this->height; } } class Square extends Rectangle { public function setWidth(int $width): void { $this->width = $this->height = $width; } public function setHeight(int $height): void { $this->width = $this->height = $height; } } function renderLargeRectangles(array $rectangles): void { foreach ($rectangles as $rectangle) { $rectangle->setWidth(4); $rectangle->setHeight(5); $area = $rectangle->getArea(); // BAD: Will return 25 for Square. Should be 20. $rectangle->render($area); } } $rectangles = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles($rectangles);友好的:
abstract class Shape { protected $width = 0; protected $height = 0; abstract public function getArea(): int; public function render(int $area): void { // ... } } class Rectangle extends Shape { public function setWidth(int $width): void { $this->width = $width; } public function setHeight(int $height): void { $this->height = $height; } public function getArea(): int { return $this->width * $this->height; } } class Square extends Shape { private $length = 0; public function setLength(int $length): void { $this->length = $length; } public function getArea(): int { return pow($this->length, 2); } } function renderLargeRectangles(array $rectangles): void { foreach ($rectangles as $rectangle) { if ($rectangle instanceof Square) { $rectangle->setLength(5); } elseif ($rectangle instanceof Rectangle) { $rectangle->setWidth(4); $rectangle->setHeight(5); } $area = $rectangle->getArea(); $rectangle->render($area); } } $shapes = [new Rectangle(), new Rectangle(), new Square()]; renderLargeRectangles($shapes);
ISP的意思就是说“使用者不该该强制使用它不须要的接口”。
当一个类须要大量的设置是一个不错的例子去解释这个原则。为了方便去调用这个接口须要作大量的设置,可是大多数状况下是不须要的。强制让他们使用这些设置会让整个接口显得臃肿。
不友好的:
interface Employee { public function work(): void; public function eat(): void; } class Human implements Employee { public function work(): void { // ....working } public function eat(): void { // ...... eating in lunch break } } class Robot implements Employee { public function work(): void { //.... working much more } public function eat(): void { //.... robot can't eat, but it must implement this method } }友好的:
并不是每个工人都是职员,可是每个职员都是工人。interface Workable { public function work(): void; } interface Feedable { public function eat(): void; } interface Employee extends Feedable, Workable { } class Human implements Employee { public function work(): void { // ....working } public function eat(): void { //.... eating in lunch break } } // robot can only work class Robot implements Workable { public function work(): void { // ....working } }
这个原则有两个须要注意的地方:
- 高阶模块不能依赖于低阶模块。他们都应该依赖于抽象。
- 抽象不该该依赖于实现,实现应该依赖于抽象。
第一点可能有点难以理解,可是若是你有使用过像
Symfony
的PHP
框架,你应该有见到过依赖注入这样的原则的实现。尽管他们是不同的概念,DIP
让高阶模块从咱们所知道的低阶模块中分离出去。能够经过DI
这种方式实现。一个巨大的好处在于它解耦了不一样的模块。耦合是一个很是很差的开发模式,由于它会让你的代码难以重构。不友好的:
class Employee { public function work(): void { // ....working } } class Robot extends Employee { public function work(): void { //.... working much more } } class Manager { private $employee; public function __construct(Employee $employee) { $this->employee = $employee; } public function manage(): void { $this->employee->work(); } }友好的
interface Employee { public function work(): void; } class Human implements Employee { public function work(): void { // ....working } } class Robot implements Employee { public function work(): void { //.... working much more } } class Manager { private $employee; public function __construct(Employee $employee) { $this->employee = $employee; } public function manage(): void { $this->employee->work(); } }
尝试去研究DRY原则。
尽量别去复制代码。复制代码很是很差,由于这意味着未来有须要修改的业务逻辑时你须要修改不止一处。
想象一下你在经营一个餐馆而且你须要常常整理你的存货清单:你全部的土豆,洋葱,大蒜,辣椒等。若是你有多个列表来管理进销记录,当你用其中一些土豆作菜时你须要更新全部的列表。若是你只有一个列表的话只有一个地方须要更新!
大多数状况下你有重复的代码是由于你有超过两处细微的差异,他们大部分都是相同的,可是他们的不一样之处又不得不让你去分红不一样的方法去处理相同的事情。移除这些重复的代码意味着你须要建立一个能够用一个方法/模块/类来处理的抽象。
使用一个抽象是关键的,这也是为何在类中你要遵循
SOLID
原则的缘由。一个不优雅的抽象每每比重复的代码更糟糕,因此要谨慎使用!说了这么多,若是你已经能够构造一个优雅的抽象,那就赶忙去作吧!别重复你的代码,不然当你须要修改时你会发现你要修改许多地方。不友好的:
function showDeveloperList(array $developers): void { foreach ($developers as $developer) { $expectedSalary = $developer->calculateExpectedSalary(); $experience = $developer->getExperience(); $githubLink = $developer->getGithubLink(); $data = [ $expectedSalary, $experience, $githubLink ]; render($data); } } function showManagerList(array $managers): void { foreach ($managers as $manager) { $expectedSalary = $manager->calculateExpectedSalary(); $experience = $manager->getExperience(); $githubLink = $manager->getGithubLink(); $data = [ $expectedSalary, $experience, $githubLink ]; render($data); } }友好的:
function showList(array $employees): void { foreach ($employees as $employee) { $expectedSalary = $employee->calculateExpectedSalary(); $experience = $employee->getExperience(); $githubLink = $employee->getGithubLink(); $data = [ $expectedSalary, $experience, $githubLink ]; render($data); } }很是优雅的:
若是能更简洁那就更好了。function showList(array $employees): void { foreach ($employees as $employee) { render([ $employee->calculateExpectedSalary(), $employee->getExperience(), $employee->getGithubLink() ]); } }
文章首发地址:个人博客