Course Overview
This course provides a comprehensive overview of Design Patterns in Java from a practical perspective. This course in particular covers patterns with the use of:
The latest versions of the Java programming language
Use of modern programming approaches: dependency injection, reactive programming and more
Use of modern developer tools such as IntelliJ IDEA
Discussions of pattern variations and alternative approaches
Course Overview
This course provides a comprehensive overview of Design Patterns in Java from a practical perspective. This course in particular covers patterns with the use of:
The latest versions of the Java programming language
Use of modern programming approaches: dependency injection, reactive programming and more
Use of modern developer tools such as IntelliJ IDEA
Discussions of pattern variations and alternative approaches
This course provides an overview of all the Gang of Four (GoF) design patterns as outlined in their seminal book, together with modern-day variations, adjustments, discussions of intrinsic use of patterns in the language.
What are Design Patterns?
Design Patterns are reusable solutions to common programming problems. They were popularized with the 1994 book Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, John Vlissides, Ralph Johnson and Richard Helm (who are commonly known as a Gang of Four, hence the GoF acronym).
The original book was written using C++ and Smalltalk as examples, but since then, design patterns have been adapted to every programming language imaginable: C#, Java, PHP and even programming languages that aren't strictly object-oriented, such as JavaScript.
The appeal of design patterns is immortal: we see them in libraries, some of them are intrinsic in programming languages, and you probably use them on a daily basis even if you don't realize they are there.
What Patterns Does This Course Cover?
This course covers all the GoF design patterns. In fact, here's the full list of what is covered:
SOLID Design Principles: Single Responsibility Principle, Open-Closed Principle, Liskov Substitution Principle, Interface Segregation Principle and Dependency Inversion Principle
Creational Design Patterns: Builder, Factories (Factory Method and Abstract Factory), Prototype and Singleton
Structrural Design Patterns: Adapter, Bridge, Composite, Decorator, Façade, Flyweight and Proxy
Behavioral Design Patterns: Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Null Object, Observer, State, Strategy, Template Method and Visitor
Who Is the Course For?
This course is for Java developers who want to see not just textbook examples of design patterns, but also the different variations and tricks that can be applied to implement design patterns in a modern way. For example, the use of recursive generics helps us build fluent interfaces even when inheritance is involved.
Presentation Style
This course is presented as a (very large) series of live demonstrations being done in IntelliJ IDEA and presented using the Kinetica rendering engine. Kinetica removes the visual clutter of the IDE, making you focus on code, which is rendered perfectly, whether you are watching the course on a big screen or a mobile phone.
Most demos are single-file, so you can download the file attached to the lesson and run it in IntelliJ, Eclipse or another IDE of your choice.
This course does not use UML class diagrams; all of demos are live coding. I use IntelliJ and various Maven packages where necessary.
An overview of the different topics that we'll encounter in this course as well as some info on the way the course is organized and a few words about the instructor (that's me!)
The Single Responsibility Principle (SRP) states that a class should have a single reason to change (i.e., one primary responsibility). The opposite of an SRP-adhering class is an antipattern called God Object.
A look at the Open-Closed Principle (OCP) through the prism of the Specification pattern. Two for the price of one! :)
The Liskov Substitution Principle (LSP) suggests that you should be able to substitute a base class for a derived class. What does it mean? Watch the lesson to find out.
The Interface Segregation Principle (ISP) suggests that you split interfaces into smaller interfaces so that clients don't have to implement things they don't need. Also related to the YAGNI (You Ain't Going to Need It) principle.
The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules but on abstractions. I also suggests a preference for depending on abstractions (interfaces/abstract classes) over concrete implementations.
A brief note about the three categories of design patterns: creational, structural and behavioral.
An overview of the Builder design pattern.
There's at least one well-known builder in Java: the StringBuilder. But what problem does it solve, exactly?
Let's implement our own builder for constructing HTML lists!
Fluent interfaces allow multiple calls of a builder to be called as part of a single invocation chain.
And you thought recursive generics were just for Enum? No way! They are also useful when inheriting from classes that have fluent interfaces.
Sometimes the object is complicated enough so that you need more than one builder. This lesson illustrates how several related builders can interact with one another through inheritance and a fluent interface.
A summary of all the things we've learned about the Builder design pattern.
An overview of the Factory design pattern.
Having a static function for making an object allows us to avoid overloading problems. We can have two factory methods with identical sets of parameter types, but different names.
Let's relocate all factory methods to a separate class. We have a problem: private field are no longer accessible!
The Abstract Factory design pattern allows you to create hierarchy of factories corresponding to a hierarchy of types those factories create.
A summary of all the thing we've learned about the Factory design patterns.
An overview of the Prototype design pattern.
If we have Object.clone() and the Cloneable interface, why don't we just use them?
How can we be explicit about the fact we are making a deep copy? From the land of C++ comes the idea of making copy constructors.
If your object graph has 10 classes in it, making 10 copy constructors (or dedicated cloning methods) is unrealistic. But we can automate the copying process via serialization.
A summary of all the things we've learned about the Prototype design pattern.
A look at all the things we'll learn about the Singleton design pattern.
Let's implement the simplest possible singleton!
Even though a constructor is private, there are at least two ways of instantiating it. The obvious way is with the use of reflections. A less obvious ways of accidentally replicating the singleton is with the use of serialization.
If the singleton constructor can throw, you can initialize the singleton instance in a static block instead.
We discuss two concerns of the Singleton: lazy instantiation and thread safety.
Thread safety issues can be mitigated via the use of a nested class.
A singleton that exposes itself as an enumeration instance. No problem with reflection or serialization, except... you cannot serialize field values. Also, you cannot inherit from this singleton.
The Monostate design pattern is a peculiar variation on the Singleton. It's not very practical, but can be used as a 'last resort' whenever Singleton-like behavior needs to be added without breaking existing API usage.
A multiton is a Singleton-like key-value store with (optional) lazy loading.
Why is the Singleton so disliked? One reason is testability. Let's take a look at how a Singleton can become problematic for testing.
The only socially acceptable way of using a Singleton is via dependency injection: either done by hand or, preferably, through the use of a Dependency Injection framework.
A summary of all the things we've learned about the (not-so-scary) Singleton design pattern. This concludes our investigation of Creational design patterns!
An overview of the Adapter design pattern.
We consider a scenario where vector objects need to be rendered in raster form... this requires an Adapter!
Quite often, implementation of Adapter generate temporary objects. We can implement caching to make sure we're not regenerating them on ever instance of adapter creation.
A summary of all the things we've learned about the Adapter design pattern.
An overview of the things we are going to learn about the Bridge design pattern.
A demonstration of vector/raster rendering with the Bridge design pattern using both manual instantiation as well as automation using Google Guice.
A summary of the things we've learned about the Bridge design pattern.
An overview of the things we are going to learn about the Bridge design pattern.
We illustrate an implementation of a Composite design pattern with an object that can (optionally) contain child objects.
A slightly more complicated demo where we get a scalar object to behave as an Iterable<> and also implement a default interface method!
A summary of all the things we've learned about the Composite design pattern.
An overview of the Decorator design pattern.
One well-known final class is String. So if we want to give additional behaviors, we need to introduce our own String-like class and then delegate the members. Luckily, IntelliJ IDEA helps here!
Let's build a set of decorators for geometric shapes. These decorators are all composable, so you can mix and match!
If you want to predefine the composition of decorated types at compile-time, you can do it with Java generics, but you'll have to jump through hoops due to type erasure.
A look at a hybrid of two patterns, the Adapter and the Decorator.
A summary of the things we've learned about the Decorator design pattern.
An overview of the Facade pattern.
Financial trading systems often need large monitoring consoles that display aggregated data across multiple markets and bots. A typical text console is a complicated beast, but we can provide extra APIs to make it more palatable.
A summary of what we've learned about the Facade design pattern. And yeah, no exercise in this section of the course. Sorry. What can I say, this is one of the more boring/obvious patterns. Write a Facade for your own system instead!
A look at the Flyweight design pattern.
Imagine an online game with lots of players with similar names. Do you really want to repeat the string "Smith" a thousand times? I don't think so.
Another implementation of Flyweight - this time, to save some memory when formatting text!
A summary of the things we've learned about the Flyweight design pattern.
An overview of the Proxy design pattern.
A protection proxy controls access to a particular resource or, in other words, performs some sort of checks or validation before allowing or disallowing the underlying operation.
Field assignments are difficult to intercept and control. What we can do is replace each field with a corresponding Property<> class that has getters and setters as methods, and interception can happen in those methods.
Dynamic proxies are proxies constructed at runtime. Here we build a dynamic proxy for recording the number of times each object's method was called.
What is the difference between a Proxy and a Decorator?
A summary of the things we've learned about the Proxy design pattern.
An overview of the Chain of Responsibility design pattern.
The simplest implementation of a Chain of Responsibility involves calling a chain of handle() methods.
An explanation of the concept of Command Query Separation (CQS).
Let's take a look at a much more sophisticated example of CoR, combining also the Observer and Mediator design patterns.
A summary of the things we've learned about the Chain of Responsibility design pattern.
An overview of the Command design pattern.
OpenCourser helps millions of learners each year. People visit us to learn workspace skills, ace their exams, and nurture their curiosity.
Our extensive catalog contains over 50,000 courses and twice as many books. Browse by search, by topic, or even by career interests. We'll match you to the right resources quickly.
Find this site helpful? Tell a friend about us.
We're supported by our community of learners. When you purchase or subscribe to courses and programs or purchase books, we may earn a commission from our partners.
Your purchases help us maintain our catalog and keep our servers humming without ads.
Thank you for supporting OpenCourser.