90
Why Your Code Suxx And why you should care about it ?

Why your code suxx

Embed Size (px)

Citation preview

Page 1: Why your code suxx

Why Your Code SuxxAnd why you should care about it ?

Page 2: Why your code suxx

www.blubird.eu

Started mid 2013

Audit & consultancy

Project staffing

Project management

ABOUT BLUBIRD

Web & Mobile apps

Virtualization and cloud

Performance & optimization

Security

SMAC the pictureSecurity, Mobility, Apps & Cloud

Page 3: Why your code suxx

Nicolas De Boose

➢ Freelancer

➢ PHP, Symfony2, (Node) Js, Css

➢ 10 yrs pro

➢ A wonderful fiancee <3

➢@NicoDeBoose

➢ Blog: mechantblog.com

ABOUT ME

Page 4: Why your code suxx

WHY THIS SUBJECT?

1. Laravel vs Symfony2 vs Zend Framework2 ? Google it!

2. “Clean code” is a subject I like– Pull request– Gave lots of reviews– Read/watch Clean coders (book & website)

3. Every year, new coders must learn best practices, solid principles, patterns, ... And it’s a good booster shot

Page 5: Why your code suxx

HOW ?

1. Presentation of code that smells

2. Your opinion

3. Refactoring

Page 6: Why your code suxx

//Let's go to the bar

interface Human{public function getAge();public function getFullName();

}

interface Drink{public function getPrice();

}

interface Bar{public function removeFromStock(Drink $drink);public function hasInStock(Drink $drink);

}

Page 7: Why your code suxx

class BarMan{private $bar;

public function command(Human $human, Drink $drink){if ($this->bar->hasInStock($drink) == true) {

if ($drink->doesNotContainAlcohol()) {$this->prepare($human, $drink);

} else {if ($human->getAge() >= 18){

$this->prepare($human, $drink);} else {

$this->errorMessage = 'Too young :(';return false;

}}

} else {$this->errorMessage = 'Not in stock :(';

}return true;

}}

Page 8: Why your code suxx

class BarMan{private $bar;

public function command(Human $human, Drink $drink){if ($this->bar->hasInStock($drink) == true) {

if ($drink->doesNotContainAlcohol()) {$this->prepare($human, $drink);

} else {if ($human->getAge() >= 18){

$this->prepare($human, $drink);} else {

$this->errorMessage = 'Too young :(';return false;

}}

} else {$this->errorMessage = 'Not in stock :(';

}return true;

}}

Focus on this code

Page 9: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if ($this->bar->hasInStock($drink) == true) {if ($drink->doesNotContainAlcohol()) {

$this->prepare($human, $drink);} else {

if ($human->getAge() >= 18){$this->prepare($human, $drink);

} else {$this->errorMessage = 'Too young :(';return false;

}}

} else {$this->errorMessage = 'Not in stock :(';return false;

}return true;

Page 10: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if ($this->bar->hasInStock($drink) == true) {if ($drink->doesNotContainAlcohol()) {

$this->prepare($human, $drink);} else {

if ($human->getAge() >= 18){$this->prepare($human, $drink);

} else {$this->errorMessage = 'Too young :(';return false;

}}

} else {$this->errorMessage = 'Not in stock :(';return false;

}return true;

== true) == true) ...

Page 11: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if ($this->bar->hasInStock($drink)) {if ($drink->doesNotContainAlcohol()) {

$this->prepare($human, $drink);} else {

if ($human->getAge() >= 18){$this->prepare($human, $drink);

} else {$this->errorMessage = 'Too young :(';return false;

}}

} else {$this->errorMessage = 'Not in stock :(';return false;

}return true;

Page 12: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if ($this->bar->hasInStock($drink)) {if ($drink->doesNotContainAlcohol()) {

$this->prepare($human, $drink);} else {

if ($human->getAge() >= 18){$this->prepare($human, $drink);

} else {$this->errorMessage = 'Too young :(';return false;

}}

} else {$this->errorMessage = 'Not in stock :(';return false;

}return true;

Nested if’s

Page 13: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if ($this->bar->hasInStock($drink)) {if ($drink->doesNotContainAlcohol()) {

$this->prepare($human, $drink);} else {

if ($human->getAge() >= 18){$this->prepare($human, $drink);

} else {$this->errorMessage = 'Too young :(';return false;

}}

} else {$this->errorMessage = 'Not in stock :(';return false;

}return true;

Page 14: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {$this->errorMessage = 'Not in stock :(';return false;

}if ($drink->doesNotContainAlcohol()) {

$this->prepare($human, $drink);} else {

if ($human->getAge() >= 18){$this->prepare($human, $drink);

} else {$this->errorMessage = 'Too young :(';return false;

}}return true;

Page 15: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {$this->errorMessage = 'Not in stock :(';return false;

}if ($drink->doesNotContainAlcohol()) {

$this->prepare($human, $drink);} else {

if ($human->getAge() >= 18){$this->prepare($human, $drink);

} else {$this->errorMessage = 'Too young :(';return false;

}}return true;

call to “prepare()” here

And here + nested if

Page 16: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {$this->errorMessage = 'Not in stock :(';return false;

}if (!$drink->doesNotContainAlcohol()

&& $human->getAge() < 18) {

$this->errorMessage = 'Too young :(';return false;

}

$this->prepare($human, $drink);return true;

Page 17: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {$this->errorMessage = 'Not in stock :(';return false;

}if (!$drink->doesNotContainAlcohol()

&& $human->getAge() < 18) {

$this->errorMessage = 'Too young :(';return false;

}

$this->prepare($human, $drink);return true;

NOT doesNot… My head hurts!

Page 18: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {$this->errorMessage = 'Not in stock :(';return false;

}if ($drink->containsAlcohol()

&& $human->getAge() < 18) {

$this->errorMessage = 'Too young :(';return false;

}

$this->prepare($human, $drink);return true;

Page 19: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {$this->errorMessage = 'Not in stock :(';return false;

}if ($drink->containsAlcohol()

&& $human->getAge() < 18) {

$this->errorMessage = 'Too young :(';return false;

}

$this->prepare($human, $drink);return true;

can we be more explicit?

Page 20: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {$this->errorMessage = 'Not in stock :(';return false;

}if ($drink->containsAlcohol()

&& $human->isYoungerThan(18)) {

$this->errorMessage = 'Too young :(';return false;

}

$this->prepare($human, $drink);return true;

Page 21: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {$this->errorMessage = 'Not in stock :(';return false;

}if ($drink->containsAlcohol()

&& $human->isYoungerThan(18)) {

$this->errorMessage = 'Too young :(';return false;

}

$this->prepare($human, $drink);return true;

It’s not my job to keep the error message

It’s not my job to keep the error message

Page 22: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {throw new DrinkNotInStockException($drink);

}if ($drink->containsAlcohol()

&& $human->isYoungerThan(18)) {

return new CommandResult(false,'Too young :(');}

$this->prepare($human, $drink);return true;

Solution 1: Throw an exception

Solution 2: Return a “Result” object

Solution 1: Return nothingSolution 2: Return a “Result” object

Page 23: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {throw new DrinkNotInStockException($drink);

}if ($drink->containsAlcohol()

&& $human->isYoungerThan(18)) {

throw new TooYoungToDrinkAlcoholException();}

$this->prepare($human, $drink);

Page 24: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {throw new DrinkNotInStockException($drink);

}if ($drink->containsAlcohol()

&& $human->isYoungerThan(18)) {

throw new TooYoungToDrinkAlcoholException();}

$this->prepare($human, $drink);

What is “18”? It’s a magic number!

Page 25: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {throw new DrinkNotInStockException($drink);

}if ($drink->containsAlcohol()

&& $human->isYoungerThan(self::MINIMUM_AGE_FOR_ALCOHOL)

) {throw new TooYoungToDrinkAlcoholException();

}

$this->prepare($human, $drink);

Better but not the best place to store the constant

Page 26: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink)) {throw new DrinkNotInStockException($drink);

}if ($drink->containsAlcohol()

&& $human->isYoungerThan(BarLegislation::MINIMUM_AGE_FOR_ALCOHOL)

) {throw new TooYoungToDrinkAlcoholException();

}

$this->prepare($human, $drink);

There, it’s good enough for the moment

Page 27: Why your code suxx

//In Barman::command(Human $human, Drink $drink)

if (!$this->bar->hasInStock($drink))throw new DrinkNotInStockException($drink);

if ($drink->containsAlcohol() && $human->isYoungerThan

(BarLegislation::MINIMUM_AGE_FOR_ALCOHOL))

throw new TooYoungToDrinkAlcoholException();

$this->prepare($human, $drink);

Page 28: Why your code suxx

interface Human{// public function getAge(); => deleted

public function isYoungerThan($age); //New public function getFullName();

}

interface Drink{public function getPrice();public function containsAlcohol(); //Rename

}

interface Bar{public function removeFromStock(Drink $drink);public function hasInStock(Drink $drink);

}

Page 29: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$tmp = explode(" ", $human->getFullName());$ac = "";foreach ($tmp as $w) {

$ac .= $w[0];}$human->addToBill($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$drink->setAcronym($ac);SmsSender::send(

$human->getMobilePhoneNumber(),sprintf('Drink %s ready', $ac)

);

Page 30: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$tmp = explode(" ", $human->getFullName());$ac = "";foreach ($tmp as $w) {

$ac .= $w[0];}$human->addToBill($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$drink->setAcronym($ac);SmsSender::send(

$human->getMobilePhoneNumber(),sprintf('Drink %s ready', $ac)

);

What does “tmp” mean?

What does “ac” mean?

Page 31: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$nameParts = explode(" ",$human->getFullName());$acronym = "";foreach ($nameParts as $w) {

$acronym .= $w[0];}$human->addToBill($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$drink->setAcronym($acronym);SmsSender::send(

$human->getMobilePhoneNumber(),sprintf('Drink %s ready', $acronym)

);

Page 32: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$nameParts = explode(" ",$human->getFullName());$acronym = "";foreach ($nameParts as $w) {

$acronym .= $w[0];}$human->addToBill($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$drink->setAcronym($acronym);SmsSender::send(

$human->getMobilePhoneNumber(),sprintf('Drink %s ready', $acronym)

);

Can we compute the acronym elsewhere?

Page 33: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$acronym = $this->getHumanAcronym($human);$human->addToBill($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$drink->setAcronym($acronym);SmsSender::send(

$human->getMobilePhoneNumber(),sprintf('Drink %s ready', $acronym)

);

That’s better, but ...

Page 34: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$acronym = $human->getAcronym();$human->addToBill($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$drink->setAcronym($acronym);SmsSender::send(

$human->getMobilePhoneNumber(),sprintf('Drink %s ready', $acronym)

);

Page 35: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$acronym = $human->getAcronym();$human->addToBill($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$drink->setAcronym($acronym);SmsSender::send(

$human->getMobilePhoneNumber(),sprintf('Drink %s ready', $acronym)

);

Now let’s remove $acronym var

Page 36: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$human->addToBill($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$drink->setAcronym($human->getAcronym());SmsSender::send(

$human->getMobilePhoneNumber(),sprintf('Drink %s ready',$human->getAcronym())

);

Page 37: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$human->addToBill($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$drink->setAcronym($human->getAcronym());SmsSender::send(

$human->getMobilePhoneNumber(),sprintf('Drink %s ready',$human->getAcronym())

);

A bill for a “simple” human?

Page 38: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$client = new Client($human);$client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$drink->setAcronym($human->getAcronym());SmsSender::send(

$human->getMobilePhoneNumber(),sprintf('Drink %s ready',$human->getAcronym())

);

Create a “Client” class: He knows what he’s drinking

Page 39: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$client = new Client($human);$client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$drink->setAcronym($human->getAcronym());SmsSender::send(

$human->getMobilePhoneNumber(),sprintf('Drink %s ready',$human->getAcronym())

);

A drink should not be more than a drink.

Page 40: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$client = new Client($human);$client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$labeledDrink = new LabeledDrink($drink,$human->getAcronym());

SmsSender::send($human->getMobilePhoneNumber(),sprintf('Drink %s ready',

$labeledDrink->getLabel()));

Create a class that symbolizes the drink with an acronym

Take the label from the new object

Page 41: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$client = new Client($human);$client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$labeledDrink = new LabeledDrink($drink,$human->getAcronym());

SmsSender::send($human->getMobilePhoneNumber(),sprintf('Drink %s ready',

$labeledDrink->getLabel()));

Is there a better place to instantiate the object?

Page 42: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$client = new Client($human);$client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$labeledDrink = new LabeledDrink($drink,$human->getAcronym());

SmsSender::send($human->getMobilePhoneNumber(),sprintf('Drink %s ready',

$labeledDrink->getLabel()));

Is there a better place to instantiate the object?

Maybe here?

Page 43: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$client = new Client($human);$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send($human->getMobilePhoneNumber(),sprintf('Drink %s ready',

$labeledDrink->getLabel()));

Page 44: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$client = new Client($human);$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send($human->getMobilePhoneNumber(),sprintf('Drink %s ready',

$labeledDrink->getLabel()));

Not all human have a phone number

Page 45: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$client = new Client($human);$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send($client->getMobilePhoneNumber(),sprintf('Drink %s ready',

$labeledDrink->getLabel()));

Our clients have a phone number!

Page 46: Why your code suxx

//In Barman::prepare(Human $human, Drink $drink)

$client = new Client($human);$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send($client->getMobilePhoneNumber(),sprintf('Drink %s ready',

$labeledDrink->getLabel()));

Human becomes useless here

Page 47: Why your code suxx

//In Barman::prepare(Client $client, Drink $drink)

$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send($client->getMobilePhoneNumber(),sprintf('Drink %s ready',

$labeledDrink->getLabel()));

Page 48: Why your code suxx

//In Barman::prepare(Client $client, Drink $drink)

$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send($client->getMobilePhoneNumber(),sprintf('Drink %s ready',

$labeledDrink->getLabel()));

Hello ugly hard coded content!

Page 49: Why your code suxx

//In Barman::prepare(Client $client, Drink $drink)

$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send($client->getMobilePhoneNumber(),sprintf('Drink %s ready',

$labeledDrink->getLabel()));

Hello ugly hard coded content!

We actually send a message here...

Page 50: Why your code suxx

//In Barman::prepare(Client $client, Drink $drink)

$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send(new DrinkIsReadyMessage($labeledDrink, $client););

//FYI

class DrinkIsReadyMessage implements Message{public function getPhoneNumber(){

return $this->client->getMobilePhoneNumber();}public function getMessage(){/* … */}

}

Page 51: Why your code suxx

//In Barman::prepare(Client $client, Drink $drink)

$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send(new DrinkIsReadyMessage($labeledDrink, $client););

Page 52: Why your code suxx

//In Barman::prepare(Client $client, Drink $drink)

$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send(new DrinkIsReadyMessage($labeledDrink, $client););

Static is not testable!

Page 53: Why your code suxx

//In Barman::prepare(Client $client, Drink $drink)

$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send(new DrinkIsReadyMessage($labeledDrink, $client););

/* Should the bar manage the sms messaging system? */

Static is not testable!

Page 54: Why your code suxx

//In Barman::prepare(Client $client, Drink $drink)

$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send(new DrinkIsReadyMessage($labeledDrink, $client););

/* Should the bar manage the sms messaging system? Why not, but it already manages the drinks stock.*/

Static is not testable!

Page 55: Why your code suxx

//In Barman::prepare(Client $client, Drink $drink)

$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

SmsSender::send(new DrinkIsReadyMessage($labeledDrink, $client););

/* Should the bar manage the sms messaging system? Why not, but it already manages the drinks stock.Maybe the bar should be a facade?*/

Static is not testable!

Page 56: Why your code suxx

//In Barman::prepare(Client $client, Drink $drink)

$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$this->bar->sendMessage(new DrinkIsReadyMessage($labeledDrink, $client););

Simplest solution...

Page 57: Why your code suxx

//In Barman::prepare(Client $client, Drink $drink)

$labeledDrink = $client->addDrink($drink);$this->bar->removeFromStock($drink);

/* ... Prepare the drink ... */

$this->bar->sendMessage(new DrinkIsReadyMessage($labeledDrink, $client););

Simplest solution...

Voilà! Let's not over engineering for the moment :)

Page 58: Why your code suxx

interface Bar{public function removeFromStock(Drink $drink);public function hasInStock(Drink $drink);public function sendMessage(Message $message);//New

}

interface Client{public function addDrink(Drink $drink);public function getMobilePhoneNumber();

}interface labeledDrink extends Drink{ //New class

public function getLabel();}

interface Message{ //New interfacepublic function getPhoneNumber();public function getMessage();

}interface DrinkIsReadyMessage extends Message{} //New

Page 59: Why your code suxx

class BillPrinter{ //Let's print the bill!public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

if ($this->type === 'xml') {$this->renderAsXml($client);

} elseif ($this->type === 'html') {$this->renderAsHtml($client);

} else {throw new \Exception('Invalid input');

}}

public function renderAsHtml(Client $client){}public function renderAsXml(Client $client){}

}

Page 60: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

if ($this->type === 'xml') {$this->renderAsXml($client);

} elseif ($this->type === 'html') {$this->renderAsHtml($client);

} else {throw new \Exception('Invalid input');

}}

public function renderAsHtml(Client $client){}public function renderAsXml(Client $client){}

}

Generic exception

Page 61: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

if ($this->type === 'xml') {$this->renderAsXml($client);

} elseif ($this->type === 'html') {$this->renderAsHtml($client);

} else {throw new \InvalidTypeException();

}}

public function renderAsHtml(Client $client){}public function renderAsXml(Client $client){}

}

Page 62: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

if ($this->type === 'xml') {$this->renderAsXml($client);

} elseif ($this->type === 'html') {$this->renderAsHtml($client);

} else {throw new \InvalidTypeException();

}}

public function renderAsHtml(Client $client){}public function renderAsXml(Client $client){}

}

Render an xml

Render an html

2 responsabilities

Page 63: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

if ($this->type === 'xml') {(new XmlBillPrinter())->render($client);

} elseif ($this->type === 'html') {$this->renderAsHtml($client);

} else {throw new \InvalidTypeException();

}}

public function renderAsHtml(Client $client){}}

Render an html

Delegate xml

Page 64: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

if ($this->type === 'xml') {(new XmlBillPrinter())->render($client);

} elseif ($this->type === 'html') {(new HtmlBillPrinter())->render($client);

} else {throw new \InvalidTypeException();

}}

}

Delegate html

Delegate xml

Page 65: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

if ($this->type === 'xml') {(new XmlBillPrinter())->render($client);

} elseif ($this->type === 'html') {(new HtmlBillPrinter())->render($client);

} else {throw new \InvalidTypeException();

}}

}

Page 66: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

if ($this->type === 'xml') {(new XmlBillPrinter())->render($client);

} elseif ($this->type === 'html') {(new HtmlBillPrinter())->render($client);

} else {throw new \InvalidTypeException();

}}

}interface BillPrinterInterface{

public function render(Client $client);}

NB: The same interface is used

Page 67: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

if ($this->type === 'xml') {(new XmlBillPrinter())->render($client);

} elseif ($this->type === 'html') {(new HtmlBillPrinter())->render($client);

} else {throw new \InvalidTypeException();

}}

} It’s not my job to find what instantiate!I just want to render a bill!

Page 68: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

if ($this->type === 'xml') {(new XmlBillPrinter())->render($client);

} elseif ($this->type === 'html') {(new HtmlBillPrinter())->render($client);

} else {throw new \InvalidTypeException();

}}

} It’s not my job to find what instantiate!I just want to render a bill!

Delegate to a private method first...

Page 69: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

$this->getPrinterInstance()->render($client);}public function getPrinterInstance(){

if ($this->type === 'xml') {return new XmlBillPrinter();

} elseif ($this->type === 'html') {return new HtmlBillPrinter();

} else {throw new \InvalidTypeException();

}}

}

Page 70: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

$this->getPrinterInstance()->render($client);}public function getPrinterInstance(){

if ($this->type === 'xml') {return new XmlBillPrinter();

} elseif ($this->type === 'html') {return new HtmlBillPrinter();

} else {throw new \InvalidTypeException();

}}

}

It’s really not my job to find what instantiate!

Page 71: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

$this->getPrinterInstance()->render($client);}public function getPrinterInstance(){

if ($this->type === 'xml') {return new XmlBillPrinter();

} elseif ($this->type === 'html') {return new HtmlBillPrinter();

} else {throw new \InvalidTypeException();

}}

}

It’s really not my job to find what instantiate!

It’s a factory job!

Page 72: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

BillPrinterFactory::create($this->type)->render($client);

}}class BillPrinterFactory{

public static function create(string $type){if ($type === 'xml') {

return new XmlBillPrinter();} else //...

}}

Page 73: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

BillPrinterFactory::create($this->type)->render($client);

}}class BillPrinterFactory{

public static function create(string $type){if ($type === 'xml') {

return new XmlBillPrinter();} else //...

}}

What is the purpose of this class again?

Page 74: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

BillPrinterFactory::create($this->type)->render($client);

}}class BillPrinterFactory{

public static function create(string $type){if ($type === 'xml') {

return new XmlBillPrinter();} else //...

}}

What is the purpose of this class again?

Page 75: Why your code suxx

class BillPrinter{public function __construct(string $type) {

$this->type = $type;}public function renderClientBill(Client $client){

BillPrinterFactory::create($this->type)->render($client);

}}class BillPrinterFactory{

public static function create(string $type){if ($type === 'xml') {

return new XmlBillPrinter();} else //...

}}

Remove the unnecessary class :)

Page 76: Why your code suxx

//What about the render of the bill in HTML?class HtmlBillPrinter

implements BillPrinterInterface{

public function render(Client $client){foreach($client->getDrinks() as $drink){

$render .= '<div class="drink-line"';

if ($drink->containsAlcohol()) {$render .= ' data-alcohol="1"';

}

$render.= '>'. $drink->getPrice() .'</div>';}return $render;

}}

Page 77: Why your code suxx

class HtmlBillPrinterimplements BillPrinterInterface{

public function render(Client $client){foreach($client->getDrinks() as $drink){

$render .= '<div class="drink-line"';

if ($drink->containsAlcohol()) {$render .= ' data-alcohol="1"';

}

$render.= '>'. $drink->getPrice() .'</div>';}return $render;

}}

It renders lines

Page 78: Why your code suxx

class HtmlBillPrinterimplements BillPrinterInterface{

public function render(Client $client){foreach($client->getDrinks() as $drink){

$render .= '<div class="drink-line"';

if ($drink->containsAlcohol()) {$render .= ' data-alcohol="1"';

}

$render.= '>'. $drink->getPrice() .'</div>';}return $render;

}}

It manages HTML and its attributes

It renders lines

Page 79: Why your code suxx

class HtmlBillPrinterimplements BillPrinterInterface{

public function render(Client $client){foreach($client->getDrinks() as $drink){

$render .= '<div class="drink-line"';

if ($drink->containsAlcohol()) {$render .= ' data-alcohol="1"';

}

$render.= '>'. $drink->getPrice() .'</div>';}return $render;

}}

It manages HTML and its attributes

And who’s gonna escape string if necessary?

It renders lines

Page 80: Why your code suxx

class HtmlBillPrinterimplements BillPrinterInterface{

public function render(Client $client){foreach($client->getDrinks() as $drink){

$render .= '<div class="drink-line"';

if ($drink->containsAlcohol()) {$render .= ' data-alcohol="1"';

}

$render.= '>'. $drink->getPrice() .'</div>';}return $render;

}}

It manages HTML and its attributes

And who’s gonna escape string if necessary?

It renders lines

It’s bibi!

Page 81: Why your code suxx

class HtmlBillPrinterimplements BillPrinterInterface{

public function render(Client $client){foreach($client->getDrinks() as $drink){

return '<div class="drink-line"';$div = new HtmlTag('div');$div->addAttribute('class', 'drink-line');

if ($drink->containsAlcohol()) {$render .= ' data-alcohol="1"';

}

$render.= '>'. $drink->getPrice() .'</div>';}return $render;

}}

Page 82: Why your code suxx

class HtmlBillPrinterimplements BillPrinterInterface{

public function render(Client $client){foreach($client->getDrinks() as $drink){

$div = new HtmlTag('div');$div->addAttribute('class', 'drink-line');

if ($drink->containsAlcohol()) {$render .= ' data-alcohol="1"';

}

$render.= '>'. $drink->getPrice() .'</div>';}return $render;

}}

Page 83: Why your code suxx

class HtmlBillPrinterimplements BillPrinterInterface{

public function render(Client $client){foreach($client->getDrinks() as $drink){

$div = new HtmlTag('div');$div->addAttribute('class', 'drink-line');

if ($drink->containsAlcohol()) {return ' data-alcohol="1"'; $div->addAttribute('data-alcohol', '1');

}$render.= '>'. $drink->getPrice() .'</div>';

}return $render;

}}

Page 84: Why your code suxx

class HtmlBillPrinterimplements BillPrinterInterface{

public function render(Client $client){foreach($client->getDrinks() as $drink){

$div = new HtmlTag('div');$div->addAttribute('class', 'drink-line');

if ($drink->containsAlcohol()) {$div->addAttribute('data-alcohol', '1');

}$render.= '>'. $drink->getPrice() .'</div>';

}return $render;

}}

Page 85: Why your code suxx

class HtmlBillPrinterimplements BillPrinterInterface{

public function render(Client $client){foreach($client->getDrinks() as $drink){

$div = new HtmlTag('div');$div->addAttribute('class', 'drink-line');

if ($drink->containsAlcohol()) {$div->addAttribute('data-alcohol', '1');

}$render.= '>'. $drink->getPrice() .'</div>';$div->setHtml($drink->getPrice());return $div;

}}

}

Page 86: Why your code suxx

class HtmlBillPrinterimplements BillPrinterInterface{

public function render(Client $client){foreach($client->getDrinks() as $drink){

$div = new HtmlTag('div');$div->addAttribute('class', 'drink-line');

if ($drink->containsAlcohol()) {$div->addAttribute('data-alcohol', '1');

}$div->setHtml($drink->getPrice());return $div;

}}

}

Page 87: Why your code suxx

It’s all about making

Your code comprehensible

WRAPPING UP

Page 88: Why your code suxx

Books

Page 89: Why your code suxx

Thank you ☺Questions?

Page 90: Why your code suxx

[email protected]

+32 2 880 05 41