coming to terms with oop in drupal - php[world] 2016

57
Coming to Terms with OOP in Drupal Chris Tankersley php[world] 2016 php[world] 2016 1

Upload: chris-tankersley

Post on 16-Apr-2017

130 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 1

Coming to Terms with OOP in DrupalChris Tankersleyphp[world] 2016

Page 2: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 2

In the beginning…function mymodule_modules_enabled($modules) { // Stuff happens}

function mymodule_menu() { return array( // … );}

Page 3: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 3

Procedural Programming• Code is kept in Procedures (functions)• Procedures contain steps to be carried out

Page 4: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 4

Why don’t people like it?• Can be hard to test• Can be hard to isolate• Can be hard to name functions succinctly• Can be hard to organize• Can be hard to share• Nearly impossible to modify original, 3rd party code

Page 5: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 5

Why do we want OOP?• Better ability to test• Better architecture through code encapsulation• With Namespaces, better naming• The ability to share code that can be extended

Page 6: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 6

Vocabulary• Class – Textual representation of how an object is made up• Object – Variable built from a class that holds data and performs

actions

Page 7: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 7

Classclass Employee { protected $name; protected $number;

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

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

Page 8: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 8

Object<?php

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

Page 9: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 9

Methods and Propertiesclass Employee { protected $name; // This is a property 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; }}

Page 10: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 10

New Vocabulary• Property – Data inside of an object• Method – Function inside of an object

• Both are accessed using the -> notation

Page 11: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 11

Visibility• Public – Anyone can access the property/method• Protected – Only the class, or child classes, can access• Private – Only the class itself can access

Page 12: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 12

Classclass Employee { public $name; // Anyone can access this protected $number; // Only itself, and children can access private $ssn; // Only itself can access this

// Anyone can call this method public function setData($data) { $this->name = $data['name']; $this->number = $data['number']; }

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

Page 13: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 13

Object<?php

$manager= new Employee();$manager->name = ‘Bob’;$manager->number = 1234;

// PHP Fatal error: Cannot access protected property Employee::$number

Page 14: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 14

Extending Classes• Inheritance• Composition

Page 15: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 15

Namespaces• Sets up “packages” of code for organization• Is a “path” separated by \• Allows us to have classes with simple, clear names and avoid naming

collisions

Page 16: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 16

namespace Drupal\block\Entity;

class Block {}

Page 17: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 17

$block = new \Drupal\block\Entity\Block();

Page 18: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 18

use \Drupal\block\Entity\Block;use \MyNamespace\block\Entity\Block as MyBlock;

$block = new Block();$my_block = new MyBlock();

Page 19: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 19

Inheritance

Page 20: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 20

The first thing most people learn• Classes are “things” in the real world• We should construct class properties based on Attributes• Number of wheels• Sound it makes

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

Page 21: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 21

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

Page 22: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 22

Our Structure

Employee

Manager Scientist Laborer

Page 23: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 23

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; }}

Page 24: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 24

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; }}

Page 25: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 25

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; }}

Page 26: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 26

The Laborer Class

class Laborer extends Employee { }

Page 27: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 27

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

Page 28: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 28

We use it all the timenamespace Drupal\block\Entity;use Drupal\Core\Config\Entity\ConfigEntityBase;

use Drupal\block\BlockInterface;

use Drupal\Core\Entity\EntityWithPluginCollectionInterface;

class Block extends ConfigEntityBase, implements BlockInterface, EntityWithPluginCollectionInterface { protected $id; protected $settings; // … public function getRegion() { return $this->region; } // …}

Page 29: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 29

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

Page 30: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 30

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

Page 31: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 31

Composition

Page 32: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 32

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

Page 33: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 33

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);}

Page 34: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 34

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; }}

Page 35: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 35

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

Page 36: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 36

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.

Page 37: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 37

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;}

Page 38: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 38

Taking Advantage of OOP

Page 39: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 39

Coupling

Page 40: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 40

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

Page 41: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 41

class Block extends ConfigEntityBase, implements BlockInterface, EntityWithPluginCollectionInterface { public function getPlugin() { return $this->getPluginCollection()->get($this->plugin); }

protected function getPluginCollection() { if (!$this->pluginCollection) { $this->pluginCollection = new BlockPluginCollection( \Drupal::service('plugin.manager.block'), $this->plugin, $this->get('settings'), $this->id()); } return $this->pluginCollection; }}

Page 42: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 42

Law of Demeter

Page 43: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 43

Dependency Injection

Page 44: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 44

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

Page 45: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 45

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); }}

Page 46: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 46

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); }}

Page 47: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 47

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); }}

Page 48: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 48

Single Responsibility Principle

Page 49: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 49

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

should be encapsulated in that class

Page 50: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 50

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

Page 51: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 51

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/master/src/Laravel/Cashier/Invoice.php

Page 52: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 52

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

Page 53: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 53

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

Page 54: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 54

Unit Testing

Page 55: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 55

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

Page 57: Coming to Terms with OOP In Drupal - php[world] 2016

php[world] 2016 57

Thank You!• Software Engineer, InQuest• Co-Host of “Jerks Talk Games”• http://jerkstalkgames.com

• Author of “Docker for Developers”• https://leanpub.com/dockerfordevs

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