Advanced Oop And Design Patterns

  • June 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Advanced Oop And Design Patterns as PDF for free.

More details

  • Words: 3,255
  • Pages: 229
Advanced OOP and Design Patterns Stefan Priebsch thePHP.cc CodeWorks 09

Copyright © 2009 thePHP.cc, Germany

Premium PHP Consulting & Training. Worldwide.

Sebastian Bergmann

Arne Blankerts

Stefan Priebsch

Why OOP?

phpMyAdmin <?php echo PMA_VERSION; ?> <?php echo htmlspecialchars($HTTP_HOST); ?> <meta http-equiv="Content-Type" content="text/html; charset=" /> <meta name="robots" content="noindex,nofollow" /> <script type="text/javascript"> window.onload = function() { if (onloadCnt == 0) { if (typeof(onLoadHandler) == "function") { onLoadHandler(); } }; <script src="./js/common.js" type="text/javascript"> query($query,array((int) $postId)); $ret = array(); while ($res = $result->fetchRow()) { $imgId = $res['imgId']; $res['link'] = "image"; $parts = parse_url($_SERVER['REQUEST_URI']); $path = str_replace('tiki-blog_post.php', 'tikiview_blog_post_image.php', $parts['path']); $res['absolute'] = $tikilib->httpPrefix(). $path . "? imgId=$imgId"; $ret[] = $res; } return $ret; } ?> <noframes>



PHP

SQL

HTML

phpMyAdmin <?php echo PMA_VERSION; ?> <?php echo htmlspecialchars($HTTP_HOST); ?> <meta http-equiv="Content-Type" content="text/html; charset=" /> <meta name="robots" content="noindex,nofollow" /> <script type="text/javascript"> window.onload = function() { if (onloadCnt == 0) { if (typeof(onLoadHandler) == "function") { onLoadHandler(); } }; <script src="./js/common.js" type="text/javascript"> query($query,array((int) $postId)); $ret = array(); while ($res = $result->fetchRow()) { $imgId = $res['imgId']; $res['link'] = "image"; $parts = parse_url($_SERVER['REQUEST_URI']); $path = str_replace('tiki-blog_post.php', 'tikiview_blog_post_image.php', $parts['path']); $res['absolute'] = $tikilib->httpPrefix(). $path . "? imgId=$imgId"; $ret[] = $res; } return $ret; } ?> <noframes>



PHP

SQL

HTML

phpMyAdmin <?php echo PMA_VERSION; ?> <?php echo htmlspecialchars($HTTP_HOST); ?> <meta http-equiv="Content-Type" content="text/html; charset=" /> <meta name="robots" content="noindex,nofollow" /> <script type="text/javascript"> window.onload = function() { if (onloadCnt == 0) { if (typeof(onLoadHandler) == "function") { onLoadHandler(); } }; <script src="./js/common.js" type="text/javascript"> query($query,array((int) $postId)); $ret = array(); while ($res = $result->fetchRow()) { $imgId = $res['imgId']; $res['link'] = "image"; $parts = parse_url($_SERVER['REQUEST_URI']); $path = str_replace('tiki-blog_post.php', 'tikiview_blog_post_image.php', $parts['path']); $res['absolute'] = $tikilib->httpPrefix(). $path . "? imgId=$imgId"; $ret[] = $res; } return $ret; } ?> <noframes>



PHP

SQL

HTML

PHP

SQL

HTML

Presentation

Logic

Data Access

Separation of Concerns

Good OOP starts before classes come into play

Do we need OOP?

IF-GOTO programs

LOOP programs

WHILE programs

IF-GOTO and WHILE programs are equally powerful.

Every WHILE program can be simulated by a WHILE program with only one while loop.

OOP is syntactical sugar

Then Why OOP?

Readability

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); } } }

Encapsulation

Keeping Secrets

Interface and Implementation

$email->send(); $pdf->generate();

Maintainability

Relative Cost of a Bugfix

>150x

50x 20x 1x

5x

Req

Design

10x Code

DevT

AccT

Source: Barry Boehm: „EQUITY Keynote Address“, March 19th, 2007

Ops

Extensibility

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; }

Reusability

Only isolated and loosely coupled classes are reusable.

Maintainability

Is OOP slow?

foo() Test::foo() $test->foo()

3.09 µsec 3.26 µsec 3.12 µsec

$test = new Test(); $test->foo() 4.03 µsec Benchmark results are different on every system.

25% slower!

1 µsec

print ~ 10 file_get_contents() ~ 30 mysql_connect() ~ 100 HTTP GET Request ~ 35,000

µsec µsec µsec µsec

Benchmark results are different on every system.

I/O is where the action is.

OOP is fast enough.

Dealing with Dependencies

Decoupling

Dependencies on 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 dependency

class Something { public function __construct($data) { $this->data = $data; } }

Dependencies on Global 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 Injection

class Something { protected $dbAdapter; public function __construct(DatabaseAdapter $dbAdapter) { $this->dbAdapter = $dbAdapter; } public function loadData() { … ask somebody else to load data from database ... } }

Delegation

Let others 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 ... }

Dependencies on 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();

Dependencies on 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 $classname 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 have a getDsn() method?

Better let PHP enforce this.

Dependencies on 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 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 make the type hint work?

Interfaces

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 make good type hints.

Abstract Classes and 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 Method

class Something { abstract protected abstract protected abstract protected abstract protected

}

function function function function

public function run() { $this->initialize(); $this->calculateStuff(); $this->doLogging(); $this->sortOutput(); }

initialize(); calculateStuff(); doLogging(); sortOutput();

abstract class Something { abstract protected function abstract protected function abstract protected function abstract protected function

}

public function run() { $this->initialize(); $this->calculateStuff(); $this->doLogging(); $this->sortOutput(); }

initialize(); calculateStuff(); doLogging(); sortOutput();

abstract class Something { abstract protected function abstract protected function abstract protected function abstract protected function

}

final public function run() { $this->initialize(); $this->calculateStuff(); $this->doLogging(); $this->sortOutput(); }

initialize(); calculateStuff(); doLogging(); sortOutput();

class ConcreteSomething extends Something { protected function initialize() { } protected function calculateStuff() { } protected function doLogging() { }

}

protected function sortOutput() { }

Abstract classes cannot be instantiated.

„Every pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.“ -- Christopher Alexander

A design pattern is a general reusable solution to a commonly occurring problem.

A design pattern is not a finished design that can be transformed directly into code.

Architectural Patterns Design Patterns Idioms

Problem: display a progress bar

class Something { protected function performCalculation() { ... foreach ($items as $item) { … process the item … print '.'; } ... } }

Subject/Observer

The subject maintains a list of observers and notifies them on 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();

Factory

Defines an interface for creating a family of objects. The concrete classes that are to be instantiated are not clearly defined.

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; } }

Singleton

Limits number of instances of a class to one object.

Limits number of instances 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->... ... } }

Registry

A well-known object used by others to find shared 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; }

}

MVC

Classical

MVC

Model

Represents domain-specific data

View

Renders model data

Controller

Handles events and modifies model

Presentation

V

Logic

C

Data Access

M

Model != Data Access

Model != Data Access

Controller

View

Model

Controller observes View Controller

View

Model

Controller fetches data from View Controller

View

Model

Controller modifies Model Controller

View

Model

View observes Model Controller

View

Model

View fetches data from Model Controller

View

Model

MVC

on the Web

V

Client HTTP Request

HTTP Response Server

C

M

View is remote

Controller

View

Model

Controller can't observe View Controller

View

Model

View can't observe Model Controller

View

Model

View doesn't talk to Controller Front Controller

Controller

View

Model

Front Controller

Controller

HTTP Request

View

Model

Front Controller

execute()

Controller

HTTP Request

View

Model

Front Controller

execute()

Controller

HTTP Request

View

getData()

Model

Front Controller HTTP Request

View

execute()

Controller

() a t a tD e s

getData()

Model

Controller selects View

View generates full HTML page

Workflows by Controller chaining

Lean Controller, Fat Model

No data access in Controller or Model

No presentation in Controller or Model

Separation of Concerns

Golden Rules

„Hang the rules. They're more like guidelines anyway.“

--Elizabeth Swann, Pirates of the Caribbean

Make dependencies explicit.

„Life can only be understood backwards, backwards, but it must be lived forwards.“ forwards.“ -- Soren Kierkegaard

Solve your problem.

Do one thing at a time.

Let others do the work.

Do not care about others.

Keep it simple.

„Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.“ -- Brian W. Kernighan

Do not comment.

Do not * comment. *

Some restrictions apply.

”The most important single aspect of software development ... … is to be clear about what you are trying to build.” -- Bjarne Stroustrup

Name the thing.

„There's no sense being exact about something if you don't even know what you're talking about.“ -- John von Neumann

It has to fit on one page.

”Measuring programming progress by lines of code is like measuring aircraft building progress by weight.” weight.” -- Bill Gates

Accept Change.

”I may not have gone where I intended to go, but I think I have ended up where I needed to be.“ -- Douglas Adams

Thank you. Please rate me: http://joind.in

Contact ■

http://thePHP.cc



http://www.priebsch.de



http://www.slideshare.net/spriebsch



http://twitter.com/spriebsch



[email protected], IRC: spriebsch

Copyright © 2009 thePHP.cc, Germany

Related Documents

Design Patterns
June 2020 16
Design Patterns
May 2020 17
Design Patterns
May 2020 17
Design Patterns
November 2019 29