OOP is More Than Cars
and Dogs
Chris	
  Tankersley	
  
MadisonPHP	
  2015	
  
MadisonPHP	
  2015	
   1	
  
Who Am I
•  PHP	
  Programmer	
  for	
  over	
  10	
  years	
  
•  Work/know	
  a	
  lot	
  of	
  different	
  
languages,	...
Quick Vocabulary Lesson
•  Class	
  –	
  DefiniPon	
  of	
  code	
  
•  Object	
  –	
  InstanPaPon	
  of	
  a	
  Class	
  
...
MadisonPHP	
  2015	
   4	
  
Class
class Employee {
protected $name; // This is a member
protected $number;
// This is a M...
Object
<?php!
!
$manager= new Manager();!
// ^!
// |!
// `--- This is the Object	
  
MadisonPHP	
  2015	
   5	
  
Why are we using OOP?
MadisonPHP	
  2015	
   6	
  
Let’s count the reasons
•  Because	
  we’re	
  told	
  to,	
  procedural	
  programming	
  leads	
  to	
  spagheX	
  
code...
Getting OOP Right is Complicated
MadisonPHP	
  2015	
   8	
  
MadisonPHP	
  2015	
   9	
  
MadisonPHP	
  2015	
   10	
  
MadisonPHP	
  2015	
   11	
  
MadisonPHP	
  2015	
   12	
  
MadisonPHP	
  2015	
   13	
  
MadisonPHP	
  2015	
   14	
  
Can a Dog have Wheels?
MadisonPHP	
  2015	
   15	
  
Inheritance
MadisonPHP	
  2015	
   16	
  
What we’re all taught
•  Classes	
  are	
  “things”	
  in	
  the	
  real	
  world	
  
•  We	
  should	
  construct	
  clas...
New Vocabulary
•  Parent	
  Class	
  –	
  Class	
  that	
  is	
  extended	
  
•  Child	
  Class	
  –	
  Class	
  that	
  i...
Where I Learned It
MadisonPHP	
  2015	
   19	
  
Our Structure
MadisonPHP	
  2015	
   20	
  
Employee	
  
Manager	
   ScienPst	
   Laborer	
  
The Employee Class
MadisonPHP	
  2015	
   21	
  
abstract class Employee {
protected $name; // Employee Name
protected $nu...
The Manager Class
MadisonPHP	
  2015	
   22	
  
class Manager extends Employee {
protected $title; // Employee Title
prote...
The Scientist Class
MadisonPHP	
  2015	
   23	
  
class Scientist extends Employee {
protected $pubs; // Number of Publica...
The Laborer Class
MadisonPHP	
  2015	
   24	
  
class Laborer extends Employee {
}
What does this teach us?
•  Inheritance	
  
•  Makes	
  it	
  easier	
  to	
  group	
  code	
  together	
  and	
  share	
 ...
We use it all the time
namespace ApplicationController;!
!
use ZendMvcControllerAbstractActionController;!
use ZendViewMod...
Why it Works (Most of the time, Kinda)
•  Allows	
  us	
  to	
  extend	
  things	
  we	
  didn’t	
  necessarily	
  create	...
How to use it
•  Understand	
  the	
  difference	
  between	
  Public,	
  Protected,	
  and	
  Private	
  
•  Public	
  –	
...
The Employee Class
MadisonPHP	
  2015	
   29	
  
abstract class Employee {
protected $name; // Employee Name
protected $nu...
The Manager Class
MadisonPHP	
  2015	
   30	
  
class Manager extends Employee {
protected $title; // Employee Title
prote...
An Example
// Fatal error: Cannot instantiate abstract class Employee!
$employee = new Employee(); !
!
// We can do this t...
Why can Inheritance Be Bad
•  PHP	
  only	
  allows	
  Single	
  Inheritance	
  on	
  an	
  Class	
  
•  You	
  can	
  hav...
Composition over Inheritance
MadisonPHP	
  2015	
   33	
  
The General Idea
•  Classes	
  contain	
  other	
  classes	
  to	
  do	
  work	
  and	
  extend	
  that	
  way,	
  instead...
Interfaces
interface EmployeeInterface {!
protected $name;!
protected $number;!
!
public function getName();!
public funct...
Interface Implementation
class Employee implements EmployeeInterface {!
public function getName() {!
return $this->name;!
...
This is Good and Bad
•  “HAS-­‐A”	
  is	
  tends	
  to	
  be	
  more	
  flexible	
  than	
  “IS-­‐A”	
  
•  Somewhat	
  eas...
Traits
•  Allows	
  small	
  blocks	
  of	
  code	
  to	
  be	
  defined	
  that	
  can	
  be	
  used	
  by	
  many	
  
cla...
Avoid Code-Duplication with Traits
trait EmployeeTrait {!
public function getName() {!
return $this->name;!
}!
public func...
Taking Advantage of OOP
MadisonPHP	
  2015	
   40	
  
Coupling
MadisonPHP	
  2015	
   41	
  
What is Coupling?
•  Coupling	
  is	
  how	
  dependent	
  your	
  code	
  is	
  on	
  another	
  class	
  
•  The	
  more...
namespace ApplicationController;!
!
use ZendMvcControllerAbstractActionController;!
use ZendViewModelViewModel;!
!
class M...
Law of Demeter
MadisonPHP	
  2015	
   44	
  
Dependency Injection
MadisonPHP	
  2015	
   45	
  
What is Dependency Injection?
•  InjecPng	
  dependencies	
  into	
  classes,	
  instead	
  of	
  having	
  the	
  class	
...
Method Injection
class MapService {!
public function getLatLong(GoogleMaps $map, $street, $city, $state) {!
return $map->g...
Constructor Injection
class MapService {!
protected $map;!
public function __construct(GoogleMaps $map) {!
$this->map = $m...
Setter Injection
class MapService {!
protected $map;!
!
public function setMap(GoogleMaps $map) {!
$this->map = $map;!
}!
...
Single Responsibility Principle
MadisonPHP	
  2015	
   50	
  
Single Responsibility Principle
•  Every	
  class	
  should	
  have	
  a	
  single	
  responsibility,	
  and	
  that	
  re...
What is a Responsibility?
•  Responsibility	
  is	
  a	
  “Reason	
  To	
  Change”	
  –	
  Robert	
  C.	
  MarPn	
  
•  By...
An Example
/**
* Create a new invoice instance.
*
* @param LaravelCashierContractsBillable $billable
* @param object
* @re...
Why is this Bad?
•  This	
  single	
  class	
  has	
  the	
  following	
  responsibiliPes:	
  
•  GeneraPng	
  totals	
  f...
How to Improve
•  Change	
  responsibility	
  to	
  just	
  building	
  the	
  invoice	
  data	
  
•  Move	
  the	
  ‘outp...
Unit Testing
MadisonPHP	
  2015	
   56	
  
[Could	
  not	
  afford	
  licensing	
  fee	
  for	
  Grumpy	
  TesPng	
  Picture]	
  
MadisonPHP	
  2015	
   57	
  
This is not a testing talk
•  Using	
  Interfaces	
  makes	
  it	
  easier	
  to	
  mock	
  objects	
  
•  Reducing	
  cou...
Final Thoughts
MadisonPHP	
  2015	
   59	
  
We can make a dog with wheels!
•  Abstract	
  class	
  for	
  Animal	
  
•  Class	
  for	
  Dog	
  that	
  extends	
  Anim...
Here’s a cute dog instead
MadisonPHP	
  2015	
   61	
  
Additional Resources
•  Clean	
  Code	
  –	
  Robert	
  C.	
  MarPn	
  
•  PHP	
  Objects,	
  PaGerns,	
  and	
  PracPce	
...
Thank You!
hGp://ctankersley.com	
  
chris@ctankersley.com	
  
@dragonmantank	
  
	
  
hGps://joind.in/16010	
  
MadisonPH...
Photos
•  Slide	
  9	
  -­‐	
  hGp://bit.ly/1dkaoxS	
  
•  Slide	
  10	
  -­‐	
  hGp://bit.ly/1c4Gc8z	
  
•  Slide	
  11	
...
Upcoming SlideShare
Loading in …5
×

OOP Is More Than Cars and Dogs

363 views

Published on

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.)

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
363
On SlideShare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
14
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

OOP Is More Than Cars and Dogs

  1. 1. OOP is More Than Cars and Dogs Chris  Tankersley   MadisonPHP  2015   MadisonPHP  2015   1  
  2. 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. 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. 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; } }
  5. 5. Object <?php! ! $manager= new Manager();! // ^! // |! // `--- This is the Object   MadisonPHP  2015   5  
  6. 6. Why are we using OOP? MadisonPHP  2015   6  
  7. 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  
  8. 8. Getting OOP Right is Complicated MadisonPHP  2015   8  
  9. 9. MadisonPHP  2015   9  
  10. 10. MadisonPHP  2015   10  
  11. 11. MadisonPHP  2015   11  
  12. 12. MadisonPHP  2015   12  
  13. 13. MadisonPHP  2015   13  
  14. 14. MadisonPHP  2015   14  
  15. 15. Can a Dog have Wheels? MadisonPHP  2015   15  
  16. 16. Inheritance MadisonPHP  2015   16  
  17. 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. 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  
  19. 19. Where I Learned It MadisonPHP  2015   19  
  20. 20. Our Structure MadisonPHP  2015   20   Employee   Manager   ScienPst   Laborer  
  21. 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. 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. 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; } }
  24. 24. The Laborer Class MadisonPHP  2015   24   class Laborer extends Employee { }
  25. 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. 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. 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. 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. 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. 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. 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. 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  
  33. 33. Composition over Inheritance MadisonPHP  2015   33  
  34. 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. 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. 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. 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. 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. 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  
  40. 40. Taking Advantage of OOP MadisonPHP  2015   40  
  41. 41. Coupling MadisonPHP  2015   41  
  42. 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. 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  
  44. 44. Law of Demeter MadisonPHP  2015   44  
  45. 45. Dependency Injection MadisonPHP  2015   45  
  46. 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. 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. 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. 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  
  50. 50. Single Responsibility Principle MadisonPHP  2015   50  
  51. 51. Single Responsibility Principle •  Every  class  should  have  a  single  responsibility,  and  that  responsibility   should  be  encapsulated  in  that  class   MadisonPHP  2015   51  
  52. 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. 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. 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. 55. How to Improve •  Change  responsibility  to  just  building  the  invoice  data   •  Move  the  ‘output’  stuff  to  other  classes   MadisonPHP  2015   55  
  56. 56. Unit Testing MadisonPHP  2015   56  
  57. 57. [Could  not  afford  licensing  fee  for  Grumpy  TesPng  Picture]   MadisonPHP  2015   57  
  58. 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  
  59. 59. Final Thoughts MadisonPHP  2015   59  
  60. 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  
  61. 61. Here’s a cute dog instead MadisonPHP  2015   61  
  62. 62. Additional Resources •  Clean  Code  –  Robert  C.  MarPn   •  PHP  Objects,  PaGerns,  and  PracPce  –  MaG  Zandstra   MadisonPHP  2015   62  
  63. 63. Thank You! hGp://ctankersley.com   chris@ctankersley.com   @dragonmantank     hGps://joind.in/16010   MadisonPHP  2015   63  
  64. 64. Photos •  Slide  9  -­‐  hGp://bit.ly/1dkaoxS   •  Slide  10  -­‐  hGp://bit.ly/1c4Gc8z   •  Slide  11  -­‐  hGp://bit.ly/1R3isBp   •  Slide  12  -­‐  hGp://bit.ly/1ScEWRZ   •  Slide  13  -­‐  hGp://bit.ly/1Bc0qUv   •  Slide  14  -­‐  hGp://bit.ly/1ILhfNV   •  Slide  15  -­‐  hGp://bit.ly/1SeekA7     MadisonPHP  2015   64  

×