Seven Steps
Seven Steps
to Better OOP Code
to Better OOP Code
Stefan Priebsch
Stefan Priebsch
thePHP.cc
thePHP.cc
ZendCon09
ZendCon09
Stefan Priebsch
Stefan Priebsch
Co-Founder and Principal Consultant
Co-Founder and Principal Consultant
Premium PHP Consulting & Training. Worldwide.
Arne
Blankerts
Sebastian
Bergmann
Stefan
Priebsch
Disclaimer
Disclaimer
„
„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 Caribbean
Pirates 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 µsec
3.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 µsec
1 µ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 where
I/O is where
the action is.
the action is.
OOP is
OOP is
fast enough.
fast enough.
Do not care about
Do not care about
performance
performance*
*
*Some restrictions may apply.
*Some restrictions may apply.
#1
#1
What
What
do you
do you
do for a
do for a
living?
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 concerns
different concerns
#2
#2
One Responsibility
One Responsibility
class SomeObject
{
protected function loadData()
{
$this->data = $this->db->query(...);
}
public function render()
{
$this->loadData();
return HtmlRenderer::createTable($this->data);
}
}
Let others
Let others
do the work.
do the work.
class CrystalBall
{
public function predictLottoNumbers($a, $b, $c)
{
…
return new LottoNumbers(...);
}
}
Focus on the API
Focus on the API
#3
#3
Interface vs.
Interface vs.
Implementation
Implementation
Keep secrets
Keep 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 talk
Do not talk
to strangers.
to strangers.
Create loosely
Create loosely
coupled classes
coupled 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
injection
injection
#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?
Engine
Engine
Engine
Engine
+
+
Steering Wheel
Steering Wheel
Engine
Engine
+
+
Steering Wheel
Steering Wheel
+
+
Seat
Seat
Combine
Combine
objects.
objects.
Car
Car
Engine
Engine
Seat
Seat
Steering
Steering
Wheel
Wheel
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 inheritance
over inheritance
#6
#6
Avoid
Avoid
inheritance.
inheritance.
Superglobals
Superglobals
Constants
Constants
Session
Session
Configuration
Configuration
Database
Database
Webservice
Webservice
Class to Test
Class to Test
If it's not tested,
If it's not tested,
it does not exist
it does not exist
Make it easy
Make it easy
to (unit) test
to (unit) test
#7
#7
Fewer
Fewer
dependencies.
dependencies.
Testable
Testable
=
=
Maintainable
Maintainable
Testable
Testable
=
=
Extensible
Extensible
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
■ stefan@thePHP.cc, IRC: spriebsch
Seven Steps to Better PHP Code

Seven Steps to Better PHP Code