Upload
chris-tankersley
View
111
Download
1
Embed Size (px)
Citation preview
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
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
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
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; } }
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
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
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
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
• 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
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
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
Thank You!
hFp://ctankersley.com [email protected]
@dragonmantank
hFps://joind.in/13542
LonestarPHP 2015 55