When developers are introduced to Object Oriented Programming, one of the first things that happens is that they are taught that nouns turn into objects, verbs into methods, and Dog is a subclass of Animal. OOP is more than just turning things into classes and objects and showing that both Boats and Cars have motors, and that Dogs and Cats both speak(). Let's look at OOP in real world settings and go beyond cars and dogs, and see how to use Object Oriented Programming properly in PHP. Traits, Composition, Inheritance, none of it is off limits! (No animals were harmed in preparation for this talk, though there was mention of showing how a dog can have wheels. And yes, the title is supposed to be Cars and Dogs.)
Apidays New York 2024 - The value of a flexible API Management solution for O...
OOP Is More Than Cars and Dogs
1. OOP is More Than Cars
and Dogs
Chris
Tankersley
MadisonPHP
2015
MadisonPHP
2015
1
2. Who Am I
• PHP
Programmer
for
over
10
years
• Work/know
a
lot
of
different
languages,
even
COBOL
• Primarily
do
Zend
Framework
2
• hGps://github.com/dragonmantank
MadisonPHP
2015
2
3. Quick Vocabulary Lesson
• Class
–
DefiniPon
of
code
• Object
–
InstanPaPon
of
a
Class
• Member
–
Variable
belonging
to
a
class
• Method
–
FuncPon
belonging
to
a
class
There
will
be
more
as
we
go
along
MadisonPHP
2015
3
4. MadisonPHP
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;
}
}
7. 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
MadisonPHP
2015
7
17. What we’re all taught
• Classes
are
“things”
in
the
real
world
• We
should
construct
class
members
based
on
AGributes
• Number
of
wheels
• Sound
it
makes
• We
should
construct
class
methods
based
on
“AcPons”
• Running
• Speaking
• Jumping
MadisonPHP
2015
17
18. 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
Pme
MadisonPHP
2015
18
21. The Employee Class
MadisonPHP
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;
}
}
22. The Manager Class
MadisonPHP
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;
}
}
23. The Scientist Class
MadisonPHP
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;
}
}
25. 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
MadisonPHP
2015
25
26. We use it all the time
namespace ApplicationController;!
!
use ZendMvcControllerAbstractActionController;!
use ZendViewModelViewModel;!
!
Class IndexController extends AbstractActionController {!
public function indexAction() {!
/** @var VendorVendorService $vendor */!
$vendor = $this->serviceLocator->get('VendorVendorService');!
!
$view = new ViewModel();!
return $view;!
}!
}
MadisonPHP
2015
26
27. 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
MadisonPHP
2015
27
28. 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
instanPated
directly,
they
must
be
extended
MadisonPHP
2015
28
29. The Employee Class
MadisonPHP
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;
}
}
30. The Manager Class
MadisonPHP
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;
}
}
31. 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();
MadisonPHP
2015
31
32. 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
funcPonality
between
disparate
classes
MadisonPHP
2015
32
34. 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
funcPonality
MadisonPHP
2015
34
35. 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);!
}
MadisonPHP
2015
35
36. 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;!
}!
}
MadisonPHP
2015
36
37. 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
ImplementaPon,
so
can
lead
to
code
duplicaPon
MadisonPHP
2015
37
38. 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.
MadisonPHP
2015
38
39. 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;!
}
MadisonPHP
2015
39
42. 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
MadisonPHP
2015
42
43. namespace ApplicationController;!
!
use ZendMvcControllerAbstractActionController;!
use ZendViewModelViewModel;!
!
class MapController extends AbstractActionController!
{!
public function indexAction()!
{!
// Position is an array with a Latitude and Longitude object!
$position = $this->getServiceLocator()->get('MapService’)!
->getLatLong('123 Main Street', 'Defiance', 'OH');!
echo $position->latitude->getPoint();!
}!
}
MadisonPHP
2015
43
46. What is Dependency Injection?
• InjecPng
dependencies
into
classes,
instead
of
having
the
class
create
it
• Allows
for
much
easier
tesPng
• Allows
for
a
much
easier
Pme
swapping
out
code
• Reduces
the
coupling
that
happens
between
classes
MadisonPHP
2015
46
47. 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);!
}!
}
MadisonPHP
2015
47
48. 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);!
}!
}!
!
MadisonPHP
2015
48
49. 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);!
}!
}!
MadisonPHP
2015
49
51. Single Responsibility Principle
• Every
class
should
have
a
single
responsibility,
and
that
responsibility
should
be
encapsulated
in
that
class
MadisonPHP
2015
51
52. What is a Responsibility?
• Responsibility
is
a
“Reason
To
Change”
–
Robert
C.
MarPn
• By
having
more
than
one
“Reason
to
Change”,
code
is
harder
to
maintain
and
becomes
coupled
• Since
the
class
is
coupled
to
mulPple
responsibiliPes,
it
becomes
harder
for
the
class
to
adapt
to
any
one
responsibility
MadisonPHP
2015
52
53. An Example
/**
* Create a new invoice instance.
*
* @param LaravelCashierContractsBillable $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 SymfonyComponentHttpFoundationResponse
*/
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;
}
MadisonPHP
2015
53
hGps://github.com/laravel/cashier/blob/master/src/Laravel/Cashier/Invoice.php
54. Why is this Bad?
• This
single
class
has
the
following
responsibiliPes:
• GeneraPng
totals
for
the
invoice
(including
discounts/coupons)
• GeneraPng
an
HTML
View
of
the
invoice
(Invoice::view())
• GeneraPng
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
MadisonPHP
2015
54
55. How to Improve
• Change
responsibility
to
just
building
the
invoice
data
• Move
the
‘output’
stuff
to
other
classes
MadisonPHP
2015
55
57. [Could
not
afford
licensing
fee
for
Grumpy
TesPng
Picture]
MadisonPHP
2015
57
58. 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
InjecPon
means
you
only
mock
what
you
need
for
that
test
• Single
Responsibility
means
your
test
should
be
short
and
sweet
• Easier
tesPng
leads
to
more
tesPng
MadisonPHP
2015
58
60. 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
MadisonPHP
2015
60