Searching...
English
EnglishEnglish
EspañolSpanish
简体中文Chinese
FrançaisFrench
DeutschGerman
日本語Japanese
PortuguêsPortuguese
ItalianoItalian
한국어Korean
РусскийRussian
NederlandsDutch
العربيةArabic
PolskiPolish
हिन्दीHindi
Tiếng ViệtVietnamese
SvenskaSwedish
ΕλληνικάGreek
TürkçeTurkish
ไทยThai
ČeštinaCzech
RomânăRomanian
MagyarHungarian
УкраїнськаUkrainian
Bahasa IndonesiaIndonesian
DanskDanish
SuomiFinnish
БългарскиBulgarian
עבריתHebrew
NorskNorwegian
HrvatskiCroatian
CatalàCatalan
SlovenčinaSlovak
LietuviųLithuanian
SlovenščinaSlovenian
СрпскиSerbian
EestiEstonian
LatviešuLatvian
فارسیPersian
മലയാളംMalayalam
தமிழ்Tamil
اردوUrdu
Head First Design Patterns

Head First Design Patterns

by Eric Freeman 2004 638 pages
4.3
9.0K ratings
Listen
Try Full Access for 7 Days
Unlock listening & more!
Continue

Key Takeaways

1. Embrace Change: Encapsulate What Varies and Program to Interfaces.

Identify the aspects of your application that vary and separate them from what stays the same.

The constant of change. Software development is inherently dynamic; requirements evolve, and systems must adapt or perish. A common pitfall is designing rigid systems where changes in one area ripple through unrelated parts of the code. The core principle to combat this is to identify the volatile parts of your application and isolate them. By encapsulating these "varying" aspects, you create boundaries that prevent modifications from causing unintended side effects elsewhere.

Program to abstractions. This encapsulation is best achieved by programming to an interface (or supertype), not a concrete implementation. When your code interacts with an interface, it remains flexible, able to work with any class that implements that interface. This means you can introduce new concrete implementations without altering the client code, adhering to the "open for extension, closed for modification" principle. The Strategy Pattern, for instance, embodies this by encapsulating interchangeable algorithms behind a common interface, allowing clients to vary behavior independently.

2. Favor Composition Over Inheritance for Flexible Designs.

Favor composition over inheritance.

The pitfalls of inheritance. While inheritance offers code reuse, it often leads to rigid designs, especially when behaviors vary across subclasses. Adding new behavior to a superclass can inadvertently affect all subclasses, even those for which the behavior is inappropriate (e.g., a flying rubber duck). Overriding methods in every subclass to disable unwanted behavior creates a maintenance nightmare.

The power of HAS-A. Composition, or the "HAS-A" relationship, offers a more flexible alternative. Instead of inheriting behavior, objects acquire it by being composed with other objects that implement specific behaviors. This allows you to change an object's behavior dynamically at runtime by swapping out its composed behavior object. This approach not only encapsulates families of algorithms into their own classes but also minimizes interdependencies, leading to more adaptable and maintainable systems.

3. Decouple Object Creation with Factories.

The Dependency Inversion Principle: Depend upon abstractions. Do not depend upon concrete classes.

The problem with 'new'. Direct instantiation of concrete classes using the new operator tightly couples your code to specific implementations, making it fragile to change. When new concrete types are introduced or existing ones change, code littered with new statements must be reopened and modified, violating the Open-Closed Principle. This creates a maintenance burden and increases the risk of introducing bugs.

Factories to the rescue. Factory Patterns encapsulate object creation, moving the responsibility for instantiating concrete classes into dedicated factory objects or methods. The Simple Factory is a basic idiom for this, while the Factory Method Pattern lets subclasses decide which concrete class to instantiate, deferring creation to them. The Abstract Factory Pattern takes this further, providing an interface for creating families of related objects without specifying their concrete classes, ensuring consistency across product sets (e.g., regional pizza ingredients). This adherence to the Dependency Inversion Principle ensures both high-level and low-level components depend on abstractions.

4. Extend Functionality Dynamically with Decorators.

The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Avoiding class explosions. Traditional inheritance can lead to a "class explosion" when trying to add multiple, optional responsibilities to objects (ee.g., coffee with various condiments). This results in an unmanageable number of subclasses. The Decorator Pattern offers a superior solution by allowing you to wrap objects with "decorator" objects that add new behaviors.

Runtime flexibility. Decorators share the same supertype as the objects they decorate, enabling transparent wrapping. They add their own behavior before or after delegating to the wrapped object. This means functionality can be extended dynamically at runtime without modifying existing code, perfectly embodying the Open-Closed Principle. A classic example is Java I/O, where streams are wrapped with decorators like BufferedInputStream to add buffering behavior.

5. Adapt Incompatible Interfaces with Adapters.

The Adapter Pattern converts the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.

Bridging the gap. When integrating existing classes or libraries with incompatible interfaces, direct interaction is impossible without modifying one side. The Adapter Pattern acts as a translator, converting one interface into another that a client expects. This allows otherwise incompatible classes to work together seamlessly.

Decoupling clients. An adapter sits between the client and the adaptee, receiving requests from the client (using the target interface) and translating them into calls on the adaptee (using its existing interface). This decouples the client from the adaptee's specific implementation, making the system more resilient to changes in either interface. For instance, a TurkeyAdapter can make a Turkey object appear and behave like a Duck to a client expecting a Duck interface.

6. Keep Objects Informed with the Observer Pattern.

The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.

Loose coupling for notifications. When one object's state changes, and many other objects need to react to that change, a tightly coupled design would require the changing object to know about and directly update all its dependents. This creates a fragile system. The Observer Pattern solves this by establishing a one-to-many relationship where a "Subject" (the changing object) notifies its "Observers" (the dependents) without knowing their concrete types.

Flexible and extensible. Observers register with the Subject, and when the Subject's state changes, it iterates through its list of registered Observers and calls a common update() method on each. This loose coupling means new Observers can be added or removed at any time without modifying the Subject. It's a cornerstone of responsive systems, seen in Java Swing's ActionListeners and event-driven architectures, promoting flexible and extensible designs.

7. Encapsulate Requests for Flexible Invocation: The Command Pattern.

The Command Pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations.

Decoupling action from invoker. In systems like a remote control, the invoker (the button) should not be tightly coupled to the specific actions it triggers (e.g., turning on a light). The Command Pattern addresses this by transforming a request into a standalone object. This "Command" object encapsulates both the action to be performed and the receiver object on which the action will operate.

Powerful capabilities. The invoker simply calls an execute() method on the Command object, without knowing the specifics of the action or receiver. This decoupling enables powerful features:

  • Parameterization: An invoker can be configured with different commands at runtime.
  • Queuing/Logging: Commands can be stored in a queue or logged for later execution or recovery.
  • Undo/Redo: Commands can implement an undo() method to reverse their actions, often by storing previous state.
  • Macros: Multiple commands can be grouped into a single MacroCommand.

8. Simplify Complex Subsystems with Facades.

The Facade Pattern provides a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

Hiding complexity. Modern software often involves complex subsystems with numerous classes and intricate interactions (e.g., a home theater system). Clients interacting directly with such systems face a steep learning curve and tight coupling to many low-level components. The Facade Pattern offers a simplified, unified interface to this complexity.

Principle of Least Knowledge. A Facade class acts as a single entry point, delegating client requests to the appropriate subsystem objects. It doesn't encapsulate the subsystem (clients can still access individual components if needed), but rather provides a convenient, higher-level API. This adheres to the Principle of Least Knowledge ("talk only to your immediate friends"), reducing the number of classes a client directly interacts with. The result is a more maintainable and understandable system, as clients are decoupled from the subsystem's internal workings.

9. Define Algorithm Skeletons with Template Method.

The Template Method Pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.

Controlling the algorithm flow. When multiple classes share a common algorithm but differ in specific steps, the Template Method Pattern provides a powerful solution. It defines the overall structure of an algorithm in a "template method" within an abstract class, while leaving certain steps as abstract methods or "hooks" to be implemented by concrete subclasses.

Hollywood Principle. This pattern embodies the "Hollywood Principle" ("Don't call us, we'll call you"), where the high-level abstract class controls the algorithm's flow and calls upon its low-level subclasses for specific implementations. This ensures the algorithm's structure remains consistent and protected (often by making the template method final), while allowing subclasses to customize individual steps. Examples include java.util.Arrays.sort() (where compareTo() is the hook) and JFrame.paint(), enabling code reuse and framework creation.

10. Manage Object Behavior Through State Objects.

The State Pattern allows an object to alter its behavior when its internal state changes. The object will appear to change its class.

Eliminating conditional spaghetti. Objects whose behavior changes dramatically based on their internal state often lead to complex conditional logic (e.g., if-else or switch statements) within their methods. This makes the code hard to maintain, extend, and understand. The State Pattern offers a clean alternative by encapsulating each state's behavior into its own class.

Delegation and apparent class change. The "Context" object (the object whose state changes) maintains a reference to a "State" object, to which it delegates all state-dependent behavior. When the Context's internal state changes, it simply swaps its current State object for another. From a client's perspective, the Context object appears to change its class, as its behavior is entirely different. While structurally similar to Strategy, State's intent is to manage an object's internal state transitions, with the Context often controlling which state is active.

11. Control Object Access with Proxies.

The Proxy Pattern provides a surrogate or placeholder for another object to control access to it.

The power of a stand-in. When direct access to an object is undesirable or impractical, the Proxy Pattern provides a "surrogate" or "placeholder" object that controls access to the real object. The proxy implements the same interface as the real subject, allowing clients to interact with it as if it were the real thing, while the proxy handles the underlying complexities or restrictions.

Diverse applications. Proxies come in many forms, each controlling access in a different way:

  • Remote Proxy: Acts as a local representative for an object in a different JVM (e.g., Java RMI).
  • Virtual Proxy: Defers the creation of an expensive-to-create object until it's actually needed (e.g., loading large images).
  • Protection Proxy: Controls access to an object's methods based on caller permissions (e.g., user roles).
  • Caching Proxy: Provides temporary storage for expensive operation results.
    This pattern decouples clients from direct interaction with potentially problematic objects, enhancing system robustness and performance.

12. Combine Patterns for Powerful Solutions: MVC.

A compound pattern combines two or more patterns into a solution that solves a recurring or general problem.

Beyond individual patterns. While individual patterns solve specific design problems, their true power often emerges when they are combined into larger, more comprehensive solutions. These "compound patterns" address broader architectural challenges by leveraging the strengths of multiple patterns working in concert.

Model-View-Controller (MVC). A prime example is MVC, a foundational compound pattern for user interfaces:

  • Model: Manages application data and logic, using the Observer Pattern to notify views and controllers of state changes.
  • View: Presents the model's data to the user, often structured using the Composite Pattern for UI components, and delegates user input to the controller via the Strategy Pattern.
  • Controller: Interprets user input and translates it into actions on the model, acting as the view's strategy.
    MVC decouples these concerns, promoting reusability, flexibility, and maintainability, and has been widely adapted across various application types, including web frameworks.

Last updated:

Want to read the full book?

Review Summary

4.3 out of 5
Average of 9.0K ratings from Goodreads and Amazon.

Head First Design Patterns receives overwhelmingly positive reviews for its engaging and accessible approach to teaching design patterns. Readers praise its use of humor, real-world examples, and interactive learning techniques. Many find it superior to traditional textbooks, especially for beginners. The book is commended for its clear explanations, practical examples, and emphasis on when to use patterns. Some readers note that not all patterns are covered in depth, but overall, it's highly recommended as an introduction to design patterns for programmers of various experience levels.

Your rating:
4.68
3 ratings

About the Author

Eric Freeman is a computer scientist, author, and educator known for his work in software development and design patterns. He co-authored the popular "Head First" series of technical books, which use innovative teaching methods to make complex topics more accessible. Freeman's writing style is characterized by its use of humor, visual aids, and practical examples to engage readers. He has extensive experience in software engineering and has worked for major technology companies. Freeman's approach to teaching design patterns has been widely praised for its effectiveness in helping both novice and experienced programmers understand and apply these important concepts in their work.

Listen
Now playing
Head First Design Patterns
0:00
-0:00
Now playing
Head First Design Patterns
0:00
-0:00
1x
Voice
Speed
Dan
Andrew
Michelle
Lauren
1.0×
+
200 words per minute
Queue
Home
Swipe
Library
Get App
Create a free account to unlock:
Recommendations: Personalized for you
Requests: Request new book summaries
Bookmarks: Save your favorite books
History: Revisit books later
Ratings: Rate books & see your ratings
250,000+ readers
Try Full Access for 7 Days
Listen, bookmark, and more
Compare Features Free Pro
📖 Read Summaries
Read unlimited summaries. Free users get 3 per month
🎧 Listen to Summaries
Listen to unlimited summaries in 40 languages
❤️ Unlimited Bookmarks
Free users are limited to 4
📜 Unlimited History
Free users are limited to 4
📥 Unlimited Downloads
Free users are limited to 1
Risk-Free Timeline
Today: Get Instant Access
Listen to full summaries of 73,530 books. That's 12,000+ hours of audio!
Day 4: Trial Reminder
We'll send you a notification that your trial is ending soon.
Day 7: Your subscription begins
You'll be charged on Jan 7,
cancel anytime before.
Consume 2.8× More Books
2.8× more books Listening Reading
Our users love us
250,000+ readers
Trustpilot Rating
TrustPilot
4.6 Excellent
This site is a total game-changer. I've been flying through book summaries like never before. Highly, highly recommend.
— Dave G
Worth my money and time, and really well made. I've never seen this quality of summaries on other websites. Very helpful!
— Em
Highly recommended!! Fantastic service. Perfect for those that want a little more than a teaser but not all the intricate details of a full audio book.
— Greg M
Save 62%
Yearly
$119.88 $44.99/year/yr
$3.75/mo
Monthly
$9.99/mo
Start a 7-Day Free Trial
7 days free, then $44.99/year. Cancel anytime.
Scanner
Find a barcode to scan

We have a special gift for you
Open
38% OFF
DISCOUNT FOR YOU
$79.99
$49.99/year
only $4.16 per month
Continue
2 taps to start, super easy to cancel
Settings
General
Widget
Loading...
We have a special gift for you
Open
38% OFF
DISCOUNT FOR YOU
$79.99
$49.99/year
only $4.16 per month
Continue
2 taps to start, super easy to cancel