oop is more then cars and dogs - midwest php 2017

63
OOP is More Than Cars and Dogs Chris Tankersley Midwest PHP 2017 Midwest PHP 2017 1

Upload: chris-tankersley

Post on 05-Apr-2017

33 views

Category:

Technology


3 download

TRANSCRIPT

Midwest PHP 2017 1

OOP is More Than Cars and DogsChris TankersleyMidwest PHP 2017

Midwest PHP 2017 2

Quick Vocabulary Lesson• Class – Definition of code• Object – Instantiation of a Class• Member/Property – Variable belonging to a class• Method – Function belonging to a class

There will be more as we go along

Midwest PHP 2017 3

Classclass Employee { protected $name; // This is a member protected $number;

// This is a Method public function setData($data) { $this->name = $data['name']; $this->number = $data['number']; }

public function viewData() { echo <<<ENDTEXTName: {$this->name}Number: {$this->number}ENDTEXT; }}

Midwest PHP 2017 4

Object<?php

$manager= new Manager();// ^// |// `--- This is the Object

Midwest PHP 2017 5

Why are we using OOP?

Midwest PHP 2017 6

Let’s count the reasons• Because we’re told to, procedural programming leads to spaghetti

code• We deal with objects every day, so it shouldn’t be too hard

• We want to allow for code re-use• We want to group like code together• We want to easily extend our code• We want to be able to easily test our code

Midwest PHP 2017 7

Getting OOP Right is Complicated

Midwest PHP 2017 8

Midwest PHP 2017 9

Midwest PHP 2017 10

Midwest PHP 2017 11

Midwest PHP 2017 12

Midwest PHP 2017 13

Midwest PHP 2017 14

Can a Dog have Wheels?

Midwest PHP 2017 15

Inheritance

Midwest PHP 2017 16

What we’re all taught• Classes are “things” in the real world• We should construct class members based on Attributes• Number of wheels• Sound it makes

• We should construct class methods based on “Actions”• Running• Speaking• Jumping

Midwest PHP 2017 17

New Vocabulary• Parent Class – Class that is extended• Child Class – Class that is extending another class

In PHP, a class can be both a Child and a Parent at the same time

Midwest PHP 2017 18

Where I Learned It

Midwest PHP 2017 19

Our Structure

Employee

Manager Scientist Laborer

Midwest PHP 2017 20

The Employee Classabstract class Employee { protected $name; // Employee Name protected $number; // Employee Number

public function setData($data) { $this->name = $data['name']; $this->number = $data['number']; }

public function viewData() { echo <<<ENDTEXTName: {$this->name}Number: {$this->number}ENDTEXT; }}

Midwest PHP 2017 21

The Manager Classclass Manager extends Employee { protected $title; // Employee Title protected $dues; // Golf Dues

public function setData($data) { parent::setData($data); $this->title = $data['title']; $this->dues = $data['dues']; } public function viewData() { parent::viewData(); echo <<<ENDTEXTTitle: {$this->title}Golf Dues: {$this->dues}ENDTEXT; }}

Midwest PHP 2017 22

The Scientist Classclass Scientist extends Employee { protected $pubs; // Number of Publications

public function setData($data) { parent::setData($data); $this->pubs = $data['pubs']; }

public function viewData() { parent::viewData(); echo <<<ENDTEXTPublications: {$this->pubs}ENDTEXT; }}

Midwest PHP 2017 23

The Laborer Class

class Laborer extends Employee { }

Midwest PHP 2017 24

What does this teach us?• Inheritance• Makes it easier to group code together and share it amongst classes• Allows us to extend code as needed• PHP allows Single inheritance

Midwest PHP 2017 25

We use it all the timenamespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;use Zend\View\Model\ViewModel;

Class IndexController extends AbstractActionController { public function indexAction() { /** @var \Vendor\VendorService $vendor */ $vendor = $this->serviceLocator->get('Vendor\VendorService');

$view = new ViewModel(); return $view; }}

Midwest PHP 2017 26

Why it Works (Most of the time, Kinda)• Allows us to extend things we didn’t necessarily create• Encourages code re-use• Allows developers to abstract away things

Midwest PHP 2017 27

How to use it• Understand the difference between Public, Protected, and Private• Public – Anyone can use this, even children• Protected – Anything internal can use this, even children• Private – This is mine, hands off

• Abstract vs Concrete Classes• Abstract classes cannot be instantiated directly, they must be extended

Midwest PHP 2017 28

The Employee Classabstract class Employee { protected $name; // Employee Name protected $number; // Employee Number

public function setData($data) { $this->name = $data['name']; $this->number = $data['number']; }

public function viewData() { echo <<<ENDTEXTName: {$this->name}Number: {$this->number}ENDTEXT; }}

Midwest PHP 2017 29

The Manager Classclass Manager extends Employee { protected $title; // Employee Title protected $dues; // Golf Dues

public function setData($data) { parent::setData($data); $this->title = $data['title']; $this->dues = $data['dues']; } public function viewData() { parent::viewData(); echo <<<ENDTEXTTitle: {$this->title}Golf Dues: {$this->dues}ENDTEXT; }}

Midwest PHP 2017 30

An Example// Fatal error: Cannot instantiate abstract class Employee$employee = new Employee();

// We can do this though!$manager = new Manager();

// Fatal error: Cannot access protected property Manager::$name$manager->name = 'Bob McManager’;

// setData is public, so we can use that$manager->setData(['name' => 'Bob McManager’,'number' => 1]);

// We can also view the data, since it's public$manager->viewData();

Midwest PHP 2017 31

Why can Inheritance Be Bad• PHP only allows Single Inheritance on an Class• You can have a series of Inheritance though, for example CEO extends

Manager, Manager extends Employee

• Long inheritance chains can be a code smell• Private members and methods cannot be used by Child classes• Single Inheritance can make it hard to ‘bolt on’ new functionality

between disparate classes

Midwest PHP 2017 32

Composition over Inheritance

Midwest PHP 2017 33

The General Idea• Classes contain other classes to do work and extend that way, instead

of through Inheritance• Interfaces define “contracts” that objects will adhere to• Your classes implement interfaces to add needed functionality

Midwest PHP 2017 34

Interfacesinterface EmployeeInterface { protected $name; protected $number;

public function getName(); public function setName($name); public function getNumber(); public function setNumber($number);}

interface ManagerInterface { protected $golfHandicap; public function getHandicap(); public function setHandicap($handicap);}

Midwest PHP 2017 35

Interface Implementationclass Employee implements EmployeeInterface { public function getName() { return $this->name; } public function setName($name) { $this->name = $name; }}class Manager implements EmployeeInterface, ManagerInterface { // defines the employee getters/setters as well public function getHandicap() { return $this->handicap; } public function setHandicap($handicap) { $this->handicap = $handicap; }}

Midwest PHP 2017 36

This is Good and Bad• “HAS-A” is tends to be more flexible than “IS-A”• Somewhat easier to understand, since there isn’t a hierarchy you have

to backtrack

• Each class must provide their own Implementation, so can lead to code duplication

Midwest PHP 2017 37

Traits• Allows small blocks of code to be defined that can be used by many

classes• Useful when abstract classes/inheritance would be cumbersome• My Posts and Pages classes shouldn’t need to extend a Slugger class just to

generate slugs.

Midwest PHP 2017 38

Avoid Code-Duplication with Traitstrait EmployeeTrait { public function getName() { return $this->name; } public function setName($name) { $this->name = $name; }}class Employee implements EmployeeInterface { use EmployeeTrait;}class Manager implements EmployeeInterface, ManagerInterface { use EmployeeTrait; use ManagerTrait;}

Midwest PHP 2017 39

Taking Advantage of OOP

Midwest PHP 2017 40

Coupling

Midwest PHP 2017 41

What is Coupling?• Coupling is how dependent your code is on another class• The more classes you are coupled to, the more changes affect your

class

Midwest PHP 2017 42

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;use Zend\View\Model\ViewModel;

class MapController extends AbstractActionController{ public function indexAction() { // Position is an array with a Latitude and Longitude object $position = $this->getServiceLocator()->get('Map\Service’) ->getLatLong('123 Main Street', 'Defiance', 'OH'); echo $position->latitude->getPoint(); }}

Midwest PHP 2017 43

Law of Demeter

Midwest PHP 2017 44

Dependency Injection

Midwest PHP 2017 45

What is Dependency Injection?• Injecting dependencies into classes, instead of having the class create

it• Allows for much easier testing• Allows for a much easier time swapping out code• Reduces the coupling that happens between classes

Midwest PHP 2017 46

Method Injectionclass MapService { public function getLatLong(GoogleMaps $map, $street, $city, $state) { return $map->getLatLong($street . ' ' . $city . ' ' . $state); } public function getAddress(GoogleMaps $map, $lat, $long) { return $map->getAddress($lat, $long); }}

Midwest PHP 2017 47

Constructor Injectionclass MapService { protected $map; public function __construct(GoogleMaps $map) { $this->map = $map; } public function getLatLong($street, $city, $state) { return $this ->map ->getLatLong($street . ' ' . $city . ' ' . $state); }}

Midwest PHP 2017 48

Setter Injectionclass MapService { protected $map; public function setMap(GoogleMaps $map) { $this->map = $map; } public function getMap() { return $this->map; } public function getLatLong($street, $city, $state) { return $this->getMap()->getLatLong($street . ' ' . $city . ' ' . $state); }}

Midwest PHP 2017 49

Single Responsibility Principle

Midwest PHP 2017 50

Single Responsibility Principle• Every class should have a single responsibility, and that responsibility

should be encapsulated in that class

Midwest PHP 2017 51

What is a Responsibility?• Responsibility is a “Reason To Change” – Robert C. Martin• By having more than one “Reason to Change”, code is harder to

maintain and becomes coupled• Since the class is coupled to multiple responsibilities, it becomes

harder for the class to adapt to any one responsibility

Midwest PHP 2017 52

An Example/** * Create a new invoice instance. * * @param \Laravel\Cashier\Contracts\Billable $billable * @param object * @return void */public function __construct(BillableContract $billable, $invoice){ $this->billable = $billable; $this->files = new Filesystem; $this->stripeInvoice = $invoice;}

/** * Create an invoice download response. * * @param array $data * @param string $storagePath * @return \Symfony\Component\HttpFoundation\Response */public function download(array $data, $storagePath = null){ $filename = $this->getDownloadFilename($data['product']); $document = $this->writeInvoice($data, $storagePath); $response = new Response($this->files->get($document), 200, [ 'Content-Description' => 'File Transfer', 'Content-Disposition' => 'attachment; filename="'.$filename.'"', 'Content-Transfer-Encoding' => 'binary', 'Content-Type' => 'application/pdf', ]); $this->files->delete($document); return $response;}

https://github.com/laravel/cashier/blob/5.0/src/Laravel/Cashier/Invoice.php

Midwest PHP 2017 53

Why is this Bad?• This single class has the following responsibilities:• Generating totals for the invoice (including discounts/coupons)• Generating an HTML View of the invoice (Invoice::view())• Generating a PDF download of the invoice(Invoice::download())

• This is coupled to a shell script as well

• Two different displays handled by the class. Adding more means more responsibility• Coupled to a specific HTML template, the filesystem, the Laravel

Views system, and PhantomJS via the shell script

Midwest PHP 2017 54

How to Improve• Change responsibility to just building the invoice data• Move the ‘output’ stuff to other classes

Midwest PHP 2017 55

Unit Testing

Midwest PHP 2017 56

Test Driven Development• Write your tests• Watch them fail• Write code to make the tests pass• Feel better! (and refactor)

Midwest PHP 2017 57

This is not a testing talk• Using Interfaces makes it easier to mock objects• Reducing coupling and following Demeter’s Law makes you have to

mock less objects• Dependency Injection means you only mock what you need for that

test• Single Responsibility means your test should be short and sweet• Easier testing leads to more testing

Midwest PHP 2017 58

Final Thoughts

Midwest PHP 2017 59

We can make a dog with wheels!• Abstract class for Animal• Class for Dog that extends Animal• Trait for Wheels• With the correct methodology, we could even unit test this

As in the real world, we can now represent a crippled dog

Midwest PHP 2017 60

Here’s a cute dog instead

SunshinePHP 2017 62

Thank You!• Software Engineer for InQuest• Author of “Docker for Developers”• https://leanpub.com/dockerfordevs

• Co-Host of “Jerks Talk Games”• http://jerkstalkgames

• http://ctankersley.com• [email protected]• @dragonmantank

Midwest PHP 2017 63

Photos• Slide 9 - http://bit.ly/1dkaoxS• Slide 10 - http://bit.ly/1c4Gc8z• Slide 11 - http://bit.ly/1R3isBp• Slide 12 - http://bit.ly/1ScEWRZ• Slide 13 - http://bit.ly/1Bc0qUv• Slide 14 - http://bit.ly/1ILhfNV• Slide 15 - http://bit.ly/1SeekA7