oop is more than cars and dogs
TRANSCRIPT
Who Am I
• PHP Programmer for over 10 years
• Work/know a lot of different languages, even COBOL
• Primarily do Zend Framework 2
• https://github.com/dragonmantank
php[tek] 2015 2
Quick Vocabulary Lesson
• Class – Definition of code
• Object – Instantiation of a Class
• Member – Variable belonging to a class
• Method – Function belonging to a class
There will be more as we go along
php[tek] 2015 3
php[tek] 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;
}
}
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
php[tek] 2015 7
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
php[tek] 2015 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
php[tek] 2015 18
The Employee Class
php[tek] 2015 21
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
php[tek] 2015 22
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
php[tek] 2015 23
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
php[tek] 2015 25
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;
}
} php[tek] 2015 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
php[tek] 2015 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
php[tek] 2015 28
The Employee Class
php[tek] 2015 29
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
php[tek] 2015 30
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
// 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();
php[tek] 2015 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
php[tek] 2015 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
php[tek] 2015 34
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);
}
php[tek] 2015 35
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;
}
}
php[tek] 2015 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
php[tek] 2015 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.
php[tek] 2015 38
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;
}
php[tek] 2015 39
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
php[tek] 2015 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();
}
}
php[tek] 2015 43
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
php[tek] 2015 46
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);
}
}
php[tek] 2015 47
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);
}
}
php[tek] 2015 48
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);
}
}
php[tek] 2015 49
Single Responsibility Principle
• Every class should have a single responsibility, and that responsibility should be encapsulated in that class
php[tek] 2015 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
php[tek] 2015 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;
}
php[tek] 2015 53
https://github.com/laravel/cashier/blob/master/src/Laravel/Cashier/Invoice.php
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
php[tek] 2015 54
How to Improve
• Change responsibility to just building the invoice data
• Move the ‘output’ stuff to other classes
php[tek] 2015 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
php[tek] 2015 58
We canmake 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
php[tek] 2015 60
Thank You!
http://ctankersley.com
@dragonmantank
https://joind.in/13731
php[tek] 2015 62
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
php[tek] 2015 63