Maxence POUTORD
Cal • is • then • ics - /ˌkaləsˈTHeniks/
In the ThoughtWorks Anthology, Jeff Bay, list 9 rules for writing better Object Oriented code.
readability, maintainability, testability, and comprehensibility
...make your OO code great again!
public someFunction(someParameter)
{
if (firstCondition) {
if (secondCondition) {
// Do Something
// Do Something
// Do Something
}
}
}
public someFunction(someParameter)
{
if (!firstCondition) {
return;
}
if (secondCondition) {
// Do Something
// Do Something
// Do Something
}
}
public someFunction(someParameter)
{
if (!firstCondition) {
return;
}
if (!secondCondition) {
return;
}
// Do Something
// Do Something
// Do Something
}
"Single Entry, Single Exit"...?
"Single Entry, Single Exit" was written for assembly languages like Fortran/COBOL
You may need to split your code into multiple functions.
This is the Extract Method (cf. Martin Fowler).
//Class Command
public function validateProductList(array $products): array
{
$validProducts[];
foreach ($products as $product)
{
if (!$product->price > 0 && length($product->name)) {
$validProducts[] = $product;
}
}
return $validProducts;
}
//Class Command
public function validateProductList(array $products): array
{
return array_filter($products, 'isValidProduct');
}
public function isValidProduct(Product $product): boolean
{
if (!$product->price > 0 && length($product->name)) {
return true;
}
return false;
}
function foo($products)
{
if (isset($products) && count($products)) {
foreach ($products as $product) {
# code
}
}
}
function foo($products)
{
if (isset($products) && count($products)) {
foreach ($products as $product) {
# code
}
}
}
function foo($products)
{
foreach ($products as $product) {
# code
}
}
public function login($username, $password)
{
if ($this->isValid($username, $password)) {
redirect("homepage");
} else {
addFlash("error", "Bad credentials");
redirect("login");
}
}
public function login($username, $password)
{
if ($this->isValid($username, $password)) {
return redirect("homepage");
} else {
addFlash("error", "Bad credentials");
return redirect("login");
}
}
public function login($username, $password)
{
// defensive approach
if ($this->isValid($username, $password)) {
return redirect("homepage");
}
addFlash("error", "Bad credentials");
return redirect("login");
}
public function login($username, $password)
{
// optimistic approach
if (!$this->isValid($username, $password)) {
addFlash("error", "Bad credentials");
return redirect("login");
}
return redirect("homepage");
}
class User { /** @var string */ private $name; /** @var string */ private $email; public function __construct(string $name, string $email) { $this->name = $name; $this->email = $email; } }
//this is ok $myUser = new User("John", "john@mail.fr");
//this is ok $anotherUser = new User("Max", "0811223344");
//this is ok $whoeverUser = new User("Louise", "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
//this is ok $whoeverUserBis = new User("Lucie", "打才我醫集老電了快洋,西足並良步細主!");
class Email { /** @var string */ private $email; public function __construct(string $email) { if (!filter_var($email, FILTER_VALIDATE_EMAIL) { throw new InvalidArgumentException("Invalid format!"); } $this->email = $email; } }
// This is ok $myUser = new User("John", new Email("john@mail.fr"));
// This is NOT ok $anotherUser = new User("Max", new Email("0811223344"));
// This is NOT ok $whoeverUser = new User("Louise", new Email("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."));
// This is NOT ok $whoeverUserBis = new User("Lucie", new Email("打才我醫集老電了快洋,西足並良步細主!"));
$command = array(
array(
"user" => array(
"id" => 42,
"name" => "Maxence",
"address" => "Dublin"
),
"date" => "1989-04-11 07:28:47",
"beers" => array(
array(
"ID" => "76b44e1a-a8d6-11e6-80f5-76304dec7eb7",
"name" => "Tripel Karmeliet",
"quantity" => 4,
),
array(
"ID" => "76b45220-a8d6-11e6-80f5-76304dec7eb7",
"name" => "Journeyman's Pale Ale",
"quantity" => 8,
),
array(
"ID" => "76b45356-a8d6-11e6-80f5-76304dec7eb7",
"name" => "Duvel tripel hop",
"quantity" => 15,
)
)
)
);
foreach ($command["beers"] as $beer) {
if (isset($beer["quantity"])) {
$itemsCounter += $beer["quantity"];
}
}
// ...or
$itemsCounter = $command->countItems();
class Company
{
private $name;
private $employes;
# code
}
Where can I put filters/contains/... methods?
class Team
{
private $name;
private $employes;
# code
}
'employes' collection need to be wrapped in its own class
class EmployeesCollection
{
private $employes;
public function add(Employe $employe) {/** ... **/};
public function remove(Employe $employe) {/** ... **/};
public function count() {/** ... **/};
public function filter(Closure $p): {/** ... **/};
//...
}
"Don't talk to strangers. Only talk to your immediate friends"
$objectA->getObjectB()->getObjectC();
$objectA->getThis()->getThat()->getSomethingElse()->doSomething();
What happen if getThat() return NULL?
S mean Single Responsibility Principle (SRP)
There are only two hard things in Computer Science:
cache invalidation and naming things.
— Phil Karlton
foreach ($users as $u) {
// code
// code
// code
// code
// code
$u->sendMail($aMail); // "$u": what does it stand for?
}
class Manager
{
function perform()
{
// ...
}
}
You can also replace perform by process()
...
An object that knows too much or does too much
"Simplicity Comes from Reduction"
— Paul W. Homer
Long files are hard to:
"big stuff attracts even more stuff and gets bigger"
≈ theory of the broken window
Try to keep 2 5 instance variables (max)
class MyWonderfulService
{
private $anotherService;
private $logger;
private $translator;
private $entityService;
// code
}
"Don't ask for the information you need to do the work; ask the object that has the information to do the work for you"
— Allen Holub
// Game
private $score;
public function setScore(int $score): void {
$this->score = $score;
}
public function getScore(): int {
return score;
}
// Usage
$game->setScore($game->getScore() + ENEMY_DESTROYED_SCORE);
// Game
public function addScore(int $delta): void {
this->score += $delta;
}
// Usage
$game->addScore(ENEMY_DESTROYED_SCORE);
class Employe
{
private $name;
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
return $this;
}
}
class Employe
{
public $name;
}
#1: One level of indentation | |
#2: Don’t use the ELSE keyword | |
#3: Wrap all primitives and Strings | |
#4: First class collections | |
#5: One dot per line | |
#6: Don’t abbreviate | |
#7: Keep all entities small | |
#8: No classes with several instance variables | |
#9: No getters/setters/properties |