• Like

Loading…

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

Clean code

  • 33,069 views
Uploaded on

Slides from my talk on at sflive2011

Slides from my talk on at sflive2011

More in: Technology , Education
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • A good presentation. The only pity is that the Quotes Fowler, Bergman, Knuth, and other well-known authors are referring to the Wikipedia.
    Are you sure you want to
    Your message goes here
  • Fantástico, clases magistrales de tipicos problemas que te encontras al intentar implementar TTD. Nos muestra como hacer código testeable.
    Are you sure you want to
    Your message goes here
  • Awesome deck ... thanks for sharing!
    Are you sure you want to
    Your message goes here
  • sharing this on facebook and twitter is appreciated :)
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
33,069
On Slideshare
0
From Embeds
0
Number of Embeds
23

Actions

Shares
Downloads
1,588
Comments
4
Likes
140

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Unit TestingA guide to writing clean, testable code, that will be easyto maintain and extend
  • 2. Bulat Shakirzyanov• PHP developer• OS evangelist• Test-fu master• Doctrine core member• Symfony2 contributor• OpenSky full-time employee• and more…
  • 3. Bulat Shakirzyanov @avalanche123 mallluhuct@gmail.com github.com/avalanche123
  • 4. Agenda• Unit Testing• Dependency Injection• Mock Objects• Test-Driven Development• Clean code techniques
  • 5. Unit Testing“Unit testing is a method by which individualunits of source code are tested to determine ifthey are fit for use. A unit is the smallest testablepart of an application.” wikipedia.org
  • 6. Why and Why not Unit TestTO TEST OR NOT TOTEST?
  • 7. Why Unit Test
  • 8. Why Unit Test• Debugging is a time consuming process
  • 9. Why Unit Test• Debugging is a time consuming process• When new functionality is added, how do we make sure the old one doesnt break
  • 10. Why Unit Test• Debugging is a time consuming process• When new functionality is added, how do we make sure the old one doesnt break• By looking at a Unit Test, you can see the class in action, which lets you easily understand its intent and proper use
  • 11. Why Unit Test• Debugging is a time consuming process• When new functionality is added, how do we make sure the old one doesnt break• By looking at a Unit Test, you can see the class in action, which lets you easily understand its intent and proper use• Unit tests are the only real measure of project health and code quality
  • 12. Why not Unit Test
  • 13. Why not Unit Test• I never make mistakes
  • 14. Why not Unit Test• I never make mistakes• The functionality is trivial
  • 15. Why not Unit Test• I never make mistakes• The functionality is trivial• Tests slow me down
  • 16. Why not Unit Test• I never make mistakes• The functionality is trivial• Tests slow me down• Management wont let me
  • 17. Why not Unit Test• I never make mistakes• The functionality is trivial• Tests slow me down• Management wont let me• I dont know how to test
  • 18. Properties of a Unit TestHOW DO WE KNOW IT ISA TEST?
  • 19. Properties of a Unit Test• Isolated• Repeatable• Fast• Self-Documenting
  • 20. Test Isolation
  • 21. Test Isolation• No need to build a car to test tires
  • 22. Test Isolation• No need to build a car to test tires• Testing payment gateway should not affect my monthly statement
  • 23. Test Isolation• No need to build a car to test tires• Testing payment gateway should not affect my monthly statement• It runs faster
  • 24. Test Isolation• No need to build a car to test tires• Testing payment gateway should not affect my monthly statement• It runs fasterBuilding cars to test tires is a waste of time andmoney
  • 25. Repeatability
  • 26. Repeatability• Tests need to be ran by every developer, no matter what stack he/she uses
  • 27. Repeatability• Tests need to be ran by every developer, no matter what stack he/she uses• Tests must not rely on the environment in which they are being run
  • 28. Repeatability• Tests need to be ran by every developer, no matter what stack he/she uses• Tests must not rely on the environment in which they are being runLower the project entry costs/barriers
  • 29. Repeatability• Tests need to be ran by every developer, no matter what stack he/she uses• Tests must not rely on the environment in which they are being runLower the project entry costs/barriers – Getting every developer the same stack and educating them on how to use it is not efficient
  • 30. Speed
  • 31. Speed• Time is money, the longer we wait for tests to run, the more money our clients or company looses
  • 32. Self-documentation
  • 33. Self-documentation• Testable code is clear and easy to follow
  • 34. Self-documentation• Testable code is clear and easy to follow• No need to explain how a certain component works, they can just look at the test
  • 35. Self-documentation• Testable code is clear and easy to follow• No need to explain how a certain component works, they can just look at the test• No need to write documentation
  • 36. Self-documentation• Testable code is clear and easy to follow• No need to explain how a certain component works, they can just look at the test• No need to write documentationWe usually look for usage examples indocumentation anyway
  • 37. Decouple componentsPROPERTY 1: ISOLATION
  • 38. Dependency Injection“… a technique for decoupling highlydependent software components” wikipedia.org
  • 39. Dependency InjectionHidden Dependency <?php class BankAccount { //... public function charge($amount) { $gateway = new PaymentGateway(); // or $gateway = PaymentGateway::getInstance(); // or $gateway = Registry::get(payment_gateway); //... $gateway->charge($amount, $this); } } Cant use a test version of PaymentGateway This test will end up in my monthly statement
  • 40. Dependency InjectionInjected Dependency <?php class BankAccount { //... public function charge($amount, PaymentGateway $g) { $g->charge($amount, $this); } } PaymentGateway can now be replaced BankAccount shouldnt be responsible for charging itself 18
  • 41. Dependency InjectionSingle Responsibility Principle <?php class BankAccount { //... } $gateway = new PaymentGateway(); $account = new BankAccount(); $gateway->charge(100, $account); Makes BankAccount class lighter, leaving it only responsibility of storing account balance 19
  • 42. Dependency InjectionWhy:• Testable code• No hidden dependencies = Better API = Maintainability• All common initialization logic can be extracted (Dependency Injection Container)What to avoid:• Long method call chains
  • 43. Law Of DemeterMETHOD CALL CHAINSARE BAD
  • 44. Law Of Demeter“Each unit should have only limited knowledgeabout other units: only units "closely" related to thecurrent unit”“Each unit should only talk to its friends; dont talkto strangers”“Only talk to your immediate friends” wikipedia.org
  • 45. Law Of Demeter <?php //... Bad $sku = $product->getStockItem()->getSku(); <?php //... $sku = $product->getItemSku(); //Product.php class Product { Good //... public function getItemSku() { return $this->stockItem->getSku(); } }
  • 46. Law Of DemeterMakes code resistant to changeIf the Law Of Demeter was followed, the ->getSku() calls wouldnt need tobe changed.
  • 47. Law Of Demeter <?php //... $logger->getWriter()->getFile()->writeLine(Some Log); <?php //... $logger->log(Some Log);
  • 48. Law Of Demeter“Programmers should avoid writing code thatlooks like: dog.getBody().getTail().wag();… The solution is … [to] … rewrite our exampleas: dog.expressHappiness(); and let theimplementation of the dog decide what this means.” ThoughtWorks UK 26
  • 49. Control the interactionPROPERTY 2: SPEEDPROPERTY 3: REPEATABILITY
  • 50. Mock Objects“… mock objects are simulated objects that mimicthe behavior of real objects in controlled ways” wikipedia.org
  • 51. Mock Objects Mock Test A S S Test independence Mocking external dependencies Interface discovery Mocking not-yet-existent classes and interaction
  • 52. Mock ObjectsTest independence:• By mocking out external dependencies, we can achieve faster and more granular tests, e.g. mock filesystem, database, third party api, etc.
  • 53. Mock Objects. Example: class FileLoggerTest extends PHPUnit_Framework_TestCase { //... public function testShouldFormatLogString() { $file = $this->getMock(File); $file ->expects($this->once()) ->method(write) ->with(sprintf([ERR] %s some error, date(DATE_ISO8601))) ; $logger = new FileLogger($file); $logger->err(some error); } }Isolating file system accessNow we actually test that FileLogger correctly decorates the log string before writing it to file, leavingthe actual write to file part to the File class and test.
  • 54. Mock Objects <?php $fp = fopen(/tmp/file.ext, w+); fwrite($fp, content); Functions must become methods <?php $file = new File(/tmp/file.ext, w+); $file->write(content); 32
  • 55. Mock Objects <?php class File { private $handle; public function __construct($path, $mode) { An example of $this->handle = fopen($path, $mode);transforming a function } into a method public function write($content, $length = null) { fwrite($this->handle, $content, $length); } public function __destruct() { fclose($this->handle); } } 33
  • 56. Mock Objects
  • 57. Mock ObjectsInterface Discovery:
  • 58. Mock ObjectsInterface Discovery:• Right now you know the least about your problem domain
  • 59. Mock ObjectsInterface Discovery:• Right now you know the least about your problem domain• Your knowledge of the domain grows as you spend more time working in it
  • 60. Mock ObjectsInterface Discovery:• Right now you know the least about your problem domain• Your knowledge of the domain grows as you spend more time working in it• Why write something that youll throw away if you can mock it and see if it makes sense?
  • 61. Mock Objects. Example: <?php class PaymentGatewayTest extends PHPUnit_Framework_TestCase { //... public function testShouldCreditAccountByChargeAmount() { $bankAccount = $this->getMock(BankAccount); $bankAccount ->expects($this->once()) ->method(credit) ->with(100) ; $this->gateway->charge(100, $bankAccount); } }The above example if written before the BankAccount class, would havegiven us better understanding of the problem and could help design theBankAccount class 35
  • 62. Mock Objects Mock T T Test B S Mock U U So, it turns out, that the best use for Mock Objects is for Iterative interface discovery
  • 63. Test-Driven DevelopmentWAIT, WHAT?
  • 64. Test Driven Development
  • 65. Test Driven DevelopmentRed
  • 66. Test Driven DevelopmentRed• write a failing test case, use mock objects to represent classes that are not implemented
  • 67. Test Driven DevelopmentRed• write a failing test case, use mock objects to represent classes that are not implementedGreen
  • 68. Test Driven DevelopmentRed• write a failing test case, use mock objects to represent classes that are not implementedGreen• when the test makes sense and the intent is clear, write method bodies
  • 69. Test Driven DevelopmentRed• write a failing test case, use mock objects to represent classes that are not implementedGreen• when the test makes sense and the intent is clear, write method bodiesRefactor
  • 70. Test Driven DevelopmentRed• write a failing test case, use mock objects to represent classes that are not implementedGreen• when the test makes sense and the intent is clear, write method bodiesRefactor• now that your tests are green, see what needs to be extracted (create dependencies, remove duplication)
  • 71. TDD: Example Step 1 (Red)// tests/PaymentGatewayTest.php // lib/PaymentGateway.phprequire_once __DIR__./../lib/PaymentGateway.php; class PaymentGatewayrequire_once __DIR__./../lib/BankAccount.php; {class PaymentGatewayTest extends PHPUnit_Framework_TestCase public function charge($amount, BankAccount $account){ { } protected $gateway; } public function setUp() { $this->gateway = new PaymentGateway(); } // lib/BankAccount.php class BankAccount public function tearDown() { { } unset($this->gateway); } public function testCharge() $ phpunit tests/ PHPUnit 3.5.0RC1 by Sebastian Bergmann. { $bankAccount = $this->getMock(BankAccount); F $bankAccount Time: 0 seconds, Memory: 3.25Mb ->expects($this->once()) ->method(credit) There was 1 failure: ->with(100) 1) PaymentGatewayTest::testCharge ; Expectation failed for method name is equal to <string:credit> when invoked 1 time(s). Method was expected to be called 1 times, actually called 0 times. $this->gateway->charge(100, $bankAccount); FAILURES! } Tests: 1, Assertions: 1, Failures: 1.} 39
  • 72. TDD: Example Step 2 (Green) // lib/BankAccount.php // lib/PaymentGateway.php class BankAccount class PaymentGateway { { public function credit($amount) public function charge($amount, BankAccount $account) { { } $account->credit($amount); } } }$ phpunit tests/PHPUnit 3.5.0RC1 by Sebastian Bergmann..Time: 0 seconds, Memory: 3.25MbOK (1 test, 1 assertion) 40
  • 73. Mock only what you own...And what you don’t own, own namespace My; class Mongo { private $mongo;Classes change, method names, public function __construct(Mongo $mongo) {signature, visibility changes. $this->mongo = $mongo; }What will you do if a class public function selectDB($name)changed one of its public { return new MongoDB($this->mongo->selectDB($name));methods to final? } } class MongoDBDoctrine MongoDB ODM is built {using proxies private $mongoDb; public function __construct(MongoDB $mongoDb) { $this->mongoDb = $mongoDb; } } 41
  • 74. Optional External Dependenciesand repeatabilitynamespace Tests;class MongoTestCase extends PHPUnit_Framework_TestCase{ public function setUp() namespace Tests; { if ( ! class_exists(Mongo)) { class MongoTest extends MongoTestCase $this->markTestSkipped( { Mongo extension not installed public function setUp() ); Text { } parent::setUp(); } //...} } } If your class or library that you test rely on an external component, that might not be present and the absence of which might break the tests, make sure to add checks and mark tests as skipped
  • 75. RefactoringTHE CODE EVOLUTION
  • 76. Refactoring“… refactoring is … changing … sourcecode without modifying itsexternal functional behavior in order to improve… the software.” wikipedia.org
  • 77. Refactoring 45
  • 78. Test Driven DevelopmentWhy• Best test code coverage possible• Design is born out of necessity, not forecastWhat to avoid• Shared fixtures – dont use the same objects in multiple tests, dont use persistent objects• External Dependencies – they are slow and uncontrollable.• Test code duplication – test code needs to be maintained, keep it DRY. Extract creation methods, create test cases per fixture setup (BDD)• Over-mocking – leads to fragile code, usually is a sign of violation of the Law Of Demeter
  • 79. Premature OptimizationWHY NOT OPTIMIZEBEFORE CODING?
  • 80. Premature Optimization“Premature optimization is the root of all evil” C. A. R. Hoare, Computer Scientist“"Premature optimization" is a phrase used todescribe a situation where a programmer letsperformance considerations affect the designof a piece of code.” wikipedia.org
  • 81. Premature Optimization class Product { protected $reviews; protected $reviewCount = 0; public function __construct(ArrayCollection $r = null) { $this->reviews = $r ?: new ArrayCollection();Counters cache is part of the model }design public function getReviewCount() { return $this->reviewCount;Every new related collection brings }five methods public function incrementReviewCount() { $this->reviewCount++;Counters cache logic is duplicated }with every new collection public function decrementReviewCount() { $this->reviewCount--;Intuitive and readable operations like }count($product->getReviews()) public function addReview(Review $review)cannot be performed { $this->reviews->add($review); $this->incrementReviewCount(); } public function removeReview(Review $review) { $this->reviews->remove($review); $this->decrementReviewCount(); } }
  • 82. Premature Optimization class Product {Cache logic is removed from Product private $reviews;responsibility public function __construct(ArrayCollection $r = null)Additional methods are eliminated { $this->reviews = $r ?: new ArrayCollection();Duplication with introduction of new collections }is minimized public function getReviewCount() {Further optimization path (if necessary): return count($this->reviews); }• Replace ArrayCollection instance with ReviewCollection, that has a better public function addReview(Review $review) optimized ->count() method. { $this->reviews->add($review);• Remove the ->getReviewCount() method } public function removeReview(Review $review)• Avoid code duplication for other Product { relationships by introducing $this->reviews->remove($review); CachedCollection and replacing } ReviewCollection with it }
  • 83. Keep your code cleanPROPERTY 4:SELF-DOCUMENTATION
  • 84. Clean code techniquesDont Repeat Yourself (DRY)• If a piece of code appears more than two times, extract it into a dedicated function, method or classYou aint gonna need it (YAGNI)• There is no need to implement ACL if you were asked to write a forum board, there might never be a need• Dont optimize the code before its written, forecasts are usually wrong anyway
  • 85. Clean code techniquesAvoid magic• its easier to code when the API is not lying – Magento is a good example of abusing magic and ruining maintainability – Dependency Injection helps to get rid of magicAvoid boolean parameters, they kill readability• Example – ->processOrder(int $orderNumber, boolean $emailConfirmation = true)• It should really be – ->processOrder(int $orderNumber) – ->emailConfirmation(int $orderNumber)
  • 86. Clean code techniquesInheritance and Polymorphism over conditionals• Replace the switch with polymorphismComposition over Inheritance• Extract/inject functionality instead of inheriting it
  • 87. If statements only to be used with primitive types, switch statementsare not object orientedINHERITANCE ANDPOLYMORPHISMOVER CONDITIONALS
  • 88. Inheritance and Polymorphism overconditionals class MongoCursor { public function sort(array $fields) { if ($this->loggerCallable) { $this->log(array( sort => true, fields => $fields, )); } $this->mongoCursor->sort($fields); return $this; } }Example: Its probably not bad to have one if statement
  • 89. Inheritance and class MongoCollection {Polymorphism public function batchInsert( array &$a, array $options = array())over { //... if ($this->loggerCallable) {conditionals $this->log(array( batchInsert => true, num => count($a), data => $aExample: Duplication. )); } $result = $thisEvery new method that needs to ->mongoCollectionbe logged, will have to add the ->batchInsert($a, $options) ;if statement return $result;There are plenty of if } public function getDBRef(array $reference)statements in this class { //... if ($this->loggerCallable) { $this->log(array( get => true, reference => $reference, )); } return $dbRef; } } 57
  • 90. Inheritance and Polymorphism overconditionalsThe class that has conditionals is much harder tounderstand and test, as there are several possibleexecution flows.To test the previous example, you would need toinstantiate the class with and without$loggerCallable and test each function twice.
  • 91. Inheritance and class MongoCollection { public function batchInsert(Polymorphism { array &$a, array $options = array())over $result = $this ->mongoCollection ->batchInsert($a, $options);conditionals return $result; } public function getDBRef(array $reference) { return $dbRef;Solution: Use polymorphism to } }transfer behavior class LoggableMongoCollection extends MongoCollection {Now public function batchInsert( array &$a,LoggableMongoCollection is array $options = array()) {the class responsible for $this->log(array(logging collection interaction. batchInsert => true, num => count($a), data => $aThe regular MongoCollection )); return parent::batchInsert($a, $options);needs not know about logging } public function getDBRef(array $reference) { $this->log(array( get => true, reference => $reference, )); return parent::getDBRef($reference); } }
  • 92. Inheritance and Polymorphism overconditionals
  • 93. Inheritance and Polymorphism overconditionals—Did my if statements disappear?
  • 94. Inheritance and Polymorphism overconditionals—Did my if statements disappear?—No. • The conditional was actually moved to the instantiation part of the application, it is not part of the domain logic anymore.
  • 95. Inheritance and Polymorphism overconditionals—Did my if statements disappear?—No. • The conditional was actually moved to the instantiation part of the application, it is not part of the domain logic anymore.Testing such dedicated classes is much simpler,since there is only one execution flow
  • 96. Inheritance and Polymorphism overconditionals class MongoCollectionFactory { protected $loggerCallable; public function __construct(Closure $loggerCallable = null) { $this->loggerCallable = $loggerCallable; } public function getMongoCollection() { return isset($this->loggerCallable) ? new LoggableMongoCollection($this->loggerCallable) : new MongoCollection(); } } Dependency Injection helps to separate the “initialization” logic from “domain” logic This allows for greater testability as each concern is tested in a dedicated environment
  • 97. Inheritance and class DisablerController extends Controller Polymorphism { public function disableAction($type) over { switch ($type) { conditionals case product: case seller: case supplier: } }switches are just public function enableAction($type)grouped ifs { switch ($type) {Consider the following controller: case product: case seller:• It let’s you disable, enable or list certain case supplier: object type. } }• It uses switch statements to do so public function listAction($type)• It introduces tons of duplication { switch ($type)• Each method will have at least three test { case in order to achieve the necessary case product: converage case seller: case supplier:Why reinvent the type system if } }OOP already let’s us use types }(classes)?
  • 98. Inheritance and Polymorphism overconditionals interface DisablerControllerInterface { public function disableAction(); public function enableAction(); public function listAction(); } Define your object interface
  • 99. Inheritance and Polymorphism over conditionalsCreate your interfaceimplementations – oneimplementation per switchcondition• Classes can be tested in isolation• Lighter classes• True usage of the type systemYou could also use abstract classes andcomplicated inheritance trees
  • 100. Inheritance and Polymorphism ProductDisablerController extends Controller over implements DisablerControllerInterface { public function disableAction() conditionals { } public function enableAction() {Create your interface }implementations – oneimplementation per switch public function listAction() {condition } }• Classes can be tested in isolation• Lighter classes• True usage of the type systemYou could also use abstract classes andcomplicated inheritance trees
  • 101. Inheritance and Polymorphism ProductDisablerController extends Controller over implements DisablerControllerInterface { SellerDisablerController public function disableAction() conditionals { extends Controller } implements DisablerControllerInterface { public function disableAction() public function enableAction() { {Create your interface } }implementations – one public function enableAction()implementation per switch public function listAction() { {condition } } }• Classes can be tested in isolation public function listAction() {• Lighter classes } }• True usage of the type systemYou could also use abstract classes andcomplicated inheritance trees
  • 102. Inheritance and Polymorphism ProductDisablerController extends Controller over implements DisablerControllerInterface { SellerDisablerController public function disableAction() conditionals { extends Controller } implements DisablerControllerInterface { SupplierDisablerController public function disableAction() public function enableAction() { extends Controller {Create your interface } } implements DisablerControllerInterface {implementations – one public function disableAction() public function enableAction()implementation per switch public function listAction() { { { }condition } } } public function enableAction()• Classes can be tested in isolation public function listAction() { { } }• Lighter classes } public function listAction()• True usage of the type system { }You could also use abstract classes and }complicated inheritance trees
  • 103. Extract re-used componentsinstead of inheriting functionalityCOMPOSITION OVERINHERITANCE
  • 104. Composition over InheritanceThere might be cases when to receive an already-implemented functionality and keep it DRY, youextend the class encapsulating itFor example: a MongoCursor, that extends LoggableMongoCollection forits ->log() method
  • 105. Composition over Inheritance <?php class LoggableMongoCollection extends MongoCollection { //... public function log(array $log) { if ( ! $this->loggerCallable) { return; } $log[class] = $this->class->name; $log[db] = $this->class->db; $log[collection] = $this->class->collection; call_user_func($this->loggerCallable, $log); } } class MongoCursor extends LoggableMongoCollection implements Iterator, Countable { //... public function sort($fields) { if ($this->loggerCallable) { $this->log(array( sort => true, fields => $fields, )); } $this->mongoCursor->sort($fields); return $this; } } 67
  • 106. Composition over InheritanceBut what happens if we need the same functionalityin parallel hierarchies?• Single Inheritance doesnt allow it, we end up duplicating the ->log() method in both hierarchiesCOMPOSITION to the rescue
  • 107. Composition over Inheritance class Logger { protected $loggerCallable; public function __construct(Closure $loggerCallable) { $this->loggerCallable = $loggerCallable; } public function log(array $log) { $log[class] = $this->class->name; $log[db] = $this->class->db; $log[collection] = $this->class->collection; call_user_func($this->loggerCallable, $log); } } The solution is to extract common functionality into a dedicated class
  • 108. Composition over Inheritance interface LoggerContainer { //... public function setLogger(Logger $logger); } Create an Interface, that would define how the new component will compose the existing ones
  • 109. Composition over Inheritanceclass MongoCursor implements Iterator, <?phpCountable class MongoCollection{ { //... //... public function sort($fields) public function batchInsert(array &$a, array $options = array()) { { $this->mongoCursor->sort($fields); //... return $this; $result = $this->mongoCollection->batchInsert($a, $options); } return $result;} } public function getDBRef(array $reference) { //... return $dbRef; } } Regular classes in both hierarchies
  • 110. Composition over Inheritanceclass LoggableMongoCursor extends MongoCursor class LoggableMongoCollection extends MongoCollection implements LoggerContainer implements LoggerContainer{ { //... //... public function setLogger(Logger $logger) public function setLogger(Logger $logger) { { $this->logger = $logger; $this->logger = $logger; } } //... public function sort($fields) public function batchInsert(array &$a, array $options = array()) { { $this->logger->log(array( $this->logger->log(array( sort => true, batchInsert => true, fields => $fields, num => count($a), )); data => $a return parent::sort($fields); )); } return parent::batchInsert($a, $options);} } public function getDBRef(array $reference) { $this->logger->log(array( get => true, reference => $reference, ));Classes, that can log operations return parent::getDBRef($reference); } }
  • 111. Composition over InheritanceNOTE: dont decomposeobjects if there is no needfor re-use, YAGNI
  • 112. Resources for self-improvementWHATS NEXT?
  • 113. Resources for self-improvement•Google Tech Talks - http://www.youtube.com/user/ GoogleTechTalks – Inheritance, Polymorphism, & Testing – Unit Testing•Blogs – Invisible to the Eye – personal blog of Giorgio Sironi, Software Architect•Books – Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson and John M. Vlissides, Addison-Wesley Professional, 1994 – Refactoring: Improving the Design of Existing Code by Martin Fowler, Kent Beck, John Brant, William Opdyke and Don Roberts, Addison-Wesley Professional, 1999 – Patterns of Enterprise Application Architecture by Martin Fowler, Addison-Wesley Professional, 2002 – xUnit Test Patterns: Refactoring Test Code by Gerard Meszaros, Addison-Wesley, 2007 – Growing Object-Oriented Software Guided by Tests by Steve Freeman and Nat Pryce, Addison-Wesley Professional, 2009
  • 114. Now is the time to ask themQUESTIONS?