Share
Embed
Separation of concerns - DPC 12
Joshua Thijssen
June 08, 2012
Transcript
62 slides
-
Separation of concerns About Joshua Joshua Thijssen, NoxLogic /
Techademy Freelance Consultant, Developer, Trainer Development in PHP, Python, Perl, C, Java [email protected] @jaytaph -
Separation of concerns About Stephan Stephan Hochdörfer, bitExpert AG
Department Manager Research Labs enjoying PHP since 1999 [email protected] @shochdoerfer -
Separation of concerns It is what I sometimes have called
the separation of concerns, which [...] is yet the only available technique for effective ordering of one's thoughts. Edsger W. Dijkstra, "On the role of scientific thought" (1974) -
Separation of concerns SoC […] is the process of breaking
a computer program into distinct features that overlap in functionality as little as possible. http://en.wikipedia.org/wiki/Separation_of_concerns -
Separation of concerns <?php $items = array(); $token = file_get_contents("https://graph.facebook.com/oauth/access_token?...");
$feed = file_get_contents("https://graph.facebook.com/ident/feed?".$token); $feed = json_decode($feed, true); foreach($feed['data'] as $post) { if('123456789012' === $post['from']['id']) { $items[] = array( 'date' => strtotime($post['created_time']), 'message' => $post['message']); } } $feed = file_get_contents("http://search.twitter.com/search.json?q=from:ident"); $feed = json_decode($feed, true); foreach($feed['results'] as $tweet) { $items[] = array( 'date' => strtotime($tweet['created_at']), 'message' => $tweet['text']); } foreach($items as $item) { echo $item['message']; } -
Separation of concerns <?php $items = array(); $token = file_get_contents("https://graph.facebook.com/oauth/access_token?...");
$feed = file_get_contents("https://graph.facebook.com/ident/feed?".$token); $feed = json_decode($feed, true); foreach($feed['data'] as $post) { if('123456789012' === $post['from']['id']) { $items[] = array( 'date' => strtotime($post['created_time']), 'message' => $post['message']); } } $feed = file_get_contents("http://search.twitter.com/search.json?q=from:ident"); $feed = json_decode($feed, true); foreach($feed['results'] as $tweet) { $items[] = array( 'date' => strtotime($tweet['created_at']), 'message' => $tweet['text']); } foreach($items as $item) { echo $item['message']; }} Controller part! -
Separation of concerns <?php $items = array(); $token = file_get_contents("https://graph.facebook.com/oauth/access_token?...");
$feed = file_get_contents("https://graph.facebook.com/ident/feed?".$token); $feed = json_decode($feed, true); foreach($feed['data'] as $post) { if('123456789012' === $post['from']['id']) { $items[] = array( 'date' => strtotime($post['created_time']), 'message' => $post['message']); } } $feed = file_get_contents("http://search.twitter.com/search.json?q=from:ident"); $feed = json_decode($feed, true); foreach($feed['results'] as $tweet) { $items[] = array( 'date' => strtotime($tweet['created_at']), 'message' => $tweet['text']); } foreach($items as $item) { echo $item['message']; } View part! -
Separation of concerns What are the boundaries? Presentation Layer Business
Layer Resource Access Layer -
Separation of concerns <?php namespace Acme\DemoBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; class DemoController extends Controller { /** * @Route("/", name="_demo") * @Template() */ public function indexAction() { $items = array(); $token = file_get_contents("https://graph.facebook.com/oauth/access_to..."); $feed = file_get_contents("https://graph.facebook.com/ident/feed?".$token); $feed = json_decode($feed, true); foreach($feed['data'] as $post) { if('123456789012' === $post['from']['id']) { $items[] = array( 'date' => strtotime($post['created_time']), 'message' => $post['message']); } } -
Separation of concerns $feed = file_get_contents("http://search.twitter.com/search.json?..."); $feed = json_decode($feed, true);
foreach($feed['results'] as $tweet) { $items[] = array( 'date' => strtotime($tweet['created_at']), 'message' => $tweet['text']); } return array('items' => $items); } } -
Separation of concerns <?php namespace Acme\DemoBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; class DemoController extends Controller { /** * @Route("/", name="_demo") * @Template() */ public function indexAction() { $items = array(); $token = file_get_contents("https://graph.facebook.com/oauth/access_to..."); $feed = file_get_contents("https://graph.facebook.com/ident/feed?".$token); $feed = json_decode($feed, true); foreach($feed['data'] as $post) { if('123456789012' === $post['from']['id']) { $items[] = array( 'date' => strtotime($post['created_time']), 'message' => $post['message']); } } Facebook connector -
Separation of concerns $feed = file_get_contents("http://search.twitter.com/search.json?..." ); $feed = json_decode($feed,
true); foreach($feed['results'] as $tweet) { $items[] = array( 'date' => strtotime($tweet['created_at']), 'message' => $tweet['text']); } return array('items' => $items); } } Twitter connector -
Separation of concerns <?php interface ConnectorInterface { /** * Will
return an array of the latest posts. * return array */ public function getLatestPosts() { } } -
Separation of concerns <?php class FacebookConnector implements ConnectorInterface { protected
$handle; public function __construct($handle) { $this->handle = $handle; } public function getLatestPosts() { $items = array(); $token = file_get_contents("https://graph.facebook.com/oauth/access..."); $feed = file_get_contents("https://graph.facebook.com/. $this->handle."/feed?".$token); $feed = json_decode($feed, true); foreach($feed['data'] as $post) { if('123456789012' === $post['from']['id']) { $items[] = array( 'date' => strtotime($post['created_time']), 'message' => $post['message']); } } return $items; } } -
Separation of concerns <?php class FacebookConnector implements ConnectorInterface { protected
$handle; public function __construct($handle) { $this->handle = $handle; } public function getLatestPosts() { $token = $this->getAccessToken(); return $this->readPosts($token); } protected function getAccessToken() { return file_get_contents("https://graph.facebook.com/oauth/access_?..."); } protected function readPosts($token) { // read the post, filter all relevant and return them... } } -
Separation of concerns <?php class FacebookConnectorV2 extends FacebookConnector { protected
function getAccessToken() { return md5($this->handle); } } Easy to extend, will not influence the behaviour of other methods! -
Separation of concerns <?php namespace Acme\DemoBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; class DemoController extends Controller { /** * @Route("/", name="_demo") * @Template() */ public function indexAction() { $items = array(); $fb = $this->get('FbConnector'); $items += $fb->getLatestPosts(); $feed = file_get_contents("http://search.twitter.com/search.json?..."); $feed = json_decode($feed, true); foreach($feed['results'] as $tweet) { $items[] = array( 'date' => strtotime($tweet['created_at']), 'message' => $tweet['text']); } -
Separation of concerns <?php namespace Acme\DemoBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; class DemoController extends Controller { /** * @Route("/", name="_demo") * @Template() */ public function indexAction() { $items = array(); $fb = $this->get('FbConnector'); $items += $fb->getLatestPosts(); $twitter = $this->get('TwitterConnector'); $items += $twitter->getLatestPosts(); return array('items' => $items); } } -
The value of separation 1. Getting rid of code duplication
Separation of concerns -
The value of separation 2. Application becomes more stable and
easier to understand Separation of concerns -
The value of separation 3. Single responsibility offers clear extension
points Separation of concerns -
The value of separation 4. Increases the reusability of components
Separation of concerns -
Separation of concerns How to separate? Horizontal Separation Presentation Layer
Business Layer Resource Access Layer -
Separation of concerns How to separate? Horizontal Separation ./symfony |-app
|---cache |---config |---Resources |-src |---Acme |-----DemoBundle |-------Controller |-------Resources |---------config |---------public |-----------css |-----------images |---------views |-----------Demo |-----------Welcome |-------Tests |---------Controller |-web -
Separation of concerns How to separate? Service Separation Service Interface
Layer Business Layer Resource Access Layer -
Separation of concerns How to separate? Service Separation Frontend /
UI Webservers REST API Datastore Solr Search Service -
Separation of concerns How to separate? Vertical Separation Module A
Module B Module C -
Separation of concerns How to separate? Vertical Separation ./symfony |-app
|---cache |---config |---Resources |-src |---Acme |-----AdminBundle |-------Controller |-------Resources |-------Tests |-----ProductsBundle |-------Controller |-------Resources |-------Tests |-----CustomersBundle |-------Controller |-------Resources |-------Tests |-web -
Separation of concerns How to separate? Vertical Separation Presentation Layer
Business Layer Resource Access Layer Presentation Layer Business Layer Resource Access Layer Presentation Layer Business Layer Resource Access Layer -
Separation of concerns How to separate? Vertical Separation ./symfony |-app
|---cache |---config |---Resources |-src |---Acme |-----AdminBundle |-------Controller |-------Resources |-------Tests |-----ProductsBundle |-------Controller |-------Resources |-------Tests |-----CustomersBundle |-------Controller |-------Resources |-------Tests |-web -
Cross-cutting concerns? How to deal with security or logging aspects?
Separation of concerns -
Separation of concerns How to separate? Aspect Separation Presentation Layer
Business Layer Resource Access Layer Aspects -
Separation of concerns How to separate? Aspect Separation <?php namespace
Acme\Products\Aspects; /** * @FLOW3\Aspect */ class LoggingAspect { /** * @FLOW3\Inject * @var \Acme\Logger\LoggerInterface */ protected $logger; /** * @param \TYPO3\FLOW3\AOP\JoinPointInterface $joinPoint * @FLOW3\Before("method(Acme\Products\Model\Product->delete())") */ public function log(\TYPO3\FLOW3\AOP\JoinPointInterface $jp) { $product = $jp->getMethodArgument('product'); $this->logger->info('Removing ' . $product->getName()); } } -
Separation of concerns Dependency Direction "High-level modules should not depend
on low-level modules. Both should depend on abstractions." Robert C. Martin -
Separation of concerns Inverting Concerns Presentation Layer Business Layer Resource
Access Layer Presentation Layer Business Layer Resource Access Layer UI Component -
Separation of concerns Inverting Concerns The goal of Dependency Injection
is to separate the concerns of obtaining the dependencies from the core concerns of a component. -
Separation of concerns What are the benefits? 2. Ensure the
maintainability -
Separation of concerns What are the benefits? 4. Enables everyone
to understand the application -
Separation of concerns What are the benefits? 5. Allows developers
to work on components in isolation