Upload
kaplumbaga
View
2.131
Download
2
Embed Size (px)
DESCRIPTION
Stefan Priebsch's slides from Codeworks 2009
Citation preview
Copyright © 2009 thePHP.cc, Germany
Advanced OOPand Design Patterns
Stefan PriebschthePHP.cc
CodeWorks 09
Premium PHP Consulting & Training. Worldwide.
ArneBlankerts
Sebastian Bergmann
StefanPriebsch
WhyWhy OOP?OOP?
<?php header('Content-Type: text/html; charset=' .$GLOBALS['charset']); ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_iso_code; ?>" lang="<?php echo $lang_iso_code; ?>" dir="<?php echo $GLOBALS['text_dir']; ?>"><head><link rel="icon" href="./favicon.ico" type="image/x-icon" /><title>phpMyAdmin <?php echo PMA_VERSION; ?> -<?php echo htmlspecialchars($HTTP_HOST); ?></title><meta http-equiv="Content-Type" content="text/html; charset=<?php echo $GLOBALS['charset']; ?>" /><meta name="robots" content="noindex,nofollow" /><script type="text/javascript">window.onload = function() { if (onloadCnt == 0) { if (typeof(onLoadHandler) == "function") { onLoadHandler(); } };</script><script src="./js/common.js" type="text/javascript"></script></head><?php $query = "select `postId`,`filename`,`filesize`,`imgId` from `tiki_blog_posts_images` where `postId`=?";$result = $this->query($query,array((int) $postId));$ret = array();while ($res = $result->fetchRow()) { $imgId = $res['imgId']; $res['link'] = "<img src='tiki-view_blog_post_image.php?imgId=$imgId' border='0' alt='image' />"; $parts = parse_url($_SERVER['REQUEST_URI']); $path = str_replace('tiki-blog_post.php', 'tiki-view_blog_post_image.php', $parts['path']); $res['absolute'] = $tikilib->httpPrefix(). $path . "?imgId=$imgId"; $ret[] = $res;} return $ret; } ?><?php if ($GLOBALS['text_dir'] === 'ltr') { ?><frame frameborder="0" id="frame_navigation" src="navigation.php<?php echo $url_query; ?>" name="frame_navigation" /><?php } ?><frame frameborder="0" id="frame_content" src="<?php echo $main_target; ?>" name="frame_content" /><?php if ($GLOBALS['text_dir'] === 'rtl') { ?><frame frameborder="0" id="frame_navigation" src="navigation.php<?php echo $url_query; ?>" name="frame_navigation" /><?php } ?><noframes><body><p><?php echo $GLOBALS['strNoFrames']; ?></p></body></noframes></frameset></html>
PHPPHP
HTMLHTML
SQLSQL
<?php header('Content-Type: text/html; charset=' .$GLOBALS['charset']); ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_iso_code; ?>" lang="<?php echo $lang_iso_code; ?>" dir="<?php echo $GLOBALS['text_dir']; ?>"><head><link rel="icon" href="./favicon.ico" type="image/x-icon" /><title>phpMyAdmin <?php echo PMA_VERSION; ?> -<?php echo htmlspecialchars($HTTP_HOST); ?></title><meta http-equiv="Content-Type" content="text/html; charset=<?php echo $GLOBALS['charset']; ?>" /><meta name="robots" content="noindex,nofollow" /><script type="text/javascript">window.onload = function() { if (onloadCnt == 0) { if (typeof(onLoadHandler) == "function") { onLoadHandler(); } };</script><script src="./js/common.js" type="text/javascript"></script></head><?php $query = "select `postId`,`filename`,`filesize`,`imgId` from `tiki_blog_posts_images` where `postId`=?";$result = $this->query($query,array((int) $postId));$ret = array();while ($res = $result->fetchRow()) { $imgId = $res['imgId']; $res['link'] = "<img src='tiki-view_blog_post_image.php?imgId=$imgId' border='0' alt='image' />"; $parts = parse_url($_SERVER['REQUEST_URI']); $path = str_replace('tiki-blog_post.php', 'tiki-view_blog_post_image.php', $parts['path']); $res['absolute'] = $tikilib->httpPrefix(). $path . "?imgId=$imgId"; $ret[] = $res;} return $ret; } ?><?php if ($GLOBALS['text_dir'] === 'ltr') { ?><frame frameborder="0" id="frame_navigation" src="navigation.php<?php echo $url_query; ?>" name="frame_navigation" /><?php } ?><frame frameborder="0" id="frame_content" src="<?php echo $main_target; ?>" name="frame_content" /><?php if ($GLOBALS['text_dir'] === 'rtl') { ?><frame frameborder="0" id="frame_navigation" src="navigation.php<?php echo $url_query; ?>" name="frame_navigation" /><?php } ?><noframes><body><p><?php echo $GLOBALS['strNoFrames']; ?></p></body></noframes></frameset></html>
PHPPHP
HTMLHTML
SQLSQL
<?php header('Content-Type: text/html; charset=' .$GLOBALS['charset']); ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $lang_iso_code; ?>" lang="<?php echo $lang_iso_code; ?>" dir="<?php echo $GLOBALS['text_dir']; ?>"><head><link rel="icon" href="./favicon.ico" type="image/x-icon" /><title>phpMyAdmin <?php echo PMA_VERSION; ?> -<?php echo htmlspecialchars($HTTP_HOST); ?></title><meta http-equiv="Content-Type" content="text/html; charset=<?php echo $GLOBALS['charset']; ?>" /><meta name="robots" content="noindex,nofollow" /><script type="text/javascript">window.onload = function() { if (onloadCnt == 0) { if (typeof(onLoadHandler) == "function") { onLoadHandler(); } };</script><script src="./js/common.js" type="text/javascript"></script></head><?php $query = "select `postId`,`filename`,`filesize`,`imgId` from `tiki_blog_posts_images` where `postId`=?";$result = $this->query($query,array((int) $postId));$ret = array();while ($res = $result->fetchRow()) { $imgId = $res['imgId']; $res['link'] = "<img src='tiki-view_blog_post_image.php?imgId=$imgId' border='0' alt='image' />"; $parts = parse_url($_SERVER['REQUEST_URI']); $path = str_replace('tiki-blog_post.php', 'tiki-view_blog_post_image.php', $parts['path']); $res['absolute'] = $tikilib->httpPrefix(). $path . "?imgId=$imgId"; $ret[] = $res;} return $ret; } ?><?php if ($GLOBALS['text_dir'] === 'ltr') { ?><frame frameborder="0" id="frame_navigation" src="navigation.php<?php echo $url_query; ?>" name="frame_navigation" /><?php } ?><frame frameborder="0" id="frame_content" src="<?php echo $main_target; ?>" name="frame_content" /><?php if ($GLOBALS['text_dir'] === 'rtl') { ?><frame frameborder="0" id="frame_navigation" src="navigation.php<?php echo $url_query; ?>" name="frame_navigation" /><?php } ?><noframes><body><p><?php echo $GLOBALS['strNoFrames']; ?></p></body></noframes></frameset></html>
PHPPHP
HTMLHTML
SQLSQL
PHPPHP
HTMLHTML
SQLSQL
PresentationPresentation
LogicLogic
Data AccessData Access
Separation ofSeparation ofConcernsConcerns
Good OOP starts Good OOP starts before classesbefore classescome into playcome into play
Do weDo weneed OOP?need OOP?
IF-GOTO programsIF-GOTO programs
LOOP programsLOOP programs
WHILE programsWHILE programs
IF-GOTO andIF-GOTO andWHILE programsWHILE programsare equally powerful.are equally powerful.
Every WHILE programEvery WHILE programcan be simulated by acan be simulated by aWHILE program withWHILE program withonly one while loop.only one while loop.
OOP isOOP issyntactical sugarsyntactical sugar
Then Why OOP?Then Why OOP?
ReadabilityReadability
class BlogPosting{ public function addComment(Comment $comment)}
class BlogPosting{ public function addComment(Comment $comment) { $this->comments[] = $comment; }}
class BlogPosting{ public function addComment(Comment $comment) { $this->comments[] = $comment;
if ($comment->getAuthor()->isRegistered()) { $comment->setApproved(true); } }}
EncapsulationEncapsulation
Keeping SecretsKeeping Secrets
InterfaceInterfaceand and
ImplementationImplementation
$email->send();$email->send();
$pdf->generate();$pdf->generate();
MaintainabilityMaintainability
Source: Barry Boehm: „EQUITY Keynote Address“, March 19th, 2007Source: Barry Boehm: „EQUITY Keynote Address“, March 19th, 2007
DesignDesign CodeCodeReqReq DevTDevT OpsOps
Rela
tive C
ost
of
a B
ug
fix
Rela
tive C
ost
of
a B
ug
fix
1x1x 5x5x10x10x
20x20x
50x50x
AccTAccT
>150x>150x
ExtensibilityExtensibility
class Customer{ protected $discount = 0;
public function getDiscount() { return $this->discount; }}
class PremiumCustomer extends Customer{ protected $discount = 5;}
class PremiumCustomer extends Customer{ protected $discount = 5;
public function getDiscount() { if ($this->signupYear < 2003) { return 10; }
if ($this->getTotalRevenue() > 100000) { return 10; }
return parent::getDiscount(); }}
class PremiumCustomer extends Customer{ protected $discount = 5;
public function getDiscount() { if ($this->fulfilsYearCriterion() || $this->fulfilsRevenueCriterion()) {
return 10; }
return parent::getDiscount(); }
...}
class PremiumCustomer extends Customer{ ...
protected function fulfilsYearCriterion() { return $this->signupYear < 2003; }
protected function fulfilsRevenueCriterion() { return $this->getTotalRevenue() > 100000; }
...}
class PremiumCustomer extends Customer{ protected $specialDiscount= 10;
public function getDiscount() { if ($this->fulfilsYearCriterion() || $this->fulfilsRevenueCriterion()) {
return $this->specialDiscount; }
return parent::getDiscount(); }}
class MostValuedCustomer extends PremiumCustomer{ protected $discount = 10; protected $specialDiscount= 20;}
ReusabilityReusability
Only isolated andOnly isolated andloosely coupledloosely coupled
classes are reusable.classes are reusable.
MaintainabilityMaintainability
Is OOP slow?Is OOP slow?
Benchmark results are different on every system.Benchmark results are different on every system.
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
Benchmark results are different on every system.Benchmark results are different on every system.
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 is fast OOP is fast enough.enough.
Dealing with Dealing with DependenciesDependencies
DecouplingDecoupling
DependenciesDependencieson Constantson Constants
define('BASEPATH', '/some/absolute/path');...
class Something{ public function loadData() { $this->data = file_get_contents(BASEPATH . '/file.ext'); }}
...$something = new Something();$something->loadData();
class Something{ protected $basePath = BASEPATH; public function loadData() { $this->data = file_get_contents($this->basePath . '/file.ext'); }}
class Something{ protected $basePath = BASEPATH; public function setBasePath($basePath) { $this->basePath = $basePath; }
public function loadData() { $this->data = file_get_contents($this->basePath . '/file.ext'); }}
$something = new Something();$something->setBasePath(BASEPATH);$something->loadData();
class Something{ protected $basePath; public function setBasePath($basePath) { $this->basePath = $basePath; }
public function loadData() { $this->data = file_get_contents($this->basePath . '/file.ext'); }}
class Something{ protected $basePath; public function setBasePath($basePath) { $this->basePath = $basePath; }
public function loadData() { if (is_null($this->basePath)) { throw new RuntimeException('No basepath set'); }
$this->data = file_get_contents($this->basePath . '/file.ext'); }}
class Something{ protected $basePath; public function __construct($basePath) { $this->basePath = $basePath; }
public function loadData() { if (is_null($this->basePath)) { throw new RuntimeException('No basepath set'); }
$this->data = file_get_contents($this->basePath . '/file.ext'); }}
Files are also a Files are also a dependencydependency
class Something{ public function __construct($data) { $this->data = $data; }}
Dependencies on Dependencies on Global VariablesGlobal Variables
class Something{ public function loadData() { global $dsn;
… load data from database ... }}
class Something{ public function loadData($dsn) { … load data from database ... }}
class Something{ protected $dsn;
public function __construct($dsn) { $this->dsn = $dsn; }
public function loadData() { … load data from database ... }}
class Something{ protected $dbAdapter;
public function __construct(DatabaseAdapter $dbAdapter) { $this->dbAdapter = $dbAdapter; }}
Dependency Dependency InjectionInjection
class Something{ protected $dbAdapter;
public function __construct(DatabaseAdapter $dbAdapter) { $this->dbAdapter = $dbAdapter; }
public function loadData() { … ask somebody else to load data from database ... }}
DelegationDelegation
Let othersLet othersdo the work.do the work.
class DatabaseAdapter{ protected $dsn;
public function __construct($dsn) { $this->dsn = $dsn; }}
$dbAdapter = new DatabaseAdapter();
$something = new Something($dbAdapter);$something->doWork();
class DummyDatabaseAdapter extends DatabaseAdapter{}
$dbAdapter = new DummyDatabaseAdapter();
$something = new Something($dbAdapter);$something->doWork();
class Something{ protected $dbAdapter;
public function __construct(DatabaseAdapter $dbAdapter = null) { if (is_null($dbAdapter)) { $dbAdapter = new DefaultDatabaseAdapter(); }
$this->dbAdapter = $dbAdapter; }
public function loadData() { … load data from database ... }}
DependenciesDependencieson Functionson Functions
class Something{ public function doWork() { ... global_function(...); ... }}
require_once 'global_function.php';
$something = new Something();$something->doWork();
require_once 'mock_global_function.php';
$something = new Something();$something->doWork();
DependenciesDependencieson Static Callson Static Calls
class Something{ public function loadData() { $dsn = Configuration::getDsn();
… load data from database ... }}
class Something{ protected $configurationClass = 'Configuration';
public function setConfigurationClass($classname) { $this->configurationClass = $classname }
public function loadData() { $classname = $this->configurationClass; $db = $classname::getDsn();
… load data from database ... }}
??
class Something{ protected $configurationClass = 'Configuration';
public function setConfigurationClass($classname) { $this->configurationClass = $classname }
public function loadData() { $classname = $this->configurationClass; $db = $classname::getDsn();
… load data from database ... }}
Will $classnameWill $classnameactually be a class?actually be a class?
class Something{ protected $configurationClass = 'Configuration';
public function setConfigurationClass($classname) { $this->configurationClass = $classname }
public function loadData() { $classname = $this->configurationClass; $db = $classname::getDsn();
… load data from database ... }}
Will the class haveWill the class havea getDsn() method?a getDsn() method?
Better let PHP Better let PHP enforce this.enforce this.
DependenciesDependencieson Objectson Objects
class Something{ public function doWork() { ... $this->log('…'); ... }}
class Something{ protected function log($message) { file_put_contents(LOGFILE, $message, FILE_APPEND); }
public function doWork() { ... $this->log('…'); ... }}
class Logger{ public function log($message) { file_put_contents(LOGFILE, $message, FILE_APPEND); }}
class Something{ protected $logger;
public function __construct() { $this->logger = new Logger(); }
...}
class Something{ protected $logger;
public function __construct() { $this->logger = new Logger(); }
public function doWork() { … $this->logger->log(...); ... }}
class Something{ protected $logger;
public function __construct(Logger $logger) { $this->logger = $logger; }
public function doWork() { … $this->logger->log(...); ... }}
class Logger{ public function log($message) { file_put_contents(LOGFILE, $message, FILE_APPEND); }}
class Logger{ protected $file;
public function __construct($file) { $this->file = $file; }
public function log($message) { ... file_put_contents($this->file, $message, FILE_APPEND); ... }}
class DatabaseLogger extends Logger{ protected $dsn;
public function __construct($dsn) { $this->dsn = $dsn; }
public function log($message) { … log $message to database $dsn ... }}
class DbGateway{ protected $dsn;
public function __construct($dsn) { $this->dsn = $dsn; }
...}
class DbGateway{ protected $isConnected = false;
...
protected function connect() { … connect to $dsn ... }
public function query($query) { if (!$this->isConnected) { $this->connect(); }
… run $query ... }
...}
class DatabaseLogger extends Logger extends DbGateway{ ...}
Multiple Inheritance
??
class Something{ public function __construct(Logger $logger) { $this->logger = $logger; }
public function doWork() { ... $this->logger->log(...); ... }}
Do we really need to Do we really need to extend Logger?extend Logger?
class FileLogger{ protected $file;
public function __construct($file) { $this->file = $file; }
public function log($message) { ... file_put_contents($this->file, $message, FILE_APPEND); ... }}
class DatabaseLogger extends DbGateway{ public function log($message) { ... … write $message to database using $this->query() … ... }}
class Something{ public function __construct(Logger $logger) { $this->logger = $logger; }
public function doWork() { ... $this->logger->log(...); ... }}
How do we makeHow do we makethe type hint work?the type hint work?
InterfacesInterfaces
interface Loggable{ public function log($message);}
class FileLogger implements Loggable{ protected $file;
public function __construct($file) { $this->file = $file; }
public function log($message) { ... file_put_contents($this->file, $message, FILE_APPEND); ... }}
class DatabaseLogger extends DbGateway implements Loggable{ public function log($message) { ... … write $message to database using $this->query() … ... }}
class Something{ public function __construct(Loggable $logger) { $this->logger = $logger; }
protected function doWork() { ... $this->logger->log(...); ... }}
$logger = new FileLogger('/path/to/logfile');
$something = new Something($logger);$something->doWork();
$logger = new DatabaseLogger($dsn);
$something = new Something($logger);$something->doWork();
$logger = new DummyLogger();
$something = new Something($logger);$something->doWork();
class DummyLogger implements Loggable{ public function log($message) { }}
Interfaces makeInterfaces makegood type hints.good type hints.
Abstract Classes Abstract Classes and Methodsand Methods
class Something{ public function run() { // initialize …
// perform calculations …
// write log entry …
// sort output ... }}
class Something{ public function run() { $this->initialize(); $this->calculateStuff(); $this->doLogging(); $this->sortOutput(); }}
Template MethodTemplate Method
class Something{ abstract protected function initialize(); abstract protected function calculateStuff(); abstract protected function doLogging(); abstract protected function sortOutput();
public function run() { $this->initialize(); $this->calculateStuff(); $this->doLogging(); $this->sortOutput(); }}
abstract class Something{ abstract protected function initialize(); abstract protected function calculateStuff(); abstract protected function doLogging(); abstract protected function sortOutput();
public function run() { $this->initialize(); $this->calculateStuff(); $this->doLogging(); $this->sortOutput(); }}
abstract class Something{ abstract protected function initialize(); abstract protected function calculateStuff(); abstract protected function doLogging(); abstract protected function sortOutput();
final public function run() { $this->initialize(); $this->calculateStuff(); $this->doLogging(); $this->sortOutput(); }}
class ConcreteSomething extends Something{ protected function initialize() { }
protected function calculateStuff() { }
protected function doLogging() { }
protected function sortOutput() { }}
Abstract classes Abstract classes cannot be instantiated.cannot be instantiated.
„„Every pattern describes a problemEvery pattern describes a problemwhich occurs over and over again inwhich occurs over and over again in
our environment, and then describes our environment, and then describesthe core of the solution to that problem,the core of the solution to that problem,
in such a way that you can use thisin such a way that you can use thissolution a million times over, withoutsolution a million times over, withoutever doing it the same way twice.“ever doing it the same way twice.“
-- Christopher Alexander -- Christopher Alexander
A design patternA design patternis a general reusableis a general reusable
solution to a commonly solution to a commonly occurring problem.occurring problem.
A design pattern is not a A design pattern is not a finished design that can be finished design that can be
transformed directly into code.transformed directly into code.
ArchitecturalArchitecturalPatternsPatterns
DesignDesignPatternsPatterns
IdiomsIdioms
Problem: displayProblem: displaya progress bara progress bar
class Something{ protected function performCalculation() { ... foreach ($items as $item) { … process the item … print '.'; } ... }}
Subject/ObserverSubject/Observer
The subject maintains a list of The subject maintains a list of observers and notifies them on observers and notifies them on
status changes.status changes.
interface SplSubject{ public function attach(SplObserver $observer); public function detach(SplObserver $observer);
public function notify();}
interface SplObserver{ public function update(SplSubject $subject);}
class Subject implements SplSubject{ protected $observers;
public function __construct() { $this->observers = new SplObjectStorage(); }
...}
class Subject implements SplSubject{ …
public function attach(SplObserver $observer) { $observers->attach($observer); } public function detach(SplObserver $observer) { $observers->detach($observer); }
...}
class Subject implements SplSubject{ ...
public function notify() { foreach ($this->observers as $observer) { $observer->update($this); } }}
class Something extends Subject implements SplSubject{ public function doWork() { … foreach ($items as $item) { … $this->notify(); } ... }}
class ProgressBar implements SplObserver{ public function update(SplSubject $subject) { print '. '; }}
class Something extends Subject implements SplSubject{ public function doWork() { … foreach ($items as $item) { $this->currentItem = $item; … $this->notify(); } ... }
public function getCurrentItem() { return $this->currentItem() }}
class ProgressBar implements SplObserver{ public function update(SplSubject $subject) { $item = $subject->getCurrentItem(); … }}
class ProgressBar implements SplObserver{ public function update(SplSubject $subject) { $item = $subject->getCurrentItem();
if ($item->isDir()) { print '[' . $item->getPathname() . '] '; }
if ($item->isFile()) { print '.'; } }}
$progressBar = new ProgressBar();
$something = new Something();$something->attach($progressBar);
$something->doWork();
FactoryFactory
Defines an interface for creatingDefines an interface for creatinga family of objects. The concretea family of objects. The concrete
classes that are to be instantiated classes that are to be instantiated are not clearly defined.are not clearly defined.
Creates objects for you.Creates objects for you.
abstract class Plane{ static public function getInstance($type) { return new $type; }}
class Boeing extends Plane{}
class Airbus extends Plane{}
class Boeing777 extends Boeing{}
class AirbusA340 extends Airbus{}
$a = Plane::getInstance('Boeing777');
$b = Plane::getInstance('AirbusA340');
abstract class Plane{ static public function getInstance($type) { switch ($type) { case 'Boeing777': return new Boeing777(); … default:
throw new RuntimeException('...'); } }}
abstract class Plane{ static public function getInstance($type) { if (!in_array($type, array('Boeing777', 'AirbusA340', …))) { throw new RuntimeException('...'); }
return new $type; }}
abstract class Plane{ static public function getInstance($type) { if (!class_exists($type)) { throw new RuntimeException('...'); }
return new $type; }}
abstract class Plane{ protected $classMap = array( '777' => 'Boeing777', ... );
static public function getInstance($type) { if (!isset($this->classMap[$type])) { throw new RuntimeException('...'); }
$classname = $this->classMap[$type]; return new $classname; }}
SingletonSingleton
Limits number of instancesLimits number of instancesof a class to one object.of a class to one object.
Limits number of instancesLimits number of instancesof a class to one object.of a class to one object.
class Singleton{ static protected $instance;
static public function getInstance() { if (is_null(self::$instance)) { self::$instance = new Singleton(); }
return self::$instance; }}
class Singleton{ static protected $instance;
private function __construct() {} private function __clone() {}
static public function getInstance() { if (is_null(self::$instance)) { self::$instance = new Singleton(); }
return self::$instance; }}
class Something{ public function doWork() { $singleton = Singleton::getInstance(); $singleton->... ... }}
class Something{ public function doWork($reference) { $reference->... ... }}
RegistryRegistry
A well-known object usedA well-known object usedby others to find sharedby others to find shared
objects and services.objects and services.
…$db = StaticRegistry::getDbAdapter();...
class StaticRegistry{ static protected $dbAdapter;
static public function getDbAdapter() { if (is_null(self::$dbAdapter)) { self::$dbAdapter = new DbAdapter(); }
return self::$dbAdapter; }}
…$db = Registry::getInstance()->getDbAdapter();...
class SingletonRegistry{ static protected $instance;
protected $dbAdapter;
static public function getInstance() { … }
public function getDbAdapter() { if (is_null($this->dbAdapter)) { $this->dbAdapter = new DbAdapter(); }
return $this->dbAdapter; }}
class Registry{ …
protected $dbAdapterClass = 'DbAdapter'; protected $dbAdapter;
public function setDbAdapterClass($class) { $this->dbAdapterClass = $class; }
public function getDbAdapter() { if (is_null($this->dbAdapter)) { $classname = $this->dbAdapterClass; $this->dbAdapter = new $classname; }
return $this->dbAdapter; }}
MVCMVC
ClassicalClassical
MVCMVC
ModelModel
RepresentsRepresentsdomain-specific datadomain-specific data
View View
RendersRendersmodel datamodel data
ControllerController
Handles eventsHandles eventsand modifies modeland modifies model
PresentationPresentation
LogicLogic
Data AccessData Access MM
VV
CC
Model != Data AccessModel != Data Access
Model != Data AccessModel != Data Access
ControllerController
ModelModelViewView
ControllerController
ModelModelViewView
Controller observes ViewController observes View
ControllerController
ModelModelViewView
Controller fetches data from ViewController fetches data from View
ControllerController
ModelModelViewView
Controller modifies ModelController modifies Model
ControllerController
ModelModelViewView
View observes ModelView observes Model
ControllerController
ModelModelViewView
View fetches data from ModelView fetches data from Model
MVCMVCon the Webon the Web
ServerServer
ClientClient
HTTP RequestHTTP Request HTTP ResponseHTTP Response
VV
CC MM
View is View is remoteremote
ControllerController
ModelModelViewView
ControllerController
ModelModelViewView
Controller can't observe ViewController can't observe View
ControllerController
ModelModelViewView
View can't observe ModelView can't observe Model
ControllerController
ModelModelViewView
FrontFrontControllerController
View doesn't talk to ControllerView doesn't talk to Controller
ControllerController
ModelModelViewView
FrontFrontControllerController
HTTPHTTPRequestRequest
ControllerController
ModelModelViewView
FrontFrontControllerController
HTTPHTTPRequestRequest
execute()execute()
ControllerController
ModelModelViewView
FrontFrontControllerController
HTTPHTTPRequestRequest
execute()execute()
getData()getData()
ControllerController
ModelModelViewView
FrontFrontControllerController
HTTPHTTPRequestRequest
execute()execute()
getData()getData()
setData()
setData()
ControllerControllerselects Viewselects View
View generatesView generatesfull HTML pagefull HTML page
Workflows by Workflows by Controller chainingController chaining
Lean Controller, Lean Controller, Fat ModelFat Model
No data accessNo data accessin Controller or Modelin Controller or Model
No presentationNo presentationin Controller or Modelin Controller or Model
Separation ofSeparation ofConcernsConcerns
Golden Golden RulesRules
„„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
Make Make dependencies dependencies
explicit.explicit.
„„Life can only beLife can only beunderstoodunderstood backwardsbackwards,,but it must be but it must be livedlivedforwardsforwards.“.“
-- Soren Kierkegaard-- Soren Kierkegaard
Solve your Solve your problem.problem.
Do one thingDo one thingat a time.at a time.
Let othersLet othersdo the work.do the work.
Do not care Do not care about others.about others.
Keep itKeep itsimple.simple.
„„Debugging is twice as hardDebugging is twice as hardas writing the code in theas writing the code in thefirst place. Therefore, if youfirst place. Therefore, if youwrite the code as cleverly as write the code as cleverly as possible, you are, by definition, possible, you are, by definition, not smart enoughnot smart enoughto debug it.“to debug it.“
-- Brian W. Kernighan-- Brian W. Kernighan
Do notDo notcomment.comment.
Do notDo notcomment.comment.**
** Some restrictions apply. Some restrictions apply.
””The most important The most important single aspect of software single aspect of software development ...development ...
… is to be … is to be clearclear about about what you are trying to what you are trying to build.”build.”
-- Bjarne Stroustrup-- Bjarne Stroustrup
Name the thing.Name the thing.
„„There's no sense beingThere's no sense beingexact about somethingexact about somethingif you don't even knowif you don't even know
what you're talking about.“what you're talking about.“-- John von Neumann-- John von Neumann
It has to fitIt has to fiton one page.on one page.
””Measuring programming Measuring programming progress by lines of codeprogress by lines of codeis like measuring aircraft is like measuring aircraft building progress by building progress by weightweight.”.”
-- Bill Gates-- Bill Gates
Accept Change.Accept Change.
””I may not have gone I may not have gone where I intended to go, where I intended to go, but I think I have ended but I think I have ended up where I needed to be.“up where I needed to be.“
-- Douglas Adams-- Douglas Adams
Thank you.Thank you.
Please rate me:Please rate me:http://joind.inhttp://joind.in
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