+ All Categories
Home > Software > So S.O.L.I.D Fu - Designing Better Code

So S.O.L.I.D Fu - Designing Better Code

Date post: 15-Apr-2017
Category:
Upload: neil-crookes
View: 105 times
Download: 6 times
Share this document with a friend
31
So S.O.L.I.D Fu Designing Better Code
Transcript

So S.O.L.I.D FuDesigning Better Code

Abstract

A chat about some of the most important principles in software development. Discover or get a refresher on these tried and tested techniques for designing better code.

SaaS eCommerce Platform

What’s SOLID?

5 Principles

● Single Responsibility Principle

● Open / Closed Principle

● Liskov Substitution Principle

● Interface Segregation Principle

● Dependency Inversion Principle

SRP

OCP

LSPISP

DIPS.O.L.I.D

Why S.O.L.I.D?

● Maintainable

● Understandable

● Extendable

● Testable

● Debuggable

● Reusable

● Robust, less fragile

BETTERSOFTWARE

SRP

OCP

LSPISP

DIP

class Product{ public function getId() public function getName()

public function getPrice()

public function getStock()

public function setStock($stock)

public function getDescription()}

https://github.com/neilcrookes/SoSOLIDFu

class CreditCard{ public function getExpiryYear()

public function getExpiryMonth()

public function getLast4()

public function getToken()}

https://github.com/neilcrookes/SoSOLIDFu

class Gateway{ public function purchase(Shop $shop, CreditCard $card)}

https://github.com/neilcrookes/SoSOLIDFu

class Shop{ public function addToBasket(Product $product)

public function removeFromBasket(Product $product)

public function getTotal()

public function getBasket()

public function checkout(CreditCard $card)

protected function sendOrderConfirmationEmail(CreditCard $card)}

https://github.com/neilcrookes/SoSOLIDFu

Single Responsibility

States that

● Classes should have only one reason to change

● Therefore should have only one responsibility

class Shop{ public function addToBasket(Product $product)

public function removeFromBasket(Product $product)

public function getTotal()

public function getBasket()

public function checkout(CreditCard $card)

protected function sendOrderConfirmationEmail(CreditCard $card)}

https://github.com/neilcrookes/SoSOLIDFu

class Shop{ public function addToBasket(Product $product)

public function removeFromBasket(Product $product)

public function getTotal()

public function getBasket()

public function checkout(CreditCard $card)

protected function sendOrderConfirmationEmail(CreditCard $card)}

https://github.com/neilcrookes/SoSOLIDFu

class Basket{ public function addToBasket(Product $product)

public function removeFromBasket(Product $product)

public function getTotal()

public function getBasket()}

Class Checkout{ public function checkout(CreditCard $card)

protected function sendOrderConfirmationEmail(CreditCard $card)}

https://github.com/neilcrookes/SoSOLIDFu

Class Checkout{ public function checkout(Basket $basket, CreditCard $card) { try { $reference = $this->gateway->purchase($basket, $card);

$this->sendOrderConfirmationEmail($basket, $card);

return true; } catch (GatewayException $e) { return false; } }}

https://github.com/neilcrookes/SoSOLIDFu

Open / Closed

Objects or libraries should be open for extension, but closed for modification.

● Easy to add new features OR change behaviour

● Without modifying original

● Purists: without extending original…!?

Eh?

Open / Closed cont

Extension points

● Events● Plugin architecture● Composition● Strategy pattern● Callbacks

Open / Closed cont

Closed for modification - Clarity of intent

● Final classes● Private members● Encourage extension in expected / supported way● Encourages decoupling● Reduces risk of breaking changes

final class Checkout{ public function checkout(Basket $basket, CreditCard $card) { try { $reference = $this->gateway->purchase($basket, $card);

$this->sendOrderConfirmationEmail($basket, $card);

fire(new CheckoutEvent($basket));

return true; } catch (GatewayException $e) { return false; } }}

https://github.com/neilcrookes/SoSOLIDFu

final class Checkout{ public function sendOrderConfirmationEmail(Basket $basket, CreditCard $card) { $message = "Thank you for your order.\n";

foreach ($basket as $item) { /** @var Product $product */ $product = $item['product'];

$message .= "\n" . $product->getName() . ' x ' . $item['quantity'] . ' @ £' . $product->getPrice();

} $message .= "\n\nPayment has been taken from: xxxx xxxx xxxx " . $card->getLast4()

. ' Ex: ' . $card->getExpiryMonth() . '/' . $card->getExpiryYear();

mail('[email protected]', 'Your order at my store', $message); }}

https://github.com/neilcrookes/SoSOLIDFu

Liskov Substitution

States that you should be able to swap dependencies for a subclass class without

causing unexpected results

final class Checkout{ public function sendOrderConfirmationEmail(Basket $basket, CreditCard $card) { $message = "Thank you for your order.\n";

foreach ($basket as $item) { /** @var Product $product */ $product = $item['product'];

$message .= "\n" . $product->getBasketTitle() . ' x ' . $item['quantity'] . ' @ £' . $product->getPrice();

} $message .= "\n\nPayment has been taken from: xxxx xxxx xxxx " . $card->getLast4()

. ' Ex: ' . $card->getExpiryMonth() . '/' . $card->getExpiryYear();

mail('[email protected]', 'Your order at my store', $message); }}

https://github.com/neilcrookes/SoSOLIDFu

interface PurchasableInterface{ public function getId();

public function getPrice();

public function getStock();

public function setStock($stock);

public function getBasketTitle();}

https://github.com/neilcrookes/SoSOLIDFu

Interface Segregation

● An interface should only impose the methods that a client relies on

● Split unrelated interface methods into separate interfaces

interface PurchasableInterface{ public function getId();

public function getPrice();

public function getBasketTitle();}

interface StockableInterface{ public function getStock();

public function setStock($stock);}

https://github.com/neilcrookes/SoSOLIDFu

final class Checkout{ public function checkout(Basket $basket, CreditCard $card)}

class Gateway{ public function purchase(Basket $basket, CreditCard $card)}

https://github.com/neilcrookes/SoSOLIDFu

Dependency Inversion

● High level should not depend on low level modules, both should depend on abstractions

● Abstractions should not depend upon details. Details should depend upon abstractions.

● Depend on abstractions (abstracts / interfaces), not concretes● Code to an Interface

interface ChargeableInterface{ public function getChargeableAmount();}

interface GatewayInterface{ public function charge(ChargeableInterface $chargeable,

PaymentMethodInterface $paymentMethod);}

interface PaymentMethodInterface{ public function getToken();

public function getDetails();}

https://github.com/neilcrookes/SoSOLIDFu

final class Checkout{ public function checkout(ChargeableInterface $basket, PaymentMethodInterface $card)}

class Gateway{ public function purchase(ChargeableInterface $basket, PaymentMethodInterface $card)}

https://github.com/neilcrookes/SoSOLIDFu

Conclusion:Get Classy

● Actually only add some Interfaces, and an Event● Our code is designed much better● Easy to understand, extend● More robust, less likely to introduce bugs● Lots of classes is fine● Lots of interfaces is fine

Questions

● Now?● At the pub?● @neilcrookes


Recommended