Slide 1

Slide 1 text

PRESENTED BY JEFF CAROUTH @jcarouth Fundamental Object Oriented php

Slide 2

Slide 2 text

WiFi Sheraton-MeetingRooms joind.in joind.in/event/phptek2015 Open Social Atrium/Bar at 6:00pm Newcomers ServerGrove Room at 5:30pm Twitter #phptek

Slide 3

Slide 3 text

PRESENTED BY JEFF CAROUTH @jcarouth Fundamental Object Oriented php

Slide 4

Slide 4 text

About this tutorial

Slide 5

Slide 5 text

Agenda 1. OOP and OOD 2. Dependencies and coupling 3. Creating interfaces 4. Sharing Behavior through Inheritance 5. Sharing Behavior through Composition 6. The SOLID Principles

Slide 6

Slide 6 text

Why should we talk about object-oriented programming?

Slide 7

Slide 7 text

We live our lives in procedural fashion. That’s what makes procedural programming seem so natural.

Slide 8

Slide 8 text

In a procedural language, you have access to some data types, you can create variables, and you can define procedures to act upon or in response to those variables.

Slide 9

Slide 9 text

Procedural programming is not inherently bad. It’s also not the exact opposite of OOP.

Slide 10

Slide 10 text

The problems with procedural code come in when code is poorly structured.

Slide 11

Slide 11 text

Thinking of solutions in object- oriented ways leads to improved structure.

Slide 12

Slide 12 text

Example: shopping cart in e- commerce application.

Slide 13

Slide 13 text

// Procedural Shopping Cart $product = array( 'id' => 5634, 'name' => 'Widget', 'price' => 12.99 );

Slide 14

Slide 14 text

// Procedural Shopping Cart $product = array( 'id' => 5634, 'name' => 'Widget', 'price' => 12.99 ); $customer = array( 'id' => 8934512, 'email' => '[email protected]' );

Slide 15

Slide 15 text

// Procedural Shopping Cart $product = array( 'id' => 5634, 'name' => 'Widget', 'price' => 12.99 ); $customer = array( 'id' => 8934512, 'email' => '[email protected]' ); $cart = array();

Slide 16

Slide 16 text

// Procedural Shopping Cart $product = array( 'id' => 5634, 'name' => 'Widget', 'price' => 12.99 ); $customer = array( 'id' => 8934512, 'email' => '[email protected]' ); $cart = array(); function add_item_to_cart($cart, $item) { if (!isset($cart['items'])) { $cart['items'] = array(); } $cart['items'][] = $item; return $cart; }

Slide 17

Slide 17 text

function add_item_to_cart($cart, $item) { if (!isset($cart['items'])) { $cart['items'] = array(); } $cart['items'][] = $item; return $cart; }

Slide 18

Slide 18 text

function complete_purchase_of_cart($cart, $customer) { $order = array( 'line_items' => array(), 'customer_id' => $customer['id'], 'total' => 0.00, ); foreach ($cart['items'] as $item) { $order['line_items'][] = $item; $order['total'] += $item['price']; } mail( $customer['email'], 'Order of ' . count($order['line_items']) . ' items complete.’ ); return $order; }

Slide 19

Slide 19 text

// Procedural Shopping Cart $product = array( 'id' => 5634, 'name' => 'Widget', 'price' => 12.99 ); $customer = array( 'id' => 8934512, 'email' => '[email protected]' ); $cart = array(); function add_item_to_cart($cart, $item) { /* ... */ } function complete_purchase_of_cart($cart, $customer) { /* ... */ }

Slide 20

Slide 20 text

// Procedural Shopping Cart $product = array( 'id' => 5634, 'name' => 'Widget', 'price' => 12.99 ); $customer = array( 'id' => 8934512, 'email' => '[email protected]' ); $cart = array(); function add_item_to_cart($cart, $item) { /* ... */ } function complete_purchase_of_cart($cart, $customer) { /* ... */ } $cart = add_item_to_cart($cart, $item); $order = complete_purchase_of_cart($cart, $customer);

Slide 21

Slide 21 text

Can we improve this with objects?

Slide 22

Slide 22 text

// Object-Oriented Shopping Cart class Product { private $id; private $name; private $price; public function __construct($id, $name, $price) { $this->id = $id; $this->name = $name; $this->price = $price; } }

Slide 23

Slide 23 text

// Object-Oriented Shopping Cart class Cart { private $items; public function __construct() { $items = array(); } public function addItem($item) { $this->items[] = $item; } public function sumItemPrices() { foreach ($this->items as $item) { $sum += $item->getPrice(); } } }

Slide 24

Slide 24 text

// Object-Oriented Shopping Cart class Customer { private $id; private $email; public function __construct($id, $email) { $this->id = $id; $this->email = $email; } }

Slide 25

Slide 25 text

// Object-Oriented Shopping Cart class Order { private $items; private $customer; public function __construct($cart, $customer) { $this->items = $cart; $this->customer = $customer; } public function getTotal() { return $this->items->sumItemPrices(); } }

Slide 26

Slide 26 text

// Object-Oriented Shopping Cart class OrderProcessor { public function completeOrder($order) { mail( $customer['email'], 'Order of ' . $order->getNumberItems() . ' items complete.’ ); } }

Slide 27

Slide 27 text

// Object-Oriented Shopping Cart class Order { private $items; private $customer; public function __construct($cart, $customer) { $this->items = $cart; $this->customer = $customer; } public function getTotal() { return $this->items->sumItemPrices(); } }

Slide 28

Slide 28 text

// Object-Oriented Shopping Cart class Cart implements Countable { private $items; public function __construct() { $items = array(); } public function addItem($item) { /* ... */ } public function sumItemPrices() { /* ... */ } public function count() { return count($this->items); } }

Slide 29

Slide 29 text

// Object-Oriented Shopping Cart class Order { private $items; private $customer; public function __construct($cart, $customer) { $this->items = $cart; $this->customer = $customer; } public function getTotal() { /* ... */ } public function getNumberItems() { return count($this->items); } }

Slide 30

Slide 30 text

// Object-Oriented Shopping Cart class OrderProcessor { public function completeOrder($order) { mail( $customer['email'], 'Order of ' . $order->getNumberItems() . ' items complete.’ ); } }

Slide 31

Slide 31 text

// Object-Oriented Shopping Cart $product = new Product(5634, 'Widget', 12.99) $customer = new Customer(8934512, '[email protected]');

Slide 32

Slide 32 text

// Object-Oriented Shopping Cart $product = new Product(5634, 'Widget', 12.99) $customer = new Customer(8934512, '[email protected]'); $cart = new Cart(); $cart->addItem($product);

Slide 33

Slide 33 text

// Object-Oriented Shopping Cart $product = new Product(5634, 'Widget', 12.99) $customer = new Customer(8934512, '[email protected]'); $cart = new Cart(); $cart->addItem($product); $order = new Order($cart, $customer);

Slide 34

Slide 34 text

// Object-Oriented Shopping Cart $product = new Product(5634, 'Widget', 12.99) $customer = new Customer(8934512, '[email protected]'); $cart = new Cart(); $cart->addItem($product); $order = new Order($cart, $customer); $orderProcessor = new OrderProcessor(); $orderProcessor->completeOrder($order);

Slide 35

Slide 35 text

What does this buy us? Encapsulation of data.

Slide 36

Slide 36 text

Encapsulation can be summarized as information hiding.

Slide 37

Slide 37 text

Example: a bank account.

Slide 38

Slide 38 text

// Representing a bank account class Account { private $balance; public function __construct($startingBalance = 0.00) { $this->balance = $startingBalance; } public function getBalance() { return $this->balance; } public function setBalance($newBalance) { $this->balance = $newBalance; } }

Slide 39

Slide 39 text

$account = new Account(); $currentBalance = $account->getBalance(); $account->setBalance($currentBalance + 3507.45); var_dump($account);

Slide 40

Slide 40 text

$account = new Account(); $currentBalance = $account->getBalance(); $account->setBalance($currentBalance + 3507.45); var_dump($account); /** output **/ object(Account)#1 (1) { ["balance":"Account":private]=> float(3507.45) }

Slide 41

Slide 41 text

function deposit($account, $amount) { $newBalance = $account->getBalance() + $amount; $account->setBalance($newBalance); } $account = new Account(3507.45); deposit($account, 492.55); var_dump($account);

Slide 42

Slide 42 text

function deposit($account, $amount) { $newBalance = $account->getBalance() + $amount; $account->setBalance($newBalance); } $account = new Account(3507.45); deposit($account, 492.55); var_dump($account); /** output **/ object(Account)#1 (1) { ["balance":"Account":private]=> float(4000) }

Slide 43

Slide 43 text

function withdraw($account, $amount) { $newBalance = $account->getBalance() - $amount; $account->setBalance($newBalance); } $account = new Account(4000.00); withdraw($account, 4001.99); var_dump($account);

Slide 44

Slide 44 text

function withdraw($account, $amount) { $newBalance = $account->getBalance() - $amount; $account->setBalance($newBalance); } $account = new Account(4000.00); withdraw($account, 4001.99); var_dump($account); /** output **/ object(Account)#1 (1) { ["balance":"Account":private]=> float(-1.9899999999998) }

Slide 45

Slide 45 text

function better_withdraw($account, $amount) { if ($amount > $account->getBalance()) { throw new Exception( 'Cannot withdrawal '.$amount.' from account with only ‘ .$account->getBalance() ); } $account->setBalance($account->getBalance() - $amount); } $account = new Account(4000.00); better_withdraw($account, 4001.00);

Slide 46

Slide 46 text

function better_withdraw($account, $amount) { if ($amount > $account->getBalance()) { throw new Exception( 'Cannot withdrawal '.$amount.' from account with only ‘ .$account->getBalance() ); } $account->setBalance($account->getBalance() - $amount); } $account = new Account(4000.00); better_withdraw($account, 4001.00); /** output **/ Fatal error: Uncaught exception 'Exception' with message 'Cannot withdrawal 4001 from account with only 4000'

Slide 47

Slide 47 text

// Representing a bank account class Account { private $balance; public function __construct($startingBalance = 0.00) { $this->balance = $startingBalance; } public function getBalance() { return $this->balance; } public function setBalance($newBalance) { $this->balance = $newBalance; } }

Slide 48

Slide 48 text

// Representing a bank account class Account { /* …snip… */ public function deposit($amount) { $this->balance += $amount; } public function withdraw($amount) { if ($amount > $this->balance) { throw new Exception( 'Cannot withdrawal ‘.$amount .’ from account with only ‘.$this->balance ); } $this->balance -= $amount; } }

Slide 49

Slide 49 text

$account = new Account(5.65); $account->deposit(4.35); $account->withdraw(11.00); $account->withdraw(5.00); $account->withdraw(5.00);

Slide 50

Slide 50 text

Exercise: Develop a basic set of banking objects to deal with accounts and balances.

Slide 51

Slide 51 text

Dealing with Dependencies and Coupling

Slide 52

Slide 52 text

Object-oriented programming is about managing dependencies.

Slide 53

Slide 53 text

Dependencies are the other objects, resources, or functions any given object uses to accomplish its responsibility.

Slide 54

Slide 54 text

class Bank { public function __construct() { $this->accountRepository = new AccountRepository(); } public function openAccount($startingBalance = 0.00) { $accountNumber = $this->accountRepository->generateAccountNumber(); $account = new Account($accountNumber, $startingBalance); $this->accountRepository->add($account); return $account; } }

Slide 55

Slide 55 text

class AccountRepository { private $accounts; public function __construct($accounts = array()) { $this->accounts = $accounts; } public function add($account) { $this->accounts[$account->getAccountNumber()] = $account; } public function generateAccountNumber() { do { $accountNumber = rand(1000, 5000); } while(array_key_exists($accountNumber, $this->accounts)); return $accountNumber; } }

Slide 56

Slide 56 text

class Account { private $accountNumber; private $balance; public function __construct($accountNumber, $balance = 0.00) { $this->accountNumber = $accountNumber; $this->balance = $balance; } public function getAccountNumber() { return $this->accountNumber; } }

Slide 57

Slide 57 text

$bank = new Bank(); $account = $bank->openAccount(50.00); var_dump($account);

Slide 58

Slide 58 text

$bank = new Bank(); $account = $bank->openAccount(50.00); var_dump($account); /** output **/ object(Account)#3 (2) { ["accountNumber":"Account":private]=> int(4481) ["balance":"Account":private]=> float(50) }

Slide 59

Slide 59 text

$bank = new Bank(); $account = $bank->openAccount(50.00); var_dump($account); /** output **/ object(Account)#3 (2) { ["accountNumber":"Account":private]=> int(4481) ["balance":"Account":private]=> float(50) } $anotherAccount = $bank->openAccount(1435.56); var_dump($anotherAccount);

Slide 60

Slide 60 text

$bank = new Bank(); $account = $bank->openAccount(50.00); var_dump($account); /** output **/ object(Account)#3 (2) { ["accountNumber":"Account":private]=> int(4481) ["balance":"Account":private]=> float(50) } $anotherAccount = $bank->openAccount(1435.56); var_dump($anotherAccount); /** output **/ object(Account)#4 (2) { ["accountNumber":"Account":private]=> int(2504) ["balance":"Account":private]=> float(1435.56) }

Slide 61

Slide 61 text

class Bank { public function __construct() { $this->accountRepository = new AccountRepository(); } public function openAccount($startingBalance = 0.00) { $accountNumber = $this->accountRepository->generateAccountNumber(); $account = new Account($accountNumber, $startingBalance); $this->accountRepository->add($account); return $account; } }

Slide 62

Slide 62 text

Inject dependencies where they are needed.

Slide 63

Slide 63 text

class Bank { public function __construct(AccountRepository $accountRepository) { $this->accountRepository = $accountRepository; } public function openAccount($startingBalance = 0.00) { $accountNumber = $this->accountRepository->generateAccountNumber(); $account = new Account($accountNumber, $startingBalance); $this->accountRepository->add($account); return $account; } }

Slide 64

Slide 64 text

Don’t depend on concrete classes. Depend on abstractions.

Slide 65

Slide 65 text

class AccountRepository { private $accounts; public function __construct($accounts = array()) { $this->accounts = $accounts; } public function add($account) { $this->accounts[$account->getAccountNumber()] = $account; } public function generateAccountNumber() { do { $accountNumber = rand(1000, 5000); } while(array_key_exists($accountNumber, $this->accounts)); return $accountNumber; } }

Slide 66

Slide 66 text

class InMemoryAccountRepository implements AccountRepository { private $accounts; public function __construct($accounts = array()) { $this->accounts = $accounts; } public function add($account) { $this->accounts[$account->getAccountNumber()] = $account; } public function generateAccountNumber() { do { $accountNumber = rand(1000, 5000); } while(array_key_exists($accountNumber, $this->accounts)); return $accountNumber; } }

Slide 67

Slide 67 text

interface AccountRepository { public function add($account); public function generateAccountNumber(); }

Slide 68

Slide 68 text

$bank = new Bank(new InMemoryAccountRepository()); $account = $bank->openAccount(50.00); var_dump($account); object(Account)#3 (2) { ["accountNumber":"Account":private]=> int(3305) ["balance":"Account":private]=> float(50) } $anotherAccount = $bank->openAccount(1435.56); var_dump($anotherAccount); object(Account)#4 (2) { ["accountNumber":"Account":private]=> int(2581) ["balance":"Account":private]=> float(1435.56) }

Slide 69

Slide 69 text

Exercise: Improve your banking objects. Include a repository for storing and retrieving existing accounts.

Slide 70

Slide 70 text

Creating Interfaces

Slide 71

Slide 71 text

Simply put an interface is the collection of methods an object exposes to be interacted with.

Slide 72

Slide 72 text

Looking at the interface of the Account.

Slide 73

Slide 73 text

// Representing a bank account class Account { public function __construct($startingBalance = 0.00) { /* …snip… */ } public function getBalance() { /* …snip… */ } public function deposit($amount) { /* …snip… */ } public function withdraw($amount) { /* …snip… */ } }

Slide 74

Slide 74 text

// Account Transactions Ledger class Account { private $balance; private $transactions; public function __construct($balance = 0.00) { $this->balance = $balance; $this->transactions = array(); } /* snip */ }

Slide 75

Slide 75 text

// Account Transactions Ledger class Account { /* snip */ public function deposit($amount) { $this->transactions[] = 'Deposited '.$amount; $this->balance += $amount; } public function withdraw($amount) { if ($amount > $this->balance) { $this->transactions[] = 'Failed to withdraw '.$amount.' when balance '. $this->balance; throw new Exception('Cannot withdraw '.$amount.' from balance '.$this- >balance); } $this->transactions[] = 'Withdrew '.$amount; $this->balance -= $amount; } }

Slide 76

Slide 76 text

// Account Transactions Ledger class Account { /* snip */ public function getTransactions() { return $this->transactions; } public function getBalance() { return $this->balance; } }

Slide 77

Slide 77 text

// Account Transactions Ledger class Account { public function __construct($balance = 0.00) { /* snip */ } public function deposit($amount) { /* snip */ } public function withdraw($amount) { /* snip */ } public function getTransactions() { /* snip */ } public function getBalance() { /* snip */ } }

Slide 78

Slide 78 text

// Account Transactions Ledger $account = new Account(10.00); $account->withdraw(2.86); $account->deposit(758.34); $account->withdraw(700.00); try { $account->withdraw(66.00); } catch (Exception $e) { //do nothing because I'm bad and I should feel bad } $account->withdraw(50.00); var_dump($account->getTransactions());

Slide 79

Slide 79 text

// Account Transactions Ledger var_dump($account->getTransactions()); /** output **/ array(5) { [0]=> string(13) "Withdrew 2.86" [1]=> string(16) "Deposited 758.34" [2]=> string(12) "Withdrew 700" [3]=> string(40) "Failed to withdraw 66 when balance 65.48" [4]=> string(11) "Withdrew 50" }

Slide 80

Slide 80 text

An Interface is a mechanism available in PHP to indicate that an object which implements the interface abides by the contract it specifies.

Slide 81

Slide 81 text

The most important job of an interface in object-oriented design is to specify the role or roles an object fulfills.

Slide 82

Slide 82 text

// Account Transactions Ledger interface AcceptsDeposits { public function deposit($amount); } class Account implements AcceptsDeposits { public function deposit($amount) { $this->transactions[] = 'Deposited '.$amount; $this->balance += $amount; } }

Slide 83

Slide 83 text

BREAK TIME

Slide 84

Slide 84 text

Sharing Behavior through Inheritance

Slide 85

Slide 85 text

An object which obtains behaviors through its parent object is said to have inherited behavior.

Slide 86

Slide 86 text

Looking at our Account object, deposits and withdrawals both appear to be very similar in nature.

Slide 87

Slide 87 text

// Account with Transactions class Account { private $balance; private $transactions; public function __construct($balance = 0.00) { $this->balance = $balance; $this->transactions = array(); } public function postTransaction(Transaction $transaction) { try { $newBalance = $transaction->applyTo($this->balance); $this->transactions[] = $transaction; $this->balance = $newBalance; } catch (Exception $e) { // apply fee } } }

Slide 88

Slide 88 text

abstract class Transaction { private $amount; public function __construct($amount) { $this->amount = $amount; } public function applyTo($balance) { $amountToApply = $this->amount; if ($this->isDebit()) { if ($amountToApply > $balance) { throw new Exception('Whoops!'); } return $balance - $amountToApply; } else { return $balance + $amountToApply; } } abstract protected function isDebit(); }

Slide 89

Slide 89 text

class Withdrawal extends Transaction { protected function isDebit() { return true; } } class Deposit extends Transaction { protected function isDebit() { return false; } }

Slide 90

Slide 90 text

$account = new Account(30.00); $account->postTransaction(new Withdrawal(5.00)); $account->postTransaction(new Deposit(75.00)); $account->postTransaction(new Withdrawal(99.99)); var_dump($account);

Slide 91

Slide 91 text

/** output **/ object(Account)#1 (2) { ["balance":"Account":private]=> float(0.010000000000005) ["transactions":"Account":private]=> array(3) { [0]=> object(Withdrawal)#2 (1) { ["amount":"Transaction":private]=> float(5) } [1]=> object(Deposit)#3 (1) { ["amount":"Transaction":private]=> float(75) } [2]=> object(Withdrawal)#4 (1) { ["amount":"Transaction":private]=> float(99.99) } } }

Slide 92

Slide 92 text

Sharing behavior through composition

Slide 93

Slide 93 text

When objects are combined by holding a reference to another object to gain functionality, this is composition.

Slide 94

Slide 94 text

// Object-Oriented Shopping Cart class Cart { private $items; public function __construct() { $items = array(); } public function addItem($item) { $this->items[] = $item; } public function sumItemPrices() { foreach ($this->items as $item) { $sum += $item->getPrice(); } } }

Slide 95

Slide 95 text

// Object-Oriented Shopping Cart class Order { private $items; private $customer; public function __construct($cart, $customer) { $this->items = $cart; $this->customer = $customer; } public function getTotal() { return $this->items->sumItemPrices(); } }

Slide 96

Slide 96 text

the solid principles

Slide 97

Slide 97 text

Single Responsibility Principle Open-closed Principle Liskov Substitution Principle Interface Substitution Principle Dependency Inversion Principle

Slide 98

Slide 98 text

Single Responsibility Principle A class should be responsible for doing one thing. It should only have one reason to change.

Slide 99

Slide 99 text

class AccessControlManager { public function __construct(Customer $customer) { $this->customer = $customer; } public function login() { if ($this->customer->authenticate() && $this->customer->isAuthorized()) { return true; } return false; } }

Slide 100

Slide 100 text

class Customer { public function getId() {} public function authenticate() { // check database for user } public function isAuthorized() { // check authorization against resource } }

Slide 101

Slide 101 text

Customer will change if authorization changes or if authentication changes or if the customer changes.

Slide 102

Slide 102 text

class Customer { public function getId(); } class Login { public function authenticate(Customer $customer) { // check customer } } class Authorize { public function isAuthorized(Customer $customer) { //validate authorization } }

Slide 103

Slide 103 text

class AccessControlManager { public function __construct(Customer $customer, Login $login, Authorize $authorize) { //assign } public function login() { if ($this->login->authenticate($this->customer) && $this->authorize->isAuthorized($this->customer)) { return true; } return false; } }

Slide 104

Slide 104 text

This sounds easy to do. But finding and separating responsibilities is one of the hardest parts of programming.

Slide 105

Slide 105 text

Open-closed Principle A software entity should be open for extension but closed for modification.

Slide 106

Slide 106 text

class AccessControlManager { public function __construct(Customer $customer, Login $login, Authorize $authorize) { //assign } public function login() { if ($this->login->authenticate($this->customer) && $this->authorize->isAuthorized($this->customer)) { return true; } return false; } }

Slide 107

Slide 107 text

class Login { public function authenticate(Customer $customer) { // check customer } protected function getRepository() {} }

Slide 108

Slide 108 text

class Login { public function authenticate(Customer $customer) { // check customer } protected function getRepository() {} } class LoginOauth extends Login { public function authenticate(Customer $customer) { $token = $this->getAccessToken(); // oauth login } protected function getAccessToken() {} }

Slide 109

Slide 109 text

interface LoginService { public function authenticate(Customer $customer); protected function getRepository(); protected function getAccessToken(); }

Slide 110

Slide 110 text

class LoginDatabase implements LoginService { public function authenticate(Customer $customer) { // check customer } protected function getRepository() {} } class LoginOauth implements LoginService { public function authenticate(Customer $customer) { $token = $this->getAccessToken(); // oauth login } protected function getAccessToken() {} }

Slide 111

Slide 111 text

class AccessControlManager { public function __construct(Customer $customer, LoginService $login, Authorize $authoize) { //assign } public function login() { if ($this->login->authenticate($this->customer) && $this->authorize->isAuthorized($this->customer)) { return true; } return false; } }

Slide 112

Slide 112 text

The takeaway is that your code should not need to be modified to adapt it to new situations.

Slide 113

Slide 113 text

Liskov Substitution Principle Objects within an application should be able to be replaced with their subtypes without affecting the correctness of the application.

Slide 114

Slide 114 text

class PaymentManager { public function __construct(PayDateCalculator $calculator) {} public function schedulePayment(Payment $payment) { $payment->setPayDate($this->paydateCalculator->calculate()); //send to db } }

Slide 115

Slide 115 text

class PaymentManager { public function __construct(PayDateCalculator $calculator) {} public function schedulePayment(Payment $payment) { $payment->setPayDate($this->paydateCalculator->calculate()); //send to db } } class PayDateCalculator { public function calculate() { $today = new DateTime(); $firstDayOfNextMonth = $today->modify('first day of next month'); return $firstDayOfNextMonth; } }

Slide 116

Slide 116 text

class PayDateCalculator { public function calculate() { $today = new DateTime(); $firstDayOfNextMonth = $today->modify('first day of next month'); return $firstDayOfNextMonth; } } class LastDayPayDateCalculator extends PayDateCalculator { public function calculate() { $today = new DateTime(); $lastDayOfMonth = $today->modify('last day of this month'); return $lastDayOfMonth->format( 'F jS, Y' ); } }

Slide 117

Slide 117 text

You can’t return a DateTime from one PayDateCalculator and a String from another. That is not good.

Slide 118

Slide 118 text

abstract class PayDateCalculator { public function calculate() { $today = new DateTime(); $payDate = $this->resolvePayDate($today); return $payDate; } abstract protected function resolvePayDate($today); } class LastDayPayDateCalculator extends PayDateCalculator { protected function resolvePayDate($today) { return $today->modify('last day of this month'); } } class FirstDayPayDateCalculator extends PayDateCalculator { protected function resolvePayDate($today) { return $today->modify('first day of next month'); }

Slide 119

Slide 119 text

abstract class PayDateCalculator { public function calculate() { $today = new DateTime(); $payDate = $this->resolvePayDate($today); return $payDate; } abstract protected function resolvePayDate($today); }

Slide 120

Slide 120 text

class LastDayPayDateCalculator extends PayDateCalculator { protected function resolvePayDate($today) { return $today->modify('last day of this month'); } } class FirstDayPayDateCalculator extends PayDateCalculator { protected function resolvePayDate($today) { return $today->modify('first day of next month'); } }

Slide 121

Slide 121 text

class PaymentManager { public function __construct(PayDateCalculator $calculator) {} public function schedulePayment(Payment $payment) { $payment->setPayDate($this->paydateCalculator->calculate()); //send to db } }

Slide 122

Slide 122 text

Make sure any objects which claim to implement a certain interface actually implement that interface.

Slide 123

Slide 123 text

Interface Segregation Principle No client should be forced to depend on methods it doesn’t use.

Slide 124

Slide 124 text

class Cart implements Countable { private $items; public function __construct() {} public function addItem($item) {} public function removeItem($item) {} public function emptyCart() {} public function sumItemPrices() {} public function count() {} }

Slide 125

Slide 125 text

interface ShoppingCart { public function addItem($item); public function removeItem($item); public function emptyCart(); public function sumItemPrices(); public function count(); } class Cart implements ShoppingCart {}

Slide 126

Slide 126 text

interface ShoppingCart { public function addItem($item); public function removeItem($item); public function emptyCart(); public function sumItemPrices(); public function count(); } interface Repository { public function addItem($item); public function removeItem($item); public function emptyCart(); }

Slide 127

Slide 127 text

interface ShoppingCart { public function addItem($item); public function removeItem($item); public function emptyCart(); public function sumItemPrices(); public function count(); } interface Summable { public function sum(); }

Slide 128

Slide 128 text

interface Repository { public function addItem($item); public function removeItem($item); public function emptyCart(); } interface Summable { public function sum(); } class Cart implements Countable, Summable, Repository {}

Slide 129

Slide 129 text

Clients of your objects should not have to depend on extraneous methods. Keep your interfaces segregated.

Slide 130

Slide 130 text

Dependency Inversion Principle High level modules should not depend on low level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.

Slide 131

Slide 131 text

class AccessControlManager { public function __construct(Customer $customer, LoginService $login, Authorize $authoize) { //assign } public function login() { if ($this->login->authenticate($this->customer) && $this->authorize->isAuthorized($this->customer)) { return true; } return false; } }

Slide 132

Slide 132 text

AccessControlManager Customer Authorize LoginService LoginDatabase LoginOAuth

Slide 133

Slide 133 text

class Authorize { public function isAuthorized(Customer $customer) { //validate authorization } } class Customer { public function getId() { return $this->id; } } interface IdentityService { public function getId(); } class Customer implements IdentityService { public function getId() { return $this->id; } }

Slide 134

Slide 134 text

class Customer { public function getId() { return $this->id; } }

Slide 135

Slide 135 text

class Customer { public function getId() { return $this->id; } } interface IdentityService { public function getId(); }

Slide 136

Slide 136 text

interface IdentityService { public function getId(); } class Customer implements IdentityService { public function getId() { return $this->id; } }

Slide 137

Slide 137 text

class AccessControlManager { public function __construct(IdentityService $user, LoginService $login, Authorize $authoize) { //assign } public function login() { if ($this->login->authenticate($this->user) && $this->authorize->isAuthorized($this->user)) { return true; } return false; } }

Slide 138

Slide 138 text

AccessControlManager Customer Authorize LoginService LoginDatabase LoginOAuth IdentityService

Slide 139

Slide 139 text

Depend on abstractions. Implement against abstractions. Interfaces are the key to keeping code clean.

Slide 140

Slide 140 text

Thank You @jcarouth joind.in/13765