Seven StepsSeven Stepsto Better OOP Code to Better OOP Code
Stefan PriebschStefan Priebsch
thePHP.ccthePHP.cc
ZendCon09ZendCon09
Stefan PriebschStefan PriebschCo-Founder and Principal ConsultantCo-Founder and Principal Consultant
Premium PHP Consulting & Training. Worldwide.
ArneBlankerts
Sebastian Bergmann
StefanPriebsch
DisclaimerDisclaimer
„„Hang the rules. Hang the rules. They're more like They're more like guidelines anyway.“guidelines anyway.“
--Elizabeth Swann,--Elizabeth Swann,Pirates of the CaribbeanPirates of the Caribbean
Is OOP slow?Is OOP slow?
The usual disclaimer for benchmarks applies!The usual disclaimer for benchmarks applies!
foo() foo() 3.09 µsec3.09 µsec
Test::foo() 3.26 µsec
$test->foo() 3.12 µsec
$test = new Test();$test->foo() 4.03 µsec
25% slower!25% slower!
1 µsec1 µsec
The usual disclaimer for benchmarks applies!The usual disclaimer for benchmarks applies!
print ~ 10 µsec
file_get_contents() ~ 30 µsec
mysql_connect() ~ 100 µsec
HTTP GET Request ~ 35,000 µsec
I/O is whereI/O is wherethe action is.the action is.
OOP isOOP isfast enough.fast enough.
Do not care about Do not care about performanceperformance**
*Some restrictions may apply.*Some restrictions may apply.
#1#1
WhatWhatdo youdo youdo for ado for aliving?living?
class Something{ public function doWork() { … load data …
… perform calculations …
… render HTML output …
… store calculation result ... }}
class Something{ public function doManyThings() { $this->loadData(); $this->performCalculations(); $this->renderHtml(); $this->storeResult(); }
protected function loadData() protected function performCalulations() protected function renderHtml() protected function storeResult()}
class DataLoader{ public function loadData()}
class Calculator{ public function calculateResult()}
class HtmlRenderer{ public function render()}
class StorageManager{ public function storeResult()}
Clearly separate Clearly separate different concernsdifferent concerns#2#2
One ResponsibilityOne Responsibility
class SomeObject{ protected function loadData() { $this->data = $this->db->query(...); }
public function render() { $this->loadData(); return HtmlRenderer::createTable($this->data); }}
Let othersLet othersdo the work.do the work.
class CrystalBall{ public function predictLottoNumbers($a, $b, $c) { …
return new LottoNumbers(...); }}
Focus on the APIFocus on the API#3#3
Interface vs. Interface vs. ImplementationImplementation
Keep secretsKeep secrets
class Person{ protected function talk() { Stranger::askForACigarette(); }}
class Person{ protected function talk() { Stranger::getInstance()->askForACigarette(); }}
class Person{ protected function talkTo(Friend $friend) { $friend->askForACigarette(); }}
class Friend{ public function askForACigarette() { return new Cigarette(); }}
Do not talkDo not talkto strangers.to strangers.
Create loosely Create loosely coupled classescoupled classes#4#4
Make Make dependencies dependencies
explicit.explicit.
class SomeObject{ protected function loadData() { $this->data = $this->db->query(...); }
public function render() { $this->loadData(); return HtmlRenderer::createTable($this->data); }}
class SomeObject{ protected function loadData() { $this->data = $this->db->query(...); }
public function render(Renderer $renderer) { $this->loadData(); return $this->renderer->createTable($this->data); }}
class SomeObject{ protected function loadData(DbGateway $db) { $this->data = $db->query(...); }
public function render(Renderer $renderer) { $this->loadData(); return $renderer->createTable($this->data); }}
class SomeObject{ public function __construct(DbGateway $db, Renderer $r) { $this->db = $db; $this->renderer = $r; }
...}
Use dependency Use dependency injectioninjection#5#5
class Engine{ public function start(); public functoin stop(); public function goFaster($amount); public function goSlower($amount);}
class Car extends Engine{ ...}
class SteeringWheel{ public function turnRight($degrees); public function turnLeft($degree);}
class Car extends Engine extends SteeringWheel{ ...}
Multiple inheritance?Multiple inheritance?
EngineEngine
EngineEngine++
Steering WheelSteering Wheel
EngineEngine++
Steering WheelSteering Wheel++
SeatSeat
CombineCombineobjects.objects.
CarCar
EngineEngine
SeatSeat
SteeringSteeringWheelWheel
class Car{ protected $engine; protected $steeringWheel; protected $frontSeat;
public function __construct() { $this->engine = new Engine(); $this->steeringWheel = new SteeringWheel(); $this->frontSeat = new Seat(); }}
class Car{ public function __construct(Engine $engine, SteeringWheel $steeringWheel, Seat $seat) { $this->engine = $engine; $this->steeringWheel = $steeringWheel; $this->frontSeat = $seat; }}
$engine = new Engine();$steeringWheel = new SteeringWheel();$seat = new Seat();
$car = new Car($engine, $steeringWheel, $seat);
$engine = new SuperStrongEngine();$steeringWheel = new FancySteeringWheel();$seat = new MichaelSchuhmacherRacingSeat();
$car = new Car($engine, $steeringWheel, $seat);
Favour composition Favour composition over inheritanceover inheritance#6#6
AvoidAvoidinheritance.inheritance.
SuperglobalsSuperglobalsConstantsConstants
SessionSession
ConfigurationConfigurationDatabaseDatabase
WebserviceWebservice
Class to TestClass to Test
If it's not tested,If it's not tested,it does not existit does not exist
Make it easyMake it easyto (unit) testto (unit) test#7#7
Fewer Fewer dependencies.dependencies.
TestableTestable==
MaintainableMaintainable
TestableTestable==
ExtensibleExtensible
Thank you.Thank you.
Copyright © 2009 thePHP.cc, Germany
Contact■ http://thePHP.cc■ http://www.priebsch.de■ http://www.slideshare.net/spriebsch■ http://twitter.com/spriebsch■ [email protected], IRC: spriebsch