55
OOP is More Than Cats and Dogs Chris Tankersley LonestarPHP 2015 LonestarPHP 2015 1

OOP is more than Cars and Dogs

Embed Size (px)

Citation preview

OOP is More Than Cats and Dogs Chris  Tankersley  LonestarPHP  2015  

LonestarPHP  2015   1  

Who Am I

• PHP  Programmer  for  over  10  years  • Work/know  a  lot  of  different  languages,  even  COBOL  

• Primarily  do  Zend  Framework  2  • hFps://github.com/dragonmantank  

LonestarPHP  2015   2  

Quick Vocabulary Lesson

• Class  –  DefiniOon  of  code  • Object  –  InstanOaOon  of  a  Class  • Member  –  Variable  belonging  to  a  class  • Method  –  FuncOon  belonging  to  a  class  

There  will  be  more  as  we  go  along  

LonestarPHP  2015   3  

LonestarPHP  2015   4  

Class

class 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 <<<ENDTEXT Name: {$this->name} Number: {$this->number} ENDTEXT; } }

Object

• <?php!

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

LonestarPHP  2015   5  

Why are we using OOP?

LonestarPHP  2015   6  

Let’s count the reasons

• Because  we’re  told  to,  procedural  programming  leads  to  spagheX  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  

LonestarPHP  2015   7  

Getting OOP Right can be Complicated

• Animals  can  speak,  and  have  legs  and  fur.  Let’s  make  objects  based  on  that  

• Dogs  are  animals,  and  so  are  cats  • What  about  Fish?  • Cars  have  doors,  engines,  and  colors.  They  only  go  so  fast.  Let’s  model  that  

• Motorcycles  aren’t  Cars,  so  let’s  refactor  Cars  • Holy  crap  there  are  a  lot  of  engines…  and  wheels…    

LonestarPHP  2015   8  

Can a Dog have Wheels?

• Discuss  (or  listen  to  me  talk  some  more)  

LonestarPHP  2015   9  

Inheritance

LonestarPHP  2015   10  

What we’re all taught

• Classes  are  “things”  in  the  real  world  • We  should  construct  class  members  based  on  AFributes  

•  Number  of  wheels  •  Sound  it  makes  

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

LonestarPHP  2015   11  

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  Ome  

LonestarPHP  2015   12  

Where I Learned It

LonestarPHP  2015   13  

Our Structure

LonestarPHP  2015   14  

Employee  

Manager   ScienOst   Laborer  

The Employee Class

LonestarPHP  2015   15  

abstract 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 <<<ENDTEXT Name: {$this->name} Number: {$this->number} ENDTEXT; } }

The Manager Class

LonestarPHP  2015   16  

class 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 <<<ENDTEXT Title: {$this->title} Golf Dues: {$this->dues} ENDTEXT; } }

The Scientist Class

LonestarPHP  2015   17  

class 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 <<<ENDTEXT Publications: {$this->pubs} ENDTEXT; } }

The Laborer Class

LonestarPHP  2015   18  

class Laborer extends Employee { }

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  

LonestarPHP  2015   19  

We use it all the time namespace 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; ! } !}  

LonestarPHP  2015   20  

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  

LonestarPHP  2015   21  

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  instanOated  directly,  they  must  be  extended  

LonestarPHP  2015   22  

The Employee Class

LonestarPHP  2015   23  

abstract 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 <<<ENDTEXT Name: {$this->name} Number: {$this->number} ENDTEXT; } }

The Manager Class

LonestarPHP  2015   24  

class 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 <<<ENDTEXT Title: {$this->title} Golf Dues: {$this->dues} ENDTEXT; } }

An Example <?php!// Cannot do this. This will throw the following error: !// Fatal error: Cannot instantiate abstract class Employee !$employee = new Employee(); !// We can do this though! !$manager = new Manager(); !// Name is protected, so we can't do this. This throws: !// 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();  

LonestarPHP  2015   25  

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  funcOonality  between  disparate  classes  

LonestarPHP  2015   26  

Composition over Inheritance

LonestarPHP  2015   27  

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  funcOonality  

LonestarPHP  2015   28  

Interfaces interface 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); !}  

LonestarPHP  2015   29  

Interface Implementation class 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; ! } !}  

LonestarPHP  2015   30  

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  ImplementaOon,  so  can  lead  to  code  duplicaOon  

LonestarPHP  2015   31  

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  should  need  to  extend  a  Slugger  class  just  to  generate  slugs.    

LonestarPHP  2015   32  

Avoid Code-Duplication with Traits trait 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; !}  

LonestarPHP  2015   33  

Taking Advantage of OOP

LonestarPHP  2015   34  

Coupling

LonestarPHP  2015   35  

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  

LonestarPHP  2015   36  

<?php!!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(); ! } !}  

LonestarPHP  2015   37  

Law of Demeter

LonestarPHP  2015   38  

Dependency Injection

LonestarPHP  2015   39  

What is Dependency Injection?

•  InjecOng  dependencies  into  classes,  instead  of  having  the  class  create  it  

• Allows  for  much  easier  tesOng  • Allows  for  a  much  easier  Ome  swapping  out  code  • Reduces  the  coupling  that  happens  between  classes  

LonestarPHP  2015   40  

Method Injection class 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); ! } !}  

LonestarPHP  2015   41  

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

LonestarPHP  2015   42  

Setter Injection class 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); ! } !} !  

LonestarPHP  2015   43  

Single Responsibility Principle

LonestarPHP  2015   44  

Single Responsibility Principle

•  Every  class  should  have  a  single  responsibility,  and  that  responsibility  should  be  encapsulated  in  that  class  

LonestarPHP  2015   45  

What is a Responsibility?

• Responsibility  is  a  “Reason  To  Change”  –  Robert  C.  MarOn  • By  having  more  than  one  “Reason  to  Change”,  code  is  harder  to  maintain  and  becomes  coupled  

•  Since  the  class  is  coupled  to  mulOple  responsibiliOes,  it  becomes  harder  for  the  class  to  adapt  to  any  one  responsibility  

 

LonestarPHP  2015   46  

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

LonestarPHP  2015   47  

hFps://github.com/laravel/cashier/blob/master/src/Laravel/Cashier/Invoice.php  

Why is this Bad?

•  This  single  class  has  the  following  responsibiliOes:  •  GeneraOng  totals  for  the  invoice  (including  discounts/coupons)  •  GeneraOng  an  HTML  View  of  the  invoice  (Invoice::view())  •  GeneraOng  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  

LonestarPHP  2015   48  

How to Improve

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

LonestarPHP  2015   49  

Unit Testing

LonestarPHP  2015   50  

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  InjecOon  means  you  only  mock  what  you  need  for  that  test  

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

LonestarPHP  2015   51  

Final Thoughts

LonestarPHP  2015   52  

We can make a dog with wheels!

• Abstract  class  for  Animal  • Class  for  Dog  that  extends  Animal  •  Trait  for  Wheels  • With  the  write  methodology,  we  could  even  unit  test  this  

In  the  real  world,  we  can  now  represent  a  crippled  dog  

LonestarPHP  2015   53  

Here’s a cute dog instead

LonestarPHP  2015   54  

Thank You!

hFp://ctankersley.com  [email protected]  

@dragonmantank    

hFps://joind.in/13542  

LonestarPHP  2015   55