Date post: | 05-Apr-2017 |
Category: |
Technology |
Upload: | chris-tankersley |
View: | 33 times |
Download: | 3 times |
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 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 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 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 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 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 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 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 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 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 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 61
Additional Resources• Clean Code – Robert C. Martin• PHP Objects, Patterns, and Practice – Matt Zandstra
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