Echt een heel cool topic :) Ik denk dat veel mensen hier wat aan hebben! In combinatie met deze twee topics is al gauw de basis van OO gelegd:
-
topic 1
-
topic 2
En verder helpt
deze tutorial om de echte beginselen te planten.
Maar goed dat was even offtopic :)
unit testing
En ga fouten maken, daar leer je het meeste van. Al moet je dan niet bang zijn om regelmatig even een stap terug te doen en eerst even de gemaakte fouten te gaan herstellen. Neem dus ook een platform die je op jouw fouten wijst en zorg dat de fouten z.s.m. zichtbaar worden. Test driven development, TDD, is hier ideaal voor, dit is gemaakt om fouten direct aan het licht te brengen.
http://en.wikipedia.org/wiki/Test-driven_development
<?php echo $data->name; ?>
Ik maak gebruik altijd $this->name. Dat vind ik wel netjes.
Ik vind het maar lastig met die container en registries
Eerst een verhaaltje: Bijna 100 jaar geleden (September 1908 om precies te zijn) bracht Henry Ford zijn eerste Ford Model T (ook wel Tin Lizzie genoemd) op de markt. Zoals waarschijnlijk wel bekend, werd de Model T de eerst op zeer grote schaal geproduceerde auto. De vraag naar auto’s steeg tijdens de begindagen van de Model T enorm. Auto’s werden voor meer mensen dan alleen de elite beschikbaar.
Henry Ford had moeite om aan de vraag te voldoen. In de eerste maanden dat de Model T werd produceerd werden er slechts 11 auto’s per maand geproduceerd. Dit alles gebeurde in de fabriek van Ford op ‘Piquette Avenue’ in Detroit. In 1910 waren er ‘slechts’ 12.000 Model T Ford auto’s geproduceerd en dat was het moment dat Henry Ford de productie verplaatste naar het Highland Park Complex, waar tot op de dag van vandaag de archivering plaatsvindt voor Ford.
In het Highland Park Complex werd de assemblagelijn ontwikkeld. Henry Ford deed dit niet zelf, het was één van zijn medewerkers die in een frabriek in Chicage een straat gezien had die kippen deassembleerde. De deassemblage (klinkt mooi, niet waar?) van de kip gebeurde in deze fabriek op een gecontroleerde manier, stapje voor stapje en volledig automatisch. Tegenwoordig worden op de meeste gruwelijke manieren kippen middels uitbeenmachines, ontbeenmachines en nog veel meer machines met gruwelijk namen gedeassembleerd. Met meestelijke efficiëntie worden miljoenen kippen (gok ik) per dag gedeassembleerd. Maar goed, terug in het onderwerp. De persoon die dit allemaal zag dacht: Als we dat nou in de omgekeerde volgorde doen (assemblage in plaats van deassemblage) maar dan voor auto’s. Het was dus niet the boss himself die de assemblagelijn introduceerde, maar goed, hij was wel degene die de gok durfde te nemen deze revolutionaire manier van produceren uit te proberen.
Gauw naar het jaar 1914. Dit was het jaar dat er elke 93 minuten een Model T Ford van de band rolde. Een paar jaar eerder waren dit zoals al verteld slechts 11 per maand geweest. Als je ervan uitgaat dat men 8 uur per dag en 5 dagen per week assembleert betekend dit dat Ford meer dan 10 keer zoveel auto’s kon produceren per maand. De assemblagelijn had dus zijjn succes bewezen! Natuurlijk was niet alleen de assemblagelijn de grote reden dat er elke 93 minuten een Model T Ford van de band rolde. Ook de introductie van de 5 daagse werkweek en het vaste loon, onafhankelijk van de ‘productie’ van de medewerker waren belangrijke factoren die meehielpen bij een snelle producten van de Model T Ford. Die Henry Ford had de zaakjes goed voor mekaar dus.
Even iets dieper inzoomend op de assemblagelijn. Deze lijn klikt verschillende onderdelen aan elkaar, om uiteindelijk een auto te fabriceren. De onderdelen zelf zijn op een ander tijdstip gemaakt en zijn zo gemaakt dat ze op andere onderdelen aansluiten (boutjes, moertjes, schroefjes, pasvormen). Zo zal een kogelvrije vooruit voor een Model T Ford (zouden ze die al hebben gehad?) dezelfde vorm gehad hebben gehad als een vooruit waarmee een gewone man het moest doen. Ditzelfde geldt voor de wielen. Winterbanden (geloof niet dat ze die hadden) zul je op ongeveer dezelfde manier op de Model T Ford gezet hebben als normale banden.
Als eerste kunnen we constateren dat de assemblage van de verschillende componenten waaruit de auto bestaat volledig en voor 100% losgekoppeld is van het daadwerkelijk functioneren van deze componenten. Dit is logisch lijkt mij. Een cake bakt zichzelf niet, daar heb je een recept voor nodig en iemand die de eieren, bloem, suiker en dergelijke door de mixer haalt en in de over zet. Hetzelfde geldt voor een huis, dat bouwt zichzelf ook niet. Je hebt daar architecten voor nodig die een bouwtekening maken, metselaars en timmermannen voor de bouw van het huis. En ook een auto bouwt zichzelf niet, die wordt geassembleerd.
Een interessante vraag is waarom die assemblagelijn nu eigenlijk zo succesvol is. De algemene mening hierover is dat specialisatie efficientie bevorderd. Als je je als persoon ergens in specialiseert wordt je er beter in. Dat kunnen we ook over machines zeggen. Als je een machine bouwt die specifiek moertjes op boutjes draait, zal het gemakkelijker zijn deze machine hierop te specialiseren.
De algemene mening binnen de productiewereld is ook, dat het zich specialiseren in geval van mensen ook nadelen heeft. Overspecialisatie kan leiden tot mensen die hun werk saai gaan vinden, specifieke blessures krijgen door steeds het zelfde werk te doen (RSI, etc).
Maar goed laten we nog even bij de assemblagelijn blijven. Het onderliggende princiepe van de assemblagelijn is dus specialisatie. Dus met andere woorden; het zich richten op één deelprobleem tegelijkertijd zorgt voor een efficienter en gemakkelijker manier van het oplossen van een groter probleem. Door verschillende delen van de assemblagelijn zo in te richten dat ze elk één klein deel problemen oplossen (het op de auto schroeven van de banden, het plaatsen van het motorblok, etc) en de assemblage in zijn geheel los te koppelen van de eigenlijke functies van de auto, wordt het mogelijk om:
- Elk individuele stap in de assemblage te optimaliseren
- Elke individuele stap in de assemblage te beschrijven en herhaalbaar te maken zodat bij het inrichten van een nieuwe assemblagelijn er slechts een copy-paste actie uitgevoerd hoeft te worden.
- De individuele stap te observeren ten einde kwaliteitstestjes in te voeren die specifiek gericht zijn om deze stap in het proces te controleren (is de schroef wel goed vastgedraaid, enzovoort).
- Ten einde het hele proces van het in elkaar zetten van de auto (bijna) oneindig veel te optimaliseren en controleren.
Mmm.. Leuk dit allemaal maar ik geloof dat we op een php forum zitten? Juist ja, maar als je het bovenstaande stuk nog eens leest in het licht van php, dan moeten er toch wel lichtjes gaan branden nietwaar? Maar goed nog even terug naar de auto: die bestaat uit een (zeer grote) verzameling onderdelen die allemaal een belangrijke rol spelen in het laten rijden van de auto. De auto dient geassembleerd te worden. We zouden middels object-orientatie ook de auto kunnen modelleren:
- Car
- Engine
- Wiel
Maar als we dat doen, dan moeten we nog wel iemand hebben die voor ons die auto (de applicatie) assembleert. Dat is waar Dependency Injection, afgekort DI, om de hoek komt kijken.
Echter Dependecy Injection lost het probleem van de assemblage van een applicatie op, niet het gedrag van de componenten die zijn geassembleerd, of de communicatie tussen deze componenten. Separation of Concerns (problemen opdelen in sub-problemen) is een goed principe en ik zou bijna zo arrogant willen zijn door te zeggen dat niemand meer een applicatie moet willen bouwen zonder zich op één deelprobleem tegelijkertijd zou moeten focusen en de individuele componentjes die deze deelproblemen oplossen te assembleren doormiddel van Dependency Injection.
Hoe je die assemblage precies vormgeeft is natuurlijk vraag twee. Maar daar heb je volgens mij al genoeg informatie over gehad. Welke container ik overigens aanraad is de volgende van het [google]sphoof[/google] framework:
<?php
/**
* This file is part of the Sphoof framework.
* Copyright (c) 2010, Sphoof
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code. You can also view the
* LICENSE file online at
http://www.sphoof.nl/new-bsd.txt
*
* @category Sphoof
* @copyright Copyright (c) 2010 Sphoof (
http://sphoof.nl)
* @license
http://sphoof.nl/new-bsd.txt New BSD License
* @package Container
*/
/**
* Exception that will be thrown upon encountering an unknown scalar parameter.
*
* @package Container
* @subpackage Exception
*/
class SpUnknownScalar extends SpException { }
/**
* Exception that will be thrown upon encountering an unresolvable parameter.
*
* @package Container
*/
class SpUnknownDependency extends SpException { }
/**
* Exception that will be thrown upon requesting a class that does not exist.
*
* @package Container
*/
class SpUnknownClass extends SpException { }
/**
* Exception that will be thrown upon registering an injecting method that does not exist.
*
* @package Container
*/
class SpUnknownSetterMethod extends SpException { }
/**
* Exception that will be thrown upon requesting an interface that has no
* concrete implementation.
*
* @package Container
*/
class SpUnknownImplementation extends SpException { }
/**
* An exception thrown when using incorrect arguments.
*
* @package Container
*/
class SpInvalidArgumentException extends SpException { }
/**
* The dependency injection container.
*
* @package Container
*/
class SpContainer {
/**
* An optional context to look into.
*
* @var SpContainer
*/
protected $context;
/**
* An array of instances
*
* @var array
*/
protected $instances;
/**
* An array of scalar values.
*
* @var array
*/
protected $scalars;
/**
* An array of implementations.
*
* @var array
*/
protected $implementations;
/**
* An array of ReflectionClass instances.
*
* @var array
*/
protected $reflectedClasses;
/**
* An array of ReflectionMethod instances.
*
* @var array
*/
protected $reflectedMethods;
/**
* An array of class-specific overwrites.
*
* @var array
*/
protected $overwrites;
/**
* An array of methods to inject into after instantation.
*
* @var array
*/
protected $injectMethods;
/**
* Construct the container.
*
* @param SpContainer $context
*/
public function __construct( SpContainer $context = null ) {
$this->context = $context;
}
/**
* Creates a new instance of class $classname
*
* @param string $classname
* @return object
*/
public function create( $classname ) {
if( !$this->isAbstract( $concrete = $this->concrete( $classname ) ) ) {
return $this->injectSetters(
$this->build(
$this->reflect( $concrete ),
$this->resolve( $concrete, $this->parameters( $this->constructor( $concrete ) ) )
)
);
}
}
/**
* Returns an object of the specified type or one of it's concrete implementations.
*
* @param String $abstract
* @return Object
* @throws SpUnknownDependency if a class or one of it's dependencies could not be found.
*/
public function get( $abstract, $requester = null ) {
if( ( $abstract = strtolower( $abstract ) ) && $this->exists( $concrete = $this->concrete( $abstract, $requester ) ) ) {
if( ( $object = $this->instance( $concrete ) ) ) {
return $object;
}
throw new SpUnknownImplementation( 'Could not find implementation for ' . $abstract );
}
throw new SpUnknownClass( sprintf( 'Could not find the class %s', $abstract ) );
}
/**
* Creates a subcontainer which can be further configured and override specific values.
*
* @return SpContainer
*/
public function getSubcontainer( ) {
return new SpContainer( $this );
}
/**
* Tell the container to call and inject $methods on classes of type $classname.
*
* @param string $classname
* @param string|array $methods
* @return SpContainer
*/
public function injectMethods( $classname, $methods ) {
$this->injectMethods[$this->concrete( $classname )] = (array) $methods;
return $this;
}
/**
* Registers an instance to use for class $abstract.
*
* @param string $abstract
* @param object $concrete
* @return SpContainer
*/
public function registerInstance( $abstract, $concrete ) {
if( is_object( $concrete ) && is_string( $abstract ) ) {
$this->instances[$this->concrete( $abstract )] = $concrete;
return $this;
}
throw new SpInvalidArgumentException( );
}
/**
* Sets the scalar values a class that should be passed onto the class.
*
* @param String $classname
* @param Array $scalars
* @return SpContainer
*/
public function setScalars( $classname, Array $scalars ) {
$this->scalars[strtolower( $classname )] = $scalars;
return $this;
}
/**
* Specifies which concrete implementation of an abstract class, base class or interface should be used.
*
* @param String $abstract
* @param String $concrete
* @return SpContainer
*/
public function useImplementation( $abstract, $concrete, $forClass = null ) {
if( null !== $forClass ) {
$this->overwrites[strtolower( $forClass )][strtolower( $abstract )] = strtolower( $concrete );
return $this;
}
$this->implementations[strtolower( $abstract )] = strtolower( $concrete );
return $this;
}
/**
* Tries to instantiate the class $class with parameters $parameters.
*
* @param ReflectionClass $class
* @param array $parameters
* @return object
*/
protected function build( ReflectionClass $class, $parameters ) {
return count( $parameters ) > 0 ? $class->newInstanceArgs( $parameters ) : $class->newInstance( );
}
/**
* Determines which concrete value should be injected into the class $requester.
*
* @param string $abstract
* @param string $requester
* @return string
*/
protected function concrete( $abstract, $requester = null ) {
if( false === ( $overwrite = $this->overwrite( $abstract, $requester ) ) ) {
if( ( $abstract = strtolower( $abstract ) ) && isset( $this->implementations[$abstract] ) ) {
return $this->concrete( $this->implementations[$abstract] );
}
return strtolower( $abstract );
}
return $overwrite;
}
/**
* Gets and returns the constructor for the class $classname.
*
* @param string $classname
* @return ReflectionMethod
*/
protected function constructor( $classname ) {
return $this->constructors[$classname] = $this->reflect( $classname )->getConstructor( );
}
/**
* Determines whether or not the class with the name $class exists.
*
* @param string $class
* @return boolean
*/
protected function exists( $class ) {
return ( class_exists( $class ) || interface_exists( $class ) );
}
/**
* Tries to find a classname for the passed parameter $parameter.
*
* @param Reflectionparameter $parameter
* @return String
*/
protected function findClass( ReflectionParameter $parameter ) {
try {
return $parameter->getClass( );
}
catch( ReflectionException $e ) {
throw new SpUnknownDependency( $e->getMessage( ) );
}
}
/**
* Determines whether or not an instance of class $concrete was already made.
*
* @param string $concrete
* @return boolean
*/
protected function has( $concrete ) {
return isset( $this->instances[$concrete] );
}
/**
* Determines wether or not there is a scalar value to inject.
*
* @param string $classname
* @param string $scalar
* @return boolean
*/
protected function hasScalar( $classname, $scalar ) {
$classname = strtolower( $classname );
return isset( $this->scalars[$classname][$scalar] ) ? $this->scalars[$classname][$scalar] : null;
}
/**
* Tries to find methods that start with "set", and tries to inject the
* correct values into those methods.
*
* @param Object $object
* @return Object
*/
protected function injectSetters( $object ) {
if( ( $classname = $this->concrete( get_class( $object ) ) ) && isset( $this->injectMethods[$classname] ) ) {
foreach( $this->injectMethods[$classname] as $method ) {
if( !method_exists( $object, $method ) || !( $reflectedMethod = $this->method( $classname, $method ) ) ) {
throw new SpUnknownSetterMethod( sprintf( 'Method %s does not exist in class %s', $method, $classname ) );
}
$reflectedMethod->invokeArgs( $object, $this->resolve( $classname, $this->parameters( $reflectedMethod ) ) );
}
}
return $object;
}
/**
* Creates or retrieves an instance of type $abstract, to inject into $requester
*
* @param string $abstract
* @param string $requester
* @return object
*/
protected function instance( $abstract, $requester = null ) {
if( ( $concrete = $this->concrete( $abstract, $requester ) ) && !$this->has( $concrete ) ) {
if( ( $object = $this->create( $concrete ) ) ) {
return $this->instances[strtolower( $abstract )] = $object;
}
return isset( $this->context ) ? $this->context->get( $abstract ) : false;
}
return $this->instances[$concrete];
}
/**
* Determines whether or not a class is either an abstract class or an interface.
*
* @param string $classname
* @return boolean
*/
protected function isAbstract( $classname ) {
return $this->reflect( $classname )->isAbstract( ) || $this->reflect( $classname )->isInterface( );
}
/**
* Creates and caches a reflection method.
*
* @param string $classname
* @param string $method
* @return ReflectionMethod
*/
protected function method( $classname, $method ) {
return $this->reflectedMethods[$classname][$method] = new ReflectionMethod( $classname, $method );
}
/**
* Retrieves a potentially overwritten value, or false if there is no
* overwrite.
*
* @param string $abstract
* @param string $requester
* @return string|false
*/
protected function overwrite( $abstract, $requester ) {
$abstract = strtolower( $abstract );
$requester = strtolower( $requester );
return isset( $this->overwrites[$requester][$abstract] ) ? $this->overwrites[$requester][$abstract] : false;
}
/**
* Returns an array of ReflectionParameters, or an empty array if there are
* none.
*
* @param ReflectionMethod $method
* @return Array
*/
protected function parameters( $method ) {
return ( $method instanceof ReflectionMethod ) ? $method->getParameters( ) : array( );
}
/**
* Returns a ReflectionClass for class $classname
*
* @param string $classname
* @return ReflectionClass
*/
protected function reflect( $classname ) {
if( !isset( $this->reflectedClasses[$classname] ) ) {
$this->reflectedClasses[$classname] = new ReflectionClass( $classname );
}
return $this->reflectedClasses[$classname];
}
/**
* Loops through the parameters of a given method and tries to resolve the
* values.
*
* @param string $classname
* @param array $parameters
* @return array
*/
protected function resolve( $classname, $parameters ) {
foreach( $parameters as $parameter ) {
if( $value = $this->value( $classname, $parameter ) ) {
$values[] = $value;
}
}
return isset( $values ) ? $values : array( );
}
/**
* Retrieves a scalar value for a class, returns null if it optional and not set.
*
* @param string $classname
* @param ReflectionParameter $parameter
* @return mixed
*/
protected function scalar( $classname, ReflectionParameter $parameter ) {
if( ( $scalar = $this->hasScalar( $classname, $parameter->getName( ) ) ) || $parameter->isOptional( ) ) {
return ( null !== $scalar ) ? $scalar : null;
}
throw new SpUnknownScalar( sprintf( 'Unknown scalar values "%s" on class "%s"', $parameter->getName( ), $classname ) );
}
/**
* Tries to find the value for parameter $parameter, which can be either an
* object or a scalar value.
*
* @param string $classname
* @param ReflectionParameter $parameter
* @return mixed
*/
protected function value( $classname, ReflectionParameter $parameter ) {
if( $class = $this->findClass( $parameter ) ) {
return $this->get( $class->getName( ), $classname );
}
return $this->scalar( $classname, $parameter );
}
}
?>