Discover millions of ebooks, audiobooks, and so much more with a free trial

Only $11.99/month after trial. Cancel anytime.

Dependency Injection: Design patterns using Spring and Guice
Dependency Injection: Design patterns using Spring and Guice
Dependency Injection: Design patterns using Spring and Guice
Ebook649 pages5 hours

Dependency Injection: Design patterns using Spring and Guice

Rating: 0 out of 5 stars

()

Read preview

About this ebook

Dependency Injection is an in-depth guide to the current best practices forusing the Dependency Injection pattern-the key concept in Spring and therapidly-growing Google Guice. It explores Dependency Injection, sometimescalled Inversion of Control, in fine detail with numerous practical examples.Developers will learn to apply important techniques, focusing on their strengthsand limitations, with a particular emphasis on pitfalls, corner-cases, and bestpractices.

This book is written for developers and architects who want to understandDependency Injection and successfully leverage popular DI technologies such asSpring, Google Guice, PicoContainer, and many others. The book exploresmany small examples of anchor concepts and unfolds a larger example to showthe big picture.

Written primarily from a Java point-of-view, this book is appropriate for anydeveloper with a working knowledge of object-oriented programming in Java,Ruby, or C#.

Purchase of the print book comes with an offer of a free PDF, ePub, and Kindle eBook from Manning. Also available is all code from the book.
LanguageEnglish
PublisherManning
Release dateJul 31, 2009
ISBN9781638353010
Dependency Injection: Design patterns using Spring and Guice

Related to Dependency Injection

Related ebooks

Programming For You

View More

Related articles

Reviews for Dependency Injection

Rating: 0 out of 5 stars
0 ratings

0 ratings0 reviews

What did you think?

Tap to rate

Review must be at least 10 words

    Book preview

    Dependency Injection - Dhananjay Prasanna

    Copyright

    For online information and ordering of this and other Manning books, please visit www.manning.com. The publisher offers discounts on this book when ordered in quantity. For more information, please contact

    Special Sales Department

    Manning Publications Co.

    Sound View Court 3B   fax: (609) 877-8256

    Greenwich, CT 06830   email: orders@manning.com

    ©2009 by Manning Publications Co. All rights reserved.

    No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, mechanical, photocopying, or otherwise, without prior written permission of the publisher.

    Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in the book, and Manning Publications was aware of a trademark claim, the designations have been printed in initial caps or all caps.

    Recognizing the importance of preserving what has been written, it is Manning’s policy to have the books we publish printed on acid-free paper, and we exert our best efforts to that end. Recognizing also our responsibility to conserve the resources of our planet, Manning books are printed on paper that is at least 15% recycled and processed without the use of elemental chlorine.

    Printed in the United States of America

    1 2 3 4 5 6 7 8 9 10 – MAL – 14 13 12 11 10 09

    Dedication

    To my parents

    "If you want to make an apple pie from scratch, you must first create the universe."

    Carl Sagan

    Brief Table of Contents

    Copyright

    Brief Table of Contents

    Table of Contents

    Praise from the Creator of Guice

    Preface

    Acknowledgments

    About this Book

    About the Cover Illustration

    Chapter 1. Dependency injection: what’s all the hype?

    Chapter 2. Time for injection

    Chapter 3. Investigating DI

    Chapter 4. Building modular applications

    Chapter 5. Scope: a fresh breath of state

    Chapter 6. More use cases in scoping

    Chapter 7. From birth to death: object lifecycle

    Chapter 8. Managing an object’s behavior

    Chapter 9. Best practices in code design

    Chapter 10. Integrating with third-party frameworks

    Chapter 11. Dependency injection in action!

    Appendix A. The Butterfly Container

    Appendix B. SmartyPants for Adobe Flex

    Index

    List of Figures

    List of Tables

    List of Listings

    Table of Contents

    Copyright

    Brief Table of Contents

    Table of Contents

    Praise from the Creator of Guice

    Preface

    Acknowledgments

    About this Book

    About the Cover Illustration

    Chapter 1. Dependency injection: what’s all the hype?

    1.1. Every solution needs a problem

    1.1.1. Seeing objects as services

    1.2. Pre-DI solutions

    1.2.1. Construction by hand

    1.2.2. The Factory pattern

    1.2.3. The Service Locator pattern

    1.3. Embracing dependency injection

    1.3.1. The Hollywood Principle

    1.3.2. Inversion of Control vs. dependency injection

    1.4. Dependency injection in the real world

    1.4.1. Java

    1.4.2. DI in other languages and libraries

    1.5. Summary

    Chapter 2. Time for injection

    2.1. Bootstrapping the injector

    2.2. Constructing objects with dependency injection

    2.3. Metadata and injector configuration

    2.3.1. XML injection in Spring

    2.3.2. From XML to in-code configuration

    2.3.3. Injection in PicoContainer

    2.3.4. Revisiting Spring and autowiring

    2.4. Identifying dependencies for injection

    2.4.1. Identifying by string keys

    2.4.2. Limitations of string keys

    2.4.3. Identifying by type

    2.4.4. Limitations of identifying by type

    2.4.5. Combinatorial keys: a comprehensive solution

    2.5. Separating infrastructure and application logic

    2.6. Summary

    Chapter 3. Investigating DI

    3.1. Injection idioms

    3.1.1. Constructor injection

    3.1.2. Setter injection

    3.1.3. Interface injection

    3.1.4. Method decoration (or AOP injection)

    3.2. Choosing an injection idiom

    3.2.1. Constructor vs. setter injection

    3.2.2. The constructor pyramid problem

    3.2.3. The circular reference problem

    3.2.4. The in-construction problem

    3.2.5. Constructor injection and object validity

    3.3. Not all at once: partial injection

    3.3.1. The reinjection problem

    3.3.2. Reinjection with the Provider pattern

    3.3.3. The contextual injection problem

    3.3.4. Contextual injection with the Assisted Injection pattern

    3.3.5. Flexible partial injection with the Builder pattern

    3.4. Injecting objects in sealed code

    3.4.1. Injecting with externalized metadata

    3.4.2. Using the Adapter pattern

    3.5. Summary

    Chapter 4. Building modular applications

    4.1. Understanding the role of an object

    4.2. Separation of concerns (my pants are too tight!)

    4.2.1. Perils of tight coupling

    4.2.2. Refactoring impacts of tight coupling

    4.2.3. Programming to contract

    4.2.4. Loose coupling with dependency injection

    4.3. Testing components

    4.3.1. Out-of-container (unit) testing

    4.3.2. I really need my dependencies!

    4.3.3. More on mocking dependencies

    4.3.4. Integration testing

    4.4. Different deployment profiles

    4.4.1. Rebinding dependencies

    4.4.2. Mutability with the Adapter pattern

    4.5. Summary

    Chapter 5. Scope: a fresh breath of state

    5.1. What is scope?

    5.2. The no scope (or default scope)

    5.3. The singleton scope

    5.3.1. Singletons in practice

    5.3.2. The singleton anti-pattern

    5.4. Domain-specific scopes: the web

    5.4.1. HTTP request scope

    5.4.2. HTTP session scope

    5.5. Summary

    Chapter 6. More use cases in scoping

    6.1. Defining a custom scope

    6.1.1. A quick primer on transactions

    6.1.2. Creating a custom transaction scope

    6.1.3. A custom scope in Guice

    6.1.4. A custom scope in Spring

    6.2. Pitfalls and corner cases in scoping

    6.2.1. Singletons must be thread-safe

    6.2.2. Perils of scope-widening injection

    6.3. Leveraging the power of scopes

    6.3.1. Cache scope

    6.3.2. Grid scope

    6.3.3. Transparent grid computing with DI

    6.4. Summary

    Chapter 7. From birth to death: object lifecycle

    7.1. Significant events in the life of objects

    7.1.1. Object creation

    7.1.2. Object destruction (or finalization)

    7.2. One size doesn’t fit all (domain-specific lifecycle)

    7.2.1. Contrasting lifecycle scenarios: servlets vs. database connections

    7.2.2. The Destructor anti-pattern

    7.2.3. Using Java’s Closeable interface

    7.3. A real-world lifecycle scenario: stateful EJBs

    7.4. Lifecycle and lazy instantiation

    7.5. Customizing lifecycle with postprocessing

    7.6. Customizing lifecycle with multicasting

    7.7. Summary

    Chapter 8. Managing an object’s behavior

    8.1. Intercepting methods and AOP

    8.1.1. A tracing interceptor with Guice

    8.1.2. A tracing interceptor with Spring

    8.1.3. How proxying works

    8.1.4. Too much advice can be dangerous!

    8.2. Enterprise use cases for interception

    8.2.1. Transactional methods with warp-persist

    8.2.2. Securing methods with Spring Security

    8.3. Pitfalls and assumptions about interception and proxying

    8.3.1. Sameness tests are unreliable

    8.3.2. Static methods cannot be intercepted

    8.3.3. Neither can private methods

    8.3.4. And certainly not final methods!

    8.3.5. Fields are off limits

    8.3.6. Unit tests and interception

    8.4. Summary

    Chapter 9. Best practices in code design

    9.1. Objects and visibility

    9.1.1. Safe publication

    9.1.2. Safe wiring

    9.2. Objects and design

    9.2.1. On data and services

    9.2.2. On better encapsulation

    9.3. Objects and concurrency

    9.3.1. More on mutability

    9.3.2. Synchronization vs. concurrency

    9.4. Summary

    Chapter 10. Integrating with third-party frameworks

    10.1. Fragmentation of DI solutions

    10.2. Lessons for framework designers

    10.2.1. Rigid configuration anti-patterns

    10.2.2. Black box anti-patterns

    10.3. Programmatic configuration to the rescue

    10.3.1. Case study: JSR-303

    10.4. Summary

    Chapter 11. Dependency injection in action!

    11.1. Crosstalk: a Twitter clone!

    11.1.1. Crosstalk’s requirements

    11.2. Setting up the application

    11.3. Configuring Google Sitebricks

    11.4. Crosstalk’s modularity and service coupling

    11.5. The presentation layer

    11.5.1. The HomePage template

    11.5.2. The Tweet domain object

    11.5.3. Users and sessions

    11.5.4. Logging in and out

    11.6. The persistence layer

    11.6.1. Configuring the persistence layer

    11.7. The security layer

    11.8. Tying up to the web lifecycle

    11.9. Finally: up and running!

    11.10. Summary

    Appendix A. The Butterfly Container

    A.1. A DSL for dependency injection

    A.2. Application configuration

    A.2.1. The DSL basics

    A.2.2. The Factory chain

    A.2.3. Contextual injection via input parameters

    A.2.4. Reinjection via factory injection

    Appendix B. SmartyPants for Adobe Flex

    B.1. The class+name key

    B.1.1. Injection annotations

    B.2. Injector rules

    B.2.1. But how do I kickstart the whole thing?

    Index

    List of Figures

    List of Tables

    List of Listings

    Praise from the Creator of Guice

    Dhanji lives on the bleeding edge. He’s been around Google Guice since the beginning. Less than a week after we released Guice 1.0, Dhanji refactored a non-trivial Swing application to use Guice. A ThoughtWorks consultant at the time, he measured dramatic design improvements and performance and reliability gains. As a result, Dhanji sent the Guice team some of our first kudos, not to mention one of our first external feature requests.

    Building on his extensive experience developing enterprise Java standards, Dhanji went on to receive his own praise when he released Warp Persist and Warp Servlets, two essential Guice extensions that respectively integrate with standard Java persistence and web APIs. His Warp frameworks seamlessly extend Guice’s fluent, plain-Java configuration style to the enterprise.

    Warp Persist provides JPA integration and declarative transaction support, two indispensable components of a modern enterprise Java stack. Warp Persist has no doubt been key to Guice’s success in this space.

    Dhanji became a key Guice team member when, in Guice 2, Warp Servlets supplanted Guice’s default servlet module. Nowadays, Dhanji works with us at Google on one of the most notable Guice-based applications: Google Wave.

    JavaOne attendees voted Dhanji a Rock Star speaker and his popular presentation style translates well to print. Dhanji’s unique combination of experience as both a framework designer and an in-the-trenches dependency injection user results in deep yet accessible explanations. Advice like his comes only from years of eating one’s own dog food.

    This book is not a reference. While most dependency injection documentation focuses on the mechanics of individual frameworks, this book covers customary and effective use of dependency injection, especially with respect to application design. It fills a much needed gap.

    While I’m obviously biased toward Guice, I’m happy to see that this book transcends implementation details and covers what will no doubt continue to become an essential part of day-to-day Java development. One day, we’ll look at dependency injection support as just another language feature, a construct for importing instances. This book prepares you for that day.

    BOB LEE

    SOFTWARE ENGINEER, GOOGLE INC. AND CREATOR OF GUICE

    Preface

    The reasonable man adapts himself to the world; the unreasonable one persists in trying to adapt the world to himself. Therefore, all progress depends on the unreasonable man.

    George Bernard Shaw

    So, novice-expert-aficionado-learner, welcome to our dependency injection theme park! We’ll begin this tour of dependency injection at the gate: by first defining the problem it solves.

    The tour will then proceed down the streams of specific implementations and popular technologies, examining quick solutions in such frameworks as Guice, Spring, and PicoContainer, among others. A long break will follow for the purpose of theoretical nourishment, where we shall discuss the foundational idioms behind dependency injection, its core viral ideology, its types and practices.

    Then, we’ll peruse the hall of curiosities where we consider such peculiar notions as objects with managed state, lifecycle, and modularity and examine their curious aspectual behaviors.

    In turn, we’ll descend into the caverns of pitfalls, nuances, and corner cases that ought to titillate and horrify the strongest of constitutions; for when you cross these lines you can expect the demons of class loading, dynamic proxying, and unintended encapsulation to rear their ugly heads, only to be shown up by even greater challenges of concurrency.

    We’ll make a round about the marvelous gilded architectures of enterprise applications, casting a purposeful eye at their grandeur, which is composed of modular, remote-able service models that separate cleanly between compartments of the enterprise—particularly those nefarious tiers, web and business.

    These will leave us with a meditation on the big picture—how we can marry these elegant idioms of dependency injection with their many suitors: applications, third-party libraries, and frameworks.

    Finally, dear tourist, you will be presented with a purposed comparison of popular dependency injection frameworks, gauging their performance, features, safety, and rigor (as a functioning example in chapter 11).

    The theme park metaphor aside, I have attempted to structure this book as a journey. While the ideas and engineering patterns explored in this book do not refer to a particular language, many of the examples will be in Java. The frameworks we explore will primarily be well-known Java libraries that are widely in use.

    A basic understanding of Java (or C#) is preferable; however, for you to get the best out of this book I also recommend familiarity with the principles of object-oriented programming (particularly encapsulation, polymorphism, and inheritance).

    Acknowledgments

    I would not be in the position of having authored this book without the grace and assistance of many people who have been involved in the project, and who have put up with my taxing demeanor during the months of development and writing it took to complete. To all of them, I owe an unrepayable debt.

    Foremost, Bob Lee: from the first email I sent him, and every single one since, he has shown me nothing but kindness and encouragement. He not only recommended publishers for my book, but was also instrumental in my joining Google, where I now work on interesting and compelling projects. I am fortunate to count him as a colleague and a friend.

    Jesse Wilson, the Guice team lead and the technical reviewer for this book, has been one of my most stalwart defenders, critics, and friends in recent months. Jesse never tires or loses cheer and is infectious. Large parts of my work at Google, in open source, and of this book are owed to him.

    Apache Wicket Developer, Manning author, and good friend Eelco Hillenius was responsible for directing me to Manning. He went out on a proverbial limb, championing me and Guice to Manning. Acquisitions editor Mike Stephens was the one who suggested the idea of a software engineering book focusing on design patterns. But most important to this book’s development was Tom Cirtin, my editor at Manning. Tom’s generosity, sense of humor, and keen eye helped me more than I can express. He pushed me when he had to, and politely steered me from disaster wherever it brewed. I applaud and thank him for his efforts.

    Tom Wilson, my team lead at ThoughtWorks, put up with months of my trying behavior while having the courage to listen to every worthwhile idea I brought up and to shoot down every worthless one. Paul Hammant, the PicoContainer author, carefully reviewed many of my early drafts and suggested many corrections. Jon Tirsen, Kevin Bourrillion, Josh Bloch, and Ghassan Mishergi at Google always had time for my rantings and my incessant and often obtuse questioning. Concurrency experts Tim Peierls and Brian Goetz corrected me at different times about pants being up or down. Mike Brock and Michael Neale from Red Hat kept me honest by telling me when I was being silly. Brad Dwyer, Jody Elson, and Dr. George Corliss never refused a request to read or comment on drafts. Thanks also to Robbie Vanbrabant, who contributes to warp-persist, and Mike Bain, my great friend and the best iteration manager ThoughtWorks has ever had.

    A special note of thanks to the many reviewers who read my manuscript a number of times during development, contributing feedback and insights along the way that helped make this a much better book. I am grateful to them for their time and efforts: Dan Dobrin, Frank Wang, Paul King, Rick Wagner, Robert Hanson, Shreekanth Joshi, Wahid Sadik, Dave Corun, Sam Newman, Andrew Konkol, David Strong, Patrick Steger, Jon Skeet, Martyn Fletcher, Marcus Baker, Howard Lewis Ship, Mark Seemann, Ramarao Kanneganti, and King Wang.

    I would also like to thank the production team at Manning for turning my manuscript into the book you are now reading: copyeditor Linda Recktenwald, proofreader Elizabeth Martin, typesetter Gordan Salinovic, cover designer Leslie Haimes, and project manager Mary Piergies.

    I owe a massive debt to Josh McDonald, who created the elegant and subtle theme that is in all the illustrations and who was kind enough to draw many of them. Also for his contributions to my open source project work as well as the many beers he has put back on my account.

    Special words of appreciation to Yutgong Au, my close friend and closet artistic genius, who created many of the icons and line drawings in this book, producing them at a moment’s notice any time I asked for them.

    And to Zac Steacy for being my all around best friend, every day.

    About this Book

    This book is intended for newcomers to dependency injection as well as for seasoned framework users. It is a guide to design patterns and best practices when using dependency injection, an increasingly indispensable tool in any programmer’s arsenal.

    Who should read this book

    The book is for anyone who writes code and is interested in learning best practices for designing large, developer-scale applications. It is also intended for an audience of architects and professionals seeking to identify and correct common anti-patterns in design. All the examples are written in Java using Spring or Guice. But the lessons apply equally to other statically typed languages like C#. Whether you’re a developer, architect, team lead, or novice programmer, you will find this book useful.

    Roadmap

    The book is divided into 11 chapters.

    Chapters 1-4 provide a thorough introduction to testability and the reasons for using dependency injection, its various design patterns, and usage idioms, particularly how to choose these patterns and apply them in various situations.

    Chapters 5-6 focus on scopes, which, are a way of managing state in your application without polluting logic with infrastructure concerns. They also explore deep problems with common idioms such as the static singleton anti-pattern and solutions using dependency injection, and examine scopes for web applications and other purposes.

    Chapter 7 talks about how to manage your application’s lifecycle via the dependency injector. This involves sending objects notifications of important events in the life of the application and designing custom lifecycle infrastructure for your own uses.

    Chapter 8 deals with aspect oriented programming (or AOP) as it applies to dependency injection, how it helps save on boilerplate code, and reduces the risk of errors. A large part of this chapter is devoted to potential problems and pitfalls in using this powerful technique.

    Chapters 9-11 investigate concurrency and design and look at case studies in performance, design, and design integration with third party frameworks. Chapter 11 is a case study on how to build a complete web application using the concepts presented throughout the book.

    Finally, there are two appendixes that discuss alternate approaches to dependency injection frameworks, SmartyPantsIOC for Flex and the Butterfly Container.

    Code conventions and downloads

    All source code in listings or in the text is in a fixed-width font like this to separate it from ordinary text. In listings, bold code indicates code that has changed from the previous example, or that will change in the next example. Code annotations accompany some of the listings, highlighting important concepts. In some cases, numbered bullets link to explanations that follow in the text.

    You can download the source code for this book from the publisher’s website at www.manning.com/DependencyInjection.

    Software requirements

    To use the code in this book, you need the Sun Java SDK (which is free) running on Linux, Windows, or Mac OS X. You’ll also find it useful to download libraries for the Spring Framework (http://springframework.org) and Google Guice (http://code.google.com/p/google-guice). Both are open source and free to download from the links provided. Other tools will be referenced where they’re relevant. At your discretion you may choose to use a Java IDE such as IntelliJ IDEA, but this is not strictly necessary.

    Author Online

    Purchase of Dependency Injection includes free access to a private web forum run by Manning Publications where you can make comments about the book, ask technical questions, and receive help from the author and from other users. To access the forum and subscribe to it, point your web browser to http://www.manning.com/DependencyInjection. This page provides information on how to get on the forum once you are registered, what kind of help is available, and the rules of conduct on the forum. It also provides links to the source code for the examples in the book, errata, and other downloads.

    Manning’s commitment to our readers is to provide a venue where a meaningful dialog between individual readers and between readers and the authors can take place. It is not a commitment to any specific amount of participation on the part of the author, whose contribution to the Author Online remains voluntary (and unpaid). We suggest you try asking the author some challenging questions lest his interest stray!

    You can also ask the author a question at http://twitter.com/dhanji.

    About the Cover Illustration

    The figure on the cover of Dependency Injection is captioned Le Créole de Cayenne, a descendent of colonialists in French Guiana, an overseas region of France located in South America. Cayenne is the capital of French Guiana. The city stands on a former island at the mouth of the Cayenne River on the Atlantic coast. The illustration is taken from a 19th-century edition of Sylvain Maréchal’s four-volume compendium of regional and world dress customs published in France. Each illustration is finely drawn and colored by hand.

    The rich variety of Maréchal’s collection reminds us vividly of how culturally apart the world’s towns and regions were just 200 years ago. Isolated from each other, people spoke different dialects and languages. In the streets or in the countryside, it was easy to identify where they lived and what their trade or station in life was just by their dress.

    Dress codes have changed since then and the diversity by region and nation, so rich at the time, has faded away. It is now hard to tell apart the inhabitants of different continents, let alone different towns or regions. Perhaps we have traded cultural diversity for a more varied personal life—certainly for a more varied and fast-paced technological life.

    At a time when it is hard to tell one computer book from another, Manning celebrates the inventiveness and initiative of the computer business with book covers based on the rich diversity of regional life of two centuries ago, brought back to life by Maréchal’s pictures.

    Chapter 1. Dependency injection: what’s all the hype?

    This chapter covers:

    Seeing an object as a service

    Learning about building and assembling services

    Taking a tour of pre-existing solutions

    Investigating the Hollywood Principle

    Surveying available frameworks

    We all agree that your theory is crazy, but is it crazy enough?

    Niels Bohr

    So you’re an expert on dependency injection (DI); you know it and use it every day. It’s like your morning commute—you sleepwalk through it, making all the right left turns (and the occasional wrong right turns before quickly correcting) until you’re comfortably sitting behind your desk at work. Or you’ve heard of DI and Inversion of Control (IoC) and read the occasional article, in which case this is your first commute to work on a new job and you’re waiting at the station, with a strong suspicion you are about to get on the wrong train and an even stronger suspicion you’re on the wrong platform.

    Or you’re somewhere in between; you’re feeling your way through the idea, not yet fully convinced about DI, planning out that morning commute and looking for the best route to work, MapQuesting it. Or you have your own home-brew setup that works just fine, thank you very much. You’ve no need of a DI technology: You bike to work, get a lot of exercise on the way, and are carbon efficient.

    Stop! Take a good, long breath. Dependency injection is the art of making work come home to you.

    1.1. Every solution needs a problem

    Most software today is written to automate some real-world process, whether it be writing a letter, purchasing the new album from your favorite band, or placing an order to sell some stock. In object-oriented programming (OOP), these are objects and their interactions are methods.

    Objects represent their real-world counterparts. An Airplane represents a 747 and a Car represents a Toyota; a PurchaseOrder represents you buying this book; and so on.

    Of particular interest is the interaction between objects: An airplane flies, while a car can be driven and a book can be opened and read. This is where the value of the automation is realized and where it is valuable in simplifying our lives.

    Take the familiar activity of writing an email; you compose the message using an email application (like Mozilla Thunderbird or Gmail) and you send it across the internet to one or more recipients, as in figure 1.1. This entire activity can be modeled as the interaction of various objects.

    Figure 1.1. Email is composed locally, delivered across an internet relay, and received by an inbox.

    This highlights an important precept in this book: the idea of an object acting as a service. In the example, email acts as a message composition service, internet relays are delivery agents, and my correspondent’s inbox is a receiving service.

    1.1.1. Seeing objects as services

    The process of emailing a correspondent can be reduced to the composing, delivering, and receiving of email by each responsible object, namely the Emailer, InternetRelay, and RecipientInbox. Each object is a client of the next.

    Emailer uses the InternetRelay as a service to send email, and in turn, the InternetRelay uses the RecipientInbox as a service to deliver sent mail.

    The act of composing an email can be reduced to more granular tasks:

    Writing the message

    Checking spelling

    Looking up a recipient’s address

    And so on. Each is a fairly specialized task and is modeled as a specific service. For example, writing the message falls into the domain of editing text, so choosing a TextEditor is appropriate. Modeling the TextEditor in this fashion has many advantages over extending Emailer to write text messages itself: We know exactly where to look if we want to find out what the logic looks like for editing text.

    Our Emailer is not cluttered with distracting code meant for text manipulation.

    We can reuse the TextEditor component in other scenarios (say, a calendar or note-taking application) without much additional coding.

    If someone else has written a general-purpose text-editing component, we can make use of it rather than writing one from scratch.

    Similarly, checking spelling is done by a SpellChecker. If we wanted to check spelling in a different language, it would not be difficult to swap out the English SpellChecker in favor of a French one. Emailer itself would not need to worry about checking spelling—French, English, or otherwise.

    So now we’ve seen the value of decomposing our services into objects. This principle is important because it highlights the relationship between one object and others it uses to perform a service: An object depends on its services to perform a function.

    In our example, the Emailer depends on a SpellChecker, a TextEditor, and an AddressBook. This relationship is called a dependency. In other words, Emailer is a client of its dependencies.

    Composition also applies transitively; an object may depend on other objects that themselves have dependencies, and so on. In our case, SpellChecker may depend on a Parser to recognize words and a Dictionary of valid words. Parser and Dictionary may themselves depend on other objects.

    This composite system of dependencies is commonly called an object graph. This object graph, though composed of many dependencies, is functionally a single unit.

    Let’s sum up what we have so far:

    Service—An object that performs a well-defined function when called upon

    Client—Any consumer of a service; an object that calls upon a service to perform a well-understood function

    The service–client relationship implies a clear contract between the objects in the role of performing a specific function that is formally understood by both entities. You will also hear them referred to as:

    Dependency—A specific service that is required by another object to fulfill its function.

    Dependent—A client object that needs a dependency (or dependencies) in order to perform its function.

    Not only can you describe an object graph as a system of discrete services and clients, but you also begin to see that a client cannot function without its services. In other words, an object cannot function properly without its dependencies.

    Note

    DI as a subject is primarily concerned with reliably and efficiently building such object graphs and the strategies, patterns, and best practices therein.

    Let’s look at ways of building object graphs before we take on DI.

    1.2. Pre-DI solutions

    Figure 1.2 shows a simple relationship between an object and its dependency.

    Figure 1.2. Object Emailer is composed of another object, SpellChecker.

    Were you asked to code such a class without any other restrictions, you might attempt something like this:

    public class Emailer {

        private SpellChecker spellChecker;

        public Emailer() {

            this.spellChecker = new SpellChecker();

        }

        public void send(String text) { .. }

    }

    Then constructing a working Emailer (one with a SpellChecker) is as simple as constructing an Emailer itself:

    Emailer emailer = new Emailer();

    No doubt you have written code like this at some point. I certainly have. Now, let’s say you want to write a unit test for the send() method to ensure that it is checking spelling before sending any message. How would you do it? You might create a mock SpellChecker and give that to the Emailer. Something like:

    Of course, we can’t use this mock because we are unable to substitute the internal spellchecker that an Emailer has. This effectively makes your class untestable, which is a showstopper for this approach.

    This approach also prevents us from creating objects of the same class with different behaviors. See listing 1.1 and figure 1.3.

    Listing 1.1. An email service that checks spelling in English

    public class Emailer {

        private SpellChecker spellChecker;

        public Emailer() {

            this.spellChecker = new EnglishSpellChecker();

        }

        ...

    }

    Figure 1.3. An email service that checks spelling in English

    In this example, the Emailer has an EnglishSpellChecker. Can we create an Emailer with a FrenchSpellChecker? We cannot! Emailer encapsulates the creation of its dependencies.

    What we need is a more flexible solution: construction by hand (sometimes called manual dependency injection), where instead of a dependent creating its own dependencies, it has them provided externally.

    1.2.1. Construction by hand

    Naturally, the solution is not to encapsulate the creation of dependencies, but what does this mean and to whom do we offload this burden? Several techniques can solve this problem. In the earlier section, we used the object’s constructor to create its dependencies. With a slight modification, we can keep the structure of the object graph but offload the burden of creating dependencies. Here is such a modification (see figure 1.4):

    public class Emailer {

        private SpellChecker spellChecker;

        public void setSpellChecker(SpellChecker spellChecker) {

            this.spellChecker = spellChecker;

        }

        ...

    }

    Figure 1.4. An email service that checks spelling using an abstract spelling service

    Notice that I’ve replaced the constructor that created its own SpellChecker with a method that accepts a SpellChecker. Now we can construct an Emailer and substitute a mock SpellChecker:

    Similarly, it is easy to construct Emailers with various behaviors. Here’s one for French spelling:

    Emailer service = new Emailer();

    service.setSpellChecker(new FrenchSpellChecker());

    And one for Japanese:

    Emailer service = new Emailer();

    service.setSpellChecker(new JapaneseSpellChecker());

    Cool! At the time of creating the Emailer, it’s up to you to provide its dependencies. This allows you to choose particular flavors of its services that suit your needs (French, Japanese, and so on) as shown in figure 1.5.

    Figure 1.5. The same Emailer class can now check spelling in a variety of languages.

    Since you end up connecting the pipes yourself at the time of construction, this technique is referred to as construction by hand. In the previous example we used a setter method (a method that accepts a value and sets it as a dependency). You can also pass in the dependency via a constructor, as per the following example:

    public class Emailer {

        private SpellChecker spellChecker;

        public Emailer(SpellChecker spellChecker) {

            this.spellChecker = spellChecker;

        }

        ...

    }

    Then creating the Emailer is even more concise:

    Emailer service = new Emailer(new JapaneseSpellChecker());

    This technique is called constructor injection and has the advantage of being explicit about its contract—you can

    Enjoying the preview?
    Page 1 of 1