Document 6456463
Transcription
Document 6456463
Want to read more? You can buy this book at oreilly.com in print and ebook format. Buy 2 books, get the 3rd FREE! Use discount code: OPC10 All orders over $29.95 qualify for free shipping within the US. It’s also available at your favorite book retailer, including the iBookstore, the Android Marketplace, and Amazon.com. Spreading the knowledge of innovators oreilly.com Learning JavaScript Design Patterns by Addy Osmani Copyright © 2012 Addy Osmani. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://my.safaribooksonline.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com. Editor: Mary Treseler Production Editor: Rachel Steely Proofreader: Linley Dolby August 2012: Indexer: Fred Brown Cover Designer: Karen Montgomery Interior Designer: David Futato Illustrators: Robert Romano and Rebecca Demarest First Edition. Revision History for the First Edition: 2012-08-06 First release See http://oreilly.com/catalog/errata.csp?isbn=9781449331818 for release details. Nutshell Handbook, the Nutshell Handbook logo, and the O’Reilly logo are registered trademarks of O’Reilly Media, Inc. Learning JavaScript Design Patterns, the image of a cuckoo pheasant, and related trade dress are trademarks of O’Reilly Media, Inc. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and O’Reilly Media, Inc., was aware of a trademark claim, the designations have been printed in caps or initial caps. While every precaution has been taken in the preparation of this book, the publisher and authors assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein. Learning JavaScript Design Patterns is released under a Creative Commons Attribution-NoncommercialNo Derivative Works 3.0 unported license. It will be released via O’Reilly during Summer 2012 and will remain available both for free online and as a physical copy or ebook purchase for readers wishing to support the project. ISBN: 978-1-449-33181-8 [LSI] 1344282027 Table of Contents Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2. What Is a Pattern? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 We Already Use Patterns Every Day 4 3. “Pattern”-ity Testing, Proto-Patterns, and the Rule of Three . . . . . . . . . . . . . . . . . . . 7 4. The Structure of a Design Pattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 5. Writing Design Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 6. Anti-Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 7. Categories of Design Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Creational Design Patterns Structural Design Patterns Behavioral Design Patterns 15 16 16 8. Design Pattern Categorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 A Brief Note on Classes 17 9. JavaScript Design Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 The Constructor Pattern Object Creation Basic Constructors Constructors with Prototypes The Module Pattern Object Literals 22 22 24 25 26 26 iii The Module Pattern Module Pattern Variations The Revealing Module Pattern Advantages Disadvantages The Singleton Pattern The Observer Pattern Differences Between the Observer and Publish/Subscribe Pattern Advantages Disadvantages Publish/Subscribe Implementations The Mediator Pattern Basic Implementation Advanced Implementation Example Advantages and Disadvantages Mediator Versus Observer Mediator Versus Facade The Prototype Pattern The Command Pattern The Facade Pattern Notes on Abstraction The Factory Pattern When to Use the Factory Pattern When Not to Use the Factory Pattern Abstract Factories The Mixin Pattern Subclassing Mixins Advantages and Disadvantages The Decorator Pattern Pseudoclassical Decorators Interfaces Abstract Decorators Decorators with jQuery Advantages and Disadvantages Flyweight Using Flyweights Flyweights and Sharing Data Implementing Classical Flyweights Converting Code to Use the Flyweight Pattern A Basic Factory Managing the Extrinsic States iv | Table of Contents 27 32 37 38 38 39 42 46 48 49 49 59 60 61 66 68 68 68 69 72 74 76 77 80 80 80 81 81 83 86 86 90 90 91 94 96 96 97 97 98 101 103 104 The Flyweight Pattern and the DOM 105 10. JavaScript MV* Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 MVC Smalltalk-80 MVC MVC for JavaScript Developers Models Views Controllers Controllers in Another Library (Spine.js) Versus Backbone.js What Does MVC Give Us? Smalltalk-80 MVC in JavaScript Delving Deeper Summary MVP Models, Views, and Presenters MVP or MVC? MVC, MVP, and Backbone.js MVVM History Model View ViewModel Recap: The View and the ViewModel Recap: The ViewModel and the Model Pros and Cons Advantages Disadvantages MVVM with Looser Data Bindings MVC Versus MVP Versus MVVM Backbone.js Versus KnockoutJS 109 109 110 111 113 115 116 118 118 119 119 119 120 121 121 123 124 124 125 127 129 130 130 130 130 131 135 136 11. Modern Modular JavaScript Design Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 A Note on Script Loaders AMD Getting Started with Modules AMD Modules with Dojo AMD Module Design Patterns (Dojo) AMD Modules with jQuery AMD Conclusions CommonJS Getting Started Consuming Multiple Dependencies 137 138 139 142 143 144 147 147 147 148 Table of Contents | v Loaders and Frameworks that Support CommonJS Is CommonJS Suitable for the Browser? Related Reading AMD and CommonJS: Competing, but Equally Valid Standards UMD: AMD and CommonJS-Compatible Modules for Plug-ins ES Harmony Modules with Imports and Exports Modules Loaded from Remote Sources Module Loader API CommonJS-like Modules for the Server Classes with Constructors, Getters, and Setters ES Harmony Conclusions Related Reading Conclusions 149 149 150 150 150 155 156 157 157 157 158 159 159 159 12. Design Patterns in jQuery . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 The Composite Pattern The Adapter Pattern The Facade Pattern The Observer Pattern The Iterator Pattern Lazy Initialization The Proxy Pattern The Builder Pattern 161 162 164 166 169 170 171 173 13. jQuery Plug-in Design Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 Patterns A Lightweight Start Pattern Complete Widget Factory Pattern Nested Namespacing Plug-in Pattern Custom Events Plug-in Pattern (with the Widget Factory) Prototypal Inheritance with the DOM-to-Object Bridge Pattern jQuery UI Widget Factory Bridge Pattern jQuery Mobile Widgets with the Widget Factory RequireJS and the jQuery UI Widget Factory Usage Globally and Per-Call Overridable Options (Best Options Pattern) A Highly Configurable and Mutable Plug-in Pattern What Makes a Good Plug-in Beyond Patterns? Quality Code Style Compatibility Reliability vi | Table of Contents 176 177 179 181 183 184 186 188 191 194 194 196 198 198 198 198 199 Performance Documentation Likelihood of maintenance Conclusions Namespacing Patterns Namespacing Fundamentals Single Global Variables Prefix Namespacing Object Literal Notation Nested Namespacing Immediately Invoked Function Expressions (IIFE)s Namespace Injection Advanced Namespacing Patterns Automating Nested Namespacing Dependency Declaration Pattern Deep Object Extension Recommendation 199 199 199 199 200 200 201 201 201 205 206 208 210 210 212 213 216 14. Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 Appendix: References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 Table of Contents | vii CHAPTER 1 Introduction One of the most important aspects of writing maintainable code is being able to notice the recurring themes in that code and optimize them. This is an area where knowledge of design patterns can prove invaluable. In the first part of this book, we will explore the history and importance of design patterns, which can really be applied to any programming language. If you’re already sold on or are familiar with this history, feel free to skip to Chapter 2 to continue reading. Design patterns can be traced back to the early work of an architect named Christopher Alexander. He would often write publications about his experience in solving design issues and how they related to buildings and towns. One day, it occurred to Alexander that when used time and time again, certain design constructs lead to a desired optimal effect. In collaboration with Sara Ishikawa and Murray Silverstein, Alexander produced a pattern language that would help empower anyone wishing to design and build at any scale. This was published back in 1977 in a paper titled “A Pattern Language,” which was later released as a complete hardcover book. Some 30 years ago, software engineers began to incorporate the principles Alexander had written about into the first documentation about design patterns, which was to be a guide for novice developers looking to improve their coding skills. It’s important to note that the concepts behind design patterns have actually been around in the programming industry since its inception, albeit in a less formalized form. One of the first and arguably most iconic formal works published on design patterns in software engineering was a book in 1995 called Design Patterns: Elements of Reusable Object-Oriented Software. This was written by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides—a group that became known as the Gang of Four (or GoF for short). The GoF’s publication is considered quite instrumental to pushing the concept of design patterns further in our field, as it describes a number of development techniques 1 and pitfalls, as well as providing 23 core object-oriented design patterns frequently used around the world today. We will be covering these patterns in more detail in Chapter 7. In this book, we will take a look at a number of popular JavaScript design patterns and explore why certain patterns may be more suitable for your projects than others. Remember that patterns can be applied not just to vanilla JavaScript (i.e., standard JavaScript code), but also to abstracted libraries such as jQuery and Dojo. Before we begin, let’s look at the exact definition of a “pattern” in software design. 2 | Chapter 1: Introduction CHAPTER 2 What Is a Pattern? A pattern is a reusable solution that can be applied to commonly occurring problems in software design—in our case, in writing JavaScript web applications. Another way of looking at patterns is as templates for how we solve problems—ones that can be used in quite a few different situations. So, why is it important to understand patterns and be familiar with them? Design patterns have three main benefits: Patterns are proven solutions. They provide solid approaches to solving issues in software development using proven techniques that reflect the experience and insights the developers that helped define them bring to the pattern. Patterns can be easily reused. A pattern usually reflects an out-of-the-box solution that can be adapted to suit our own needs. This feature makes them quite robust. Patterns can be expressive. When we look at a pattern, there’s generally a set structure and vocabulary to the solution presented that can help express rather large solutions quite elegantly. Patterns are not exact solutions. It’s important that we remember the role of a pattern is merely to provide us with a solution scheme. Patterns don’t solve all design problems, nor do they replace good software designers, however, they do support them. Next, we’ll take a look at some of the other advantages patterns have to offer. • Reusing patterns assists in preventing minor issues that can cause major problems in the application development process. What this means is when code is built on proven patterns, we can afford to spend less time worrying about the structure of our code and more time focusing on the quality of our overall solution. This is because patterns can encourage us to code in a more structured and organized fashion, avoiding the need to refactor it for cleanliness purposes in the future. 3 • Patterns can provide generalized solutions, documented in a fashion that doesn’t require them to be tied to a specific problem. This generalized approach means that, regardless of the application (and in many cases the programming language), we are working with, design patterns can be applied to improve the structure of our code. • Certain patterns can actually decrease the overall file-size footprint of our code by avoiding repetition. By encouraging developers to look more closely at their solutions for areas where instant reductions in repetition can be made—e.g., reducing the number of functions performing similar processes in favor of a single generalized function—the overall size of our codebase can be decreased. This is also known as making code more dry. • Patterns add to a developers vocabulary, which makes communication faster. • Patterns that are frequently used can be improved over time by harnessing the collective experiences other developers using those patterns contribute back to the design pattern community. In some cases, this leads to the creation of entirely new design patterns, while in others it can lead to the provision of improved guidelines on how specific patterns can be best used. This can ensure that pattern-based solutions continue to become more robust than ad hoc solutions may be. We Already Use Patterns Every Day To understand how useful patterns can be, let’s review a very simple element selection problem that the jQuery library solves for us. Imagine that we have a script where for each DOM element found on a page with class “foo,” we wish to increment a counter. What’s the most efficient way to query for this collection of elements? Well, there are a few different ways this problem could be tackled: • Select all of the elements in the page and then store references for them. Next, filter this collection and use regular expressions (or another means) to store only those with the class “foo.” • Use a modern native browser feature such as querySelectorAll() to select all of the elements with the class “foo.” • Use a native feature such as getElementsByClassName() to similarly get back the desired collection. So, which of these options is the fastest? It’s actually the third option by a factor of 8 to 10 times the alternatives. In a real-world application, however, the third option will not work in versions of Internet Explorer below 9; thus, it’s necessary to use the first option when neither of the others is supported. 4 | Chapter 2: What Is a Pattern? Developers using jQuery don’t have to worry about this problem, however, as it’s luckily abstracted away for us using the Facade pattern. As we’ll review in more detail later, this pattern provides a simple set of abstracted interfaces (e.g., $el.css(), $el.ani mate()) to several more complex underlying bodies of code. As we’ve seen, this means less time having to be concerned about implementation-level details. Behind the scenes, the library simply opts for the most optimal approach to selecting elements depending on what our current browser supports, and we just consume the abstraction layer. We’re probably all also familiar with jQuery’s $("selector"). This is significantly easier to use for selecting HTML elements on a page versus having to manually handle opt for getElementById(), getElementsByClassName(), getElementByTagName, and so on. Although we know that querySelectorAll() attempts to solve this problem, compare the effort involved in using jQuery’s Facade interfaces versus choosing the most optimal selection paths ourselves. There’s no contest! Abstractions using patterns offer realworld value. We’ll be looking at this and more design patterns later in the book. We Already Use Patterns Every Day | 5 CHAPTER 3 “Pattern”-ity Testing, Proto-Patterns, and the Rule of Three Remember that not every algorithm, best practice, or solution represents what might be considered a complete pattern. There may be a few key ingredients here that are missing, and the pattern community is generally wary of something claiming to be one unless it has been heavily vetted. Even if something is presented to us that appears to meet the criteria for a pattern, it should not be considered one until it has undergone suitable periods of scrutiny and testing by others. Looking back upon the work by Alexander once more, he claims that a pattern should be both a process and a “thing.” This definition is obtuse on purpose, as he follows by saying that the process should create the “thing.” This is a reason why patterns generally focus on addressing a visually identifiable structure—i.e., we should be able to visually depict the structure that results from the pattern in practice. In studying design patterns, it’s not irregular to come across the term “protopattern”: a pattern that has not yet been known to pass the “pattern”-ity tests. Protopatterns may result from the work of someone who has established a particular solution that is worthy of sharing with the community, but has not yet been vetted heavily due to its very young age. Alternatively, the individual(s) sharing the pattern may not have the time or interest of going through the “pattern”-ity process and might release a short description of their proto-pattern instead. Brief descriptions or snippets of this type of pattern are known as patlets. The work involved in fully documenting a qualified pattern can be quite daunting. Looking back at some of the earliest work in the field of design patterns, a pattern may be considered “good” if it does the following: 7 • Solves a particular problem. Patterns are not supposed to just capture principles or strategies. They need to capture solutions. This is one of the most essential ingredients for a good pattern. • Does not have an obvious solution. We can find that problem-solving techniques often attempt to derive from well-known first principles. The best design patterns usually provide solutions to problems indirectly; this is considered a necessary approach for the most challenging problems related to design. • Describes a proven concept. Design patterns require proof that they function as described, and without this proof, the design cannot be seriously considered. If a pattern is highly speculative in nature, only the brave may attempt to use it. • Describes a relationship. In some cases, it may appear that a pattern describes a type of module. Although an implementation may appear this way, the official description of the pattern must describe much deeper system structures and mechanisms that explain its relationship to code. We would be forgiven for thinking that a proto-pattern that fails to meet guidelines isn’t worth learning from; however, this is far from the truth. Many proto-patterns are actually quite good. I’m not saying that all proto-patterns are worth looking at, but there are quite a few useful ones in the wild that could assist us with future projects. Use your best judgment with the above list in mind, and you’ll be fine in your selection process. One of additional requirement for a pattern to be valid is that it display some recurring phenomenon. This is often something that can be qualified in at least three key areas, referred to as the rule of three. To show recurrence using this rule, one must demonstrate: Fitness of purpose How is the pattern considered successful? Usefulness Why is the pattern considered successful? Applicability Is the design worthy of being a pattern because it has wider applicability? If so, this needs to be explained. 8 | Chapter 3: “Pattern”-ity Testing, Proto-Patterns, and the Rule of Three O’Reilly Ebooks—Your bookshelf on your devices! When you buy an ebook through oreilly.com you get lifetime access to the book, and whenever possible we provide it to you in five, DRM-free file formats—PDF, .epub, Kindle-compatible .mobi, Android .apk, and DAISY—that you can use on the devices of your choice. Our ebook files are fully searchable, and you can cut-and-paste and print them. We also alert you when we’ve updated the files with corrections and additions. Learn more at ebooks.oreilly.com You can also purchase O’Reilly ebooks through the iBookstore, the Android Marketplace, and Amazon.com. Spreading the knowledge of innovators oreilly.com