mirror of
https://github.com/jupeter/clean-code-php.git
synced 2025-09-25 21:49:04 +02:00
Merge pull request #103 from peter-gribanov/solid
SOLID section is very important
This commit is contained in:
403
README.md
403
README.md
@@ -31,15 +31,16 @@
|
|||||||
* [Use getters and setters](#use-getters-and-setters)
|
* [Use getters and setters](#use-getters-and-setters)
|
||||||
* [Make objects have private/protected members](#make-objects-have-privateprotected-members)
|
* [Make objects have private/protected members](#make-objects-have-privateprotected-members)
|
||||||
5. [Classes](#classes)
|
5. [Classes](#classes)
|
||||||
* [S: Single Responsibility Principle (SRP)](#single-responsibility-principle-srp)
|
|
||||||
* [O: Open/Closed Principle (OCP)](#openclosed-principle-ocp)
|
|
||||||
* [L: Liskov Substitution Principle (LSP)](#liskov-substitution-principle-lsp)
|
|
||||||
* [I: Interface Segregation Principle (ISP)](#interface-segregation-principle-isp)
|
|
||||||
* [D: Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip)
|
|
||||||
* [Use method chaining](#use-method-chaining)
|
* [Use method chaining](#use-method-chaining)
|
||||||
* [Prefer composition over inheritance](#prefer-composition-over-inheritance)
|
* [Prefer composition over inheritance](#prefer-composition-over-inheritance)
|
||||||
6. [Don’t repeat yourself (DRY)](#dont-repeat-yourself-dry)
|
6. [SOLID](#solid)
|
||||||
7. [Translations](#translations)
|
* [Single Responsibility Principle (SRP)](#single-responsibility-principle-srp)
|
||||||
|
* [Open/Closed Principle (OCP)](#openclosed-principle-ocp)
|
||||||
|
* [Liskov Substitution Principle (LSP)](#liskov-substitution-principle-lsp)
|
||||||
|
* [Interface Segregation Principle (ISP)](#interface-segregation-principle-isp)
|
||||||
|
* [Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip)
|
||||||
|
7. [Don’t repeat yourself (DRY)](#dont-repeat-yourself-dry)
|
||||||
|
8. [Translations](#translations)
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
@@ -1201,6 +1202,205 @@ echo 'Employee name: '.$employee->getName(); // Employee name: John Doe
|
|||||||
|
|
||||||
## Classes
|
## Classes
|
||||||
|
|
||||||
|
### Use method chaining
|
||||||
|
|
||||||
|
This pattern is very useful and commonly used in many libraries such
|
||||||
|
as PHPUnit and Doctrine. It allows your code to be expressive, and less verbose.
|
||||||
|
For that reason, use method chaining and take a look at how clean your code
|
||||||
|
will be. In your class functions, simply use `return $this` at the end of every `set` function,
|
||||||
|
and you can chain further class methods onto it.
|
||||||
|
|
||||||
|
**Bad:**
|
||||||
|
|
||||||
|
```php
|
||||||
|
class Car
|
||||||
|
{
|
||||||
|
private $make = 'Honda';
|
||||||
|
private $model = 'Accord';
|
||||||
|
private $color = 'white';
|
||||||
|
|
||||||
|
public function setMake($make)
|
||||||
|
{
|
||||||
|
$this->make = $make;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setModel($model)
|
||||||
|
{
|
||||||
|
$this->model = $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setColor($color)
|
||||||
|
{
|
||||||
|
$this->color = $color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
var_dump($this->make, $this->model, $this->color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$car = new Car();
|
||||||
|
$car->setColor('pink');
|
||||||
|
$car->setMake('Ford');
|
||||||
|
$car->setModel('F-150');
|
||||||
|
$car->dump();
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good:**
|
||||||
|
|
||||||
|
```php
|
||||||
|
class Car
|
||||||
|
{
|
||||||
|
private $make = 'Honda';
|
||||||
|
private $model = 'Accord';
|
||||||
|
private $color = 'white';
|
||||||
|
|
||||||
|
public function setMake($make)
|
||||||
|
{
|
||||||
|
$this->make = $make;
|
||||||
|
|
||||||
|
// NOTE: Returning this for chaining
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setModel($model)
|
||||||
|
{
|
||||||
|
$this->model = $model;
|
||||||
|
|
||||||
|
// NOTE: Returning this for chaining
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setColor($color)
|
||||||
|
{
|
||||||
|
$this->color = $color;
|
||||||
|
|
||||||
|
// NOTE: Returning this for chaining
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dump()
|
||||||
|
{
|
||||||
|
var_dump($this->make, $this->model, $this->color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$car = (new Car())
|
||||||
|
->setColor('pink')
|
||||||
|
->setMake('Ford')
|
||||||
|
->setModel('F-150')
|
||||||
|
->dump();
|
||||||
|
```
|
||||||
|
|
||||||
|
**[⬆ back to top](#table-of-contents)**
|
||||||
|
|
||||||
|
### Prefer composition over inheritance
|
||||||
|
|
||||||
|
As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four,
|
||||||
|
you should prefer composition over inheritance where you can. There are lots of
|
||||||
|
good reasons to use inheritance and lots of good reasons to use composition.
|
||||||
|
The main point for this maxim is that if your mind instinctively goes for
|
||||||
|
inheritance, try to think if composition could model your problem better. In some
|
||||||
|
cases it can.
|
||||||
|
|
||||||
|
You might be wondering then, "when should I use inheritance?" It
|
||||||
|
depends on your problem at hand, but this is a decent list of when inheritance
|
||||||
|
makes more sense than composition:
|
||||||
|
|
||||||
|
1. Your inheritance represents an "is-a" relationship and not a "has-a"
|
||||||
|
relationship (Human->Animal vs. User->UserDetails).
|
||||||
|
2. You can reuse code from the base classes (Humans can move like all animals).
|
||||||
|
3. You want to make global changes to derived classes by changing a base class.
|
||||||
|
(Change the caloric expenditure of all animals when they move).
|
||||||
|
|
||||||
|
**Bad:**
|
||||||
|
|
||||||
|
```php
|
||||||
|
class Employee
|
||||||
|
{
|
||||||
|
private $name;
|
||||||
|
private $email;
|
||||||
|
|
||||||
|
public function __construct($name, $email)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->email = $email;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bad because Employees "have" tax data.
|
||||||
|
// EmployeeTaxData is not a type of Employee
|
||||||
|
|
||||||
|
class EmployeeTaxData extends Employee
|
||||||
|
{
|
||||||
|
private $ssn;
|
||||||
|
private $salary;
|
||||||
|
|
||||||
|
public function __construct($name, $email, $ssn, $salary)
|
||||||
|
{
|
||||||
|
parent::__construct($name, $email);
|
||||||
|
|
||||||
|
$this->ssn = $ssn;
|
||||||
|
$this->salary = $salary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Good:**
|
||||||
|
|
||||||
|
```php
|
||||||
|
class EmployeeTaxData
|
||||||
|
{
|
||||||
|
private $ssn;
|
||||||
|
private $salary;
|
||||||
|
|
||||||
|
public function __construct($ssn, $salary)
|
||||||
|
{
|
||||||
|
$this->ssn = $ssn;
|
||||||
|
$this->salary = $salary;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
class Employee
|
||||||
|
{
|
||||||
|
private $name;
|
||||||
|
private $email;
|
||||||
|
private $taxData;
|
||||||
|
|
||||||
|
public function __construct($name, $email)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->email = $email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTaxData($ssn, $salary)
|
||||||
|
{
|
||||||
|
$this->taxData = new EmployeeTaxData($ssn, $salary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**[⬆ back to top](#table-of-contents)**
|
||||||
|
|
||||||
|
## SOLID
|
||||||
|
|
||||||
|
**SOLID** is the mnemonic acronym introduced by Michael Feathers for the first five principles named by Robert Martin, which meant five basic principles of object-oriented programming and design.
|
||||||
|
|
||||||
|
* [S: Single Responsibility Principle (SRP)](#single-responsibility-principle-srp)
|
||||||
|
* [O: Open/Closed Principle (OCP)](#openclosed-principle-ocp)
|
||||||
|
* [L: Liskov Substitution Principle (LSP)](#liskov-substitution-principle-lsp)
|
||||||
|
* [I: Interface Segregation Principle (ISP)](#interface-segregation-principle-isp)
|
||||||
|
* [D: Dependency Inversion Principle (DIP)](#dependency-inversion-principle-dip)
|
||||||
|
|
||||||
### Single Responsibility Principle (SRP)
|
### Single Responsibility Principle (SRP)
|
||||||
|
|
||||||
As stated in Clean Code, "There should never be more than one reason for a class
|
As stated in Clean Code, "There should never be more than one reason for a class
|
||||||
@@ -1716,195 +1916,6 @@ class Manager
|
|||||||
|
|
||||||
**[⬆ back to top](#table-of-contents)**
|
**[⬆ back to top](#table-of-contents)**
|
||||||
|
|
||||||
### Use method chaining
|
|
||||||
|
|
||||||
This pattern is very useful and commonly used in many libraries such
|
|
||||||
as PHPUnit and Doctrine. It allows your code to be expressive, and less verbose.
|
|
||||||
For that reason, use method chaining and take a look at how clean your code
|
|
||||||
will be. In your class functions, simply use `return $this` at the end of every `set` function,
|
|
||||||
and you can chain further class methods onto it.
|
|
||||||
|
|
||||||
**Bad:**
|
|
||||||
|
|
||||||
```php
|
|
||||||
class Car
|
|
||||||
{
|
|
||||||
private $make = 'Honda';
|
|
||||||
private $model = 'Accord';
|
|
||||||
private $color = 'white';
|
|
||||||
|
|
||||||
public function setMake($make)
|
|
||||||
{
|
|
||||||
$this->make = $make;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setModel($model)
|
|
||||||
{
|
|
||||||
$this->model = $model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setColor($color)
|
|
||||||
{
|
|
||||||
$this->color = $color;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function dump()
|
|
||||||
{
|
|
||||||
var_dump($this->make, $this->model, $this->color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$car = new Car();
|
|
||||||
$car->setColor('pink');
|
|
||||||
$car->setMake('Ford');
|
|
||||||
$car->setModel('F-150');
|
|
||||||
$car->dump();
|
|
||||||
```
|
|
||||||
|
|
||||||
**Good:**
|
|
||||||
|
|
||||||
```php
|
|
||||||
class Car
|
|
||||||
{
|
|
||||||
private $make = 'Honda';
|
|
||||||
private $model = 'Accord';
|
|
||||||
private $color = 'white';
|
|
||||||
|
|
||||||
public function setMake($make)
|
|
||||||
{
|
|
||||||
$this->make = $make;
|
|
||||||
|
|
||||||
// NOTE: Returning this for chaining
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setModel($model)
|
|
||||||
{
|
|
||||||
$this->model = $model;
|
|
||||||
|
|
||||||
// NOTE: Returning this for chaining
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setColor($color)
|
|
||||||
{
|
|
||||||
$this->color = $color;
|
|
||||||
|
|
||||||
// NOTE: Returning this for chaining
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function dump()
|
|
||||||
{
|
|
||||||
var_dump($this->make, $this->model, $this->color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$car = (new Car())
|
|
||||||
->setColor('pink')
|
|
||||||
->setMake('Ford')
|
|
||||||
->setModel('F-150')
|
|
||||||
->dump();
|
|
||||||
```
|
|
||||||
|
|
||||||
**[⬆ back to top](#table-of-contents)**
|
|
||||||
|
|
||||||
### Prefer composition over inheritance
|
|
||||||
|
|
||||||
As stated famously in [*Design Patterns*](https://en.wikipedia.org/wiki/Design_Patterns) by the Gang of Four,
|
|
||||||
you should prefer composition over inheritance where you can. There are lots of
|
|
||||||
good reasons to use inheritance and lots of good reasons to use composition.
|
|
||||||
The main point for this maxim is that if your mind instinctively goes for
|
|
||||||
inheritance, try to think if composition could model your problem better. In some
|
|
||||||
cases it can.
|
|
||||||
|
|
||||||
You might be wondering then, "when should I use inheritance?" It
|
|
||||||
depends on your problem at hand, but this is a decent list of when inheritance
|
|
||||||
makes more sense than composition:
|
|
||||||
|
|
||||||
1. Your inheritance represents an "is-a" relationship and not a "has-a"
|
|
||||||
relationship (Human->Animal vs. User->UserDetails).
|
|
||||||
2. You can reuse code from the base classes (Humans can move like all animals).
|
|
||||||
3. You want to make global changes to derived classes by changing a base class.
|
|
||||||
(Change the caloric expenditure of all animals when they move).
|
|
||||||
|
|
||||||
**Bad:**
|
|
||||||
|
|
||||||
```php
|
|
||||||
class Employee
|
|
||||||
{
|
|
||||||
private $name;
|
|
||||||
private $email;
|
|
||||||
|
|
||||||
public function __construct($name, $email)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
$this->email = $email;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bad because Employees "have" tax data.
|
|
||||||
// EmployeeTaxData is not a type of Employee
|
|
||||||
|
|
||||||
class EmployeeTaxData extends Employee
|
|
||||||
{
|
|
||||||
private $ssn;
|
|
||||||
private $salary;
|
|
||||||
|
|
||||||
public function __construct($name, $email, $ssn, $salary)
|
|
||||||
{
|
|
||||||
parent::__construct($name, $email);
|
|
||||||
|
|
||||||
$this->ssn = $ssn;
|
|
||||||
$this->salary = $salary;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Good:**
|
|
||||||
|
|
||||||
```php
|
|
||||||
class EmployeeTaxData
|
|
||||||
{
|
|
||||||
private $ssn;
|
|
||||||
private $salary;
|
|
||||||
|
|
||||||
public function __construct($ssn, $salary)
|
|
||||||
{
|
|
||||||
$this->ssn = $ssn;
|
|
||||||
$this->salary = $salary;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
class Employee
|
|
||||||
{
|
|
||||||
private $name;
|
|
||||||
private $email;
|
|
||||||
private $taxData;
|
|
||||||
|
|
||||||
public function __construct($name, $email)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
$this->email = $email;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setTaxData($ssn, $salary)
|
|
||||||
{
|
|
||||||
$this->taxData = new EmployeeTaxData($ssn, $salary);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**[⬆ back to top](#table-of-contents)**
|
|
||||||
|
|
||||||
## Don’t repeat yourself (DRY)
|
## Don’t repeat yourself (DRY)
|
||||||
|
|
||||||
Try to observe the [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principle.
|
Try to observe the [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) principle.
|
||||||
|
Reference in New Issue
Block a user