Here's a rough attempt at an implementation.
Reflecting the hierarchical nature of the view in the render variables is left as an exercise for the reader, i.e., myc 
Would love to see what anybody else comes up with.
PHP Code:
class ServiceLocator {
/**
* @return Session
*/
function session() {
return new Session();
}
}
class Session {
/**
* @return User
*/
function user() {
return new User();
}
}
class User {
function getUsername() {
return 'usernameTest';
}
}
class Factory {
/**
* @return ViewFactory
*/
function views() {
return new ViewFactory();
}
}
class ViewFactory {
/**
* @return PageView
*/
function homePage() {
return $this->page()
->addChild($this->welcomeMessages())
->addChild($this->forumPosts());
}
/**
* @return ForumPostsView
*/
function forumPosts() {
return new ForumPostsView();
}
/**
* @return PageView
*/
function page() {
return new PageView();
}
/**
* @return WelcomeMessagesView
*/
function welcomeMessages() {
return new WelcomeMessagesView();
}
}
abstract class View {
abstract function execute(ServiceLocator $locator);
function accept(Renderer $renderer) {
$renderer->visit($this);
}
}
abstract class CompositeView extends View {
private $children = array();
/**
* @return View
*/
function addChild(View $view) {
$this->children[] = $view;
return $this;
}
function accept(Renderer $renderer) {
parent::accept($renderer);
foreach ($this->children as $child) {
$child->accept($renderer);
}
}
}
class PageView extends CompositeView {
function execute(ServiceLocator $locator) {
return array('username' => $locator->session()->user()->getUsername());
}
}
class WelcomeMessagesView extends View {
function execute(ServiceLocator $locator) {
// you would obviously use the service locator here
return array(
array('title' => 'Welcome 1', 'body' => 'Welcome Body 1'),
array('title' => 'Welcome 2', 'body' => 'Welcome Body 2')
);
}
}
class ForumPostsView extends View {
function execute(ServiceLocator $locator) {
return array(
array('title' => 'Post 1', 'body' => 'Post Body 1'),
array('title' => 'Post 2', 'body' => 'Post Body 2')
);
}
}
class Renderer {
protected $locator;
protected $vars = array();
function __construct(ServiceLocator $locator) {
$this->locator = $locator;
}
function visit(View $view) {
if (is_array($vars = $view->execute($this->locator))) {
$this->vars[get_class($view)] = $vars;
}
}
function render($template) {
// TODO: extract vars, include template
var_dump($this->vars);
}
}
$factory = new Factory();
$renderer = new Renderer(new ServiceLocator());
$factory->views()->homePage()->accept($renderer);
echo $renderer->render('myPageTemplate');
Bookmarks