19. “turns out to be pretty much what you expected” - Ward Cunningham Quotes from “Clean Code” by Robert Martin
20. That sounds nice, but I have a deadline I don't have time for clean code!
21. Do you want your emergency room doctor to rush your surgery? Do you want your account to rush his work on your tax return? Analogy from “The Clean Coder”
43. From Growing Object Oriented Software, Guided by Tests : "An object oriented system is a web of collaborating objects... The behavior of the system is an emergent property of the composition of the objects - the choice of objects and how they are connected... Thinking of a system in terms of its dynamic communication structure is a significant mental shift from the static classification that most of us learn when being introduced to objects."
45. My Shashin plugin consists of 44 classes, each of which has a meaningful name, follows the SRP, and “does one thing” PicasaPhotoDisplayer SettingsMenu YouTubeSynchronizer Uninstall etc.
46. class ShashinInstall { // ... public function run() { $this->createAlbumTable(); $this->verifyAlbumTable(); $this->createPhotoTable(); $this->verifyPhotoTable(); $this->updateSettings(); return true; } // ... } Shashin example
47. Shashin's capabilities are shaped by how these objects are wired together, through implementation of the DIP
49. Naïve model of a button and lamp class Button { private $lamp; public function __construct(Lamp $lamp) { $this->lamp = $lamp; } public function poll() { if (/* some condition */) { $this->lamp->turnOn(); } } } Example from “Agile Software Development” Button + poll() Lamp + turnOn() + turnOff()
50. Dependency inversion applied This is the Abstract Server pattern Button + poll() <<interface>> SwitchableDevice + turnOn() + turnOff() Lamp
51. class Lamp implements SwitchableDevice { public function turnOn() { // code } public function turnOff() { // code } } class Button { private $switchableDevice; public function __construct(SwitchableDevice $switchableDevice) { $this->switchableDevice = $switchableDevice; } public function poll() { if (/* some condition */) { $this->switchableDevice->turnOn(); } } }
53. “Software architectures are structures that support the use cases of the system... Frameworks are tools to be used, not architectures to be conformed to” - Bob Martin http://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Architecture.html
54. In my plugins, WordPress custom functions are moved behind a “facade”
55. Facade pattern for my plugins Shashin Functions facade interface Some other facade implementation WordPress facade implementation Yet another facade implementation
56. interface ToppaFunctionsFacade { // ... public function enqueueStylesheet($handle, $relativePath, $dependencies = false, $version = null, $media = null); } class ToppaFunctionsFacadeWp implements ToppaFunctionsFacade { // … public function enqueueStylesheet($handle, $relativePath, $dependencies = false, $version = null, $media = null) { return wp_enqueue_style($handle, $relativePath, $dependencies, $version, $media); } } // use in Shashin // ... public function setFunctionsFacade(ToppaFunctionsFacade $functionsFacade) { $this->functionsFacade = $functionsFacade; return $this->functionsFacade; } // ... $this->functionsFacade->enqueueStylesheet('shashinAdminStyle', $cssUrl, false, $this->version); Shashin example
57. But I decided to not use a facade for WordPress' hooks and filters
58. I isolated the WordPress hooks and filters in a single method
63. Shashin example This is one of the tests for the method setTableCaptionTag(), in the class ShashinLayoutManager Mock::generate('Lib_ShashinSettings'); Mock::generate('Lib_ShashinAlbumPhotosCollection'); class UnitPublic_ShashinLayoutManager extends UnitTestCase { // … public function testSetTableCaptionTagWithFewerPhotosThanMaxPerPage() { $layoutManager = new Public_ShashinLayoutManager(); $collection = new MockLib_ShashinAlbumPhotosCollection(); $collection->setReturnValue('getCount', 9); $layoutManager->setDataObjectCollection($collection); $settings = new MockLib_ShashinSettings(); $settings->setReturnValue('getPhotoLimit',10); $this->layoutManager->setSettings($settings); $this->assertNull($this->layoutManager->setTableCaptionTag()); } }
Stroustrup: inventor of C++ Booch: IBM chief scientist Thomas: founder of Object Technology International Feathers: author of Working Effectively with Legacy Code Jeffries: co-creator of XP development Cunnigham: inventor of Wikis
Doctors take the Hippocratic oath and a set of practices to ensure they practice medicine safely Accounts are expected to do double entry booking, to avoid mistakes Other professions have standards for what it means to practice that profession safely and responsibly
The software craftsmanship movement is a part of the Agile community, and is intended to start a conversation about what it means to write code responsibly and cleanly. You don't have to agree with the ideas, but you do need to engage them
Code that is hard to read will slow you down Code that is easy to read frees you to go faster
“Big ball of mud” code can't - it will eventually make you so slow, you'll want to throw it out and start over Talk about wasted time!
Dirty code will drive away potential contributors
I will probably need to save unit testing for a session in tomorrow's unconference time
The revelation for me in learning clean code techniques is that code can be expressive. That it really can “read like well written prose.” Rather than relying on comments to explain your code, the code can explain itself Comments become something you have to maintain, and if they become outdated and no longer describe the current behavior of the code, they become dangerous lies
Take a minute to read this Even without knowing the class or the properties, it's clear what this method does. You should use a 21 st century IDE, that auto-completes names for you and makes it easy to rename. I use PHP Storm
Well written object oriented code follows these principles
Do one thing, do it well, do it only For methods, this typically means changing the value of only one variable If you are passing more than 2 or 3 arguments into a method, you are probably doing more than one thing For classes, it means having a single conceptual responsibility You want high cohesiveness in a class This is the opposite of how most WordPress plugins are written
The whole class is about 100 lines The run method calls the other methods (this is a simple use of the command pattern) It reads like a table of contents Breaking the functionality down into methods that do one thing makes the code easier to read and to unit test
It's common to see code that hard-wires together all the parts, when those connections could be made more flexible and extensible
This solution violates the DIP Button depends directly on Lamp Changes to Lamp may require changes to Button Button is not reusable It can't control, for example, a Motor
What it means Neither Button nor Lamp “own” the interface Buttons can now control any device that implements SwitchableDevice Lamps and other SwitchableDevices can now be controlled by any object that accepts a SwitchableDevice I'll provide a Shashin example after introducing one more concept
Looking at the Shashin files and code, it screams “photo management and display!” It does not scream “WordPress plugin” The fact that it is a WordPress plugin is incidental to its design This makes it possible to do unit testing of your plugins, and makes them potentially usable outside of WordPress
A different implementation of the facade would allow Shashin to work outside of WordPress, without touching Shashin code
At the highest level, other CMS' or frameworks are not going to wire Shashin together to same way as WordPress. Also, since WordPress' hooks and filters return nothing, they are hard to mock for tests I need a better understanding of what other environments are like before I can design an interface with the appropriate flexibility
This isolates the impact of changes that might be needed in the future, since I don't yet have a specific use case for Shashin outside of WordPress, but I want to be ready when the need comes
Each function that is called instantiates the necessary objects for that hook. This approach is efficient. Shashin is a big plugin with a lot of files, but the only code loaded at runtime is the code needed
Having an automated test suite can alert you to bugs before your customers find them TDD drives good design, but of all the clean code practices, is the hardest to learn how to do well
Although PHPUnit is the standard, I chose to create a plugin for using SimpleTest with WordPress because: It's designed for web use and is very easy to subclass and package with a plugin It's approach to mock objects is easier to understand and use than PHPUnit's
For my plugin, you create your tests, then in a shortcode give the path to the tests, and SimpleTest will run them. Since it's running within WordPress, you can also use it for integration testing (that is, calling actual WordPress functions will work)
The complexity of having 44 classes in Shashin is justified by its need for flexibility You can support a new viewer or a new photo service simply by creating a new subclass. This is much more maintainable and extensible than a single huge file with deeply nested conditionals and unclear dependencies But if I knew I would never need that kind of flexibility, there would be no justification for creating these layers of abstraction – they would just be needless complexity