Java Design Patterns
Java Design Patterns
Design Patterns
Design Patterns
Advantages:
● identify classes of similar problems where known solutions can be applied
○ don’t reinvent the wheel every time
○ gives you a more robust/tested solution to your problem
Advantages:
● identify what solutions you should not apply a certain problems
○ don’t reinvent the wheel badly every time
○ gives you the reasons to avoid that common solution (which may not be so
obvious to less experienced designers)
Categories
Divided in 3 categories:
- Creational
- how objects are created (more control, adapt it to specific needs..)
- patterns: Singleton, Factory, Abstract Factory, Builder, Object Pool, Prototype
- Structural
- how to design class structure, relationships between classes
- patterns: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Memento, Proxy
- Behavioral
- the interaction/communication between objects
- patterns: Chain of Responsibility, Command, Iterator, Observer, Strategy, Visitor ..
Singleton
Singleton Pattern
Singleton:
- Ensures that only a single instance of a given class is created
- Provides a global access point to that instance
Examples:
- database connection pool
- HTTP session manager
- cache
- loggers
- app configuration
Singleton Pattern
class ConnectionPool {
private ConnectionPool() {}
Is this thread-safe?
- what happens if two threads call getInstance() before initialization at the same time?..
Singleton Pattern
class ConnectionPool {
private static ConnectionPool instance = new ConnectionPool();
private ConnectionPool() {}
public static ConnectionPool getInstance() { return instance; }
}
Advantages:
- only called the first time the class is used
- guaranteed to only happen once! (by JVM - it does the synchronization for us)
- no synchronization required for subsequent calls to getInstance()
Singleton Pattern
class ConnectionPool {
Advantages: same as for field initializer, plus allows more complex initialization code
Singleton Pattern
Factory Method
- Create/obtain class instances by using a static method
MyClass.create(...)
● java.time.LocalDateTime
○ from(...)
○ now()
○ of(int year, int month, int dayOfMonth, int hour, int minute)
● java.text.NumberFormat.getInstance(Locale locale)
● java.nio.charset.Charset.forName(...)
Abstract Factory
Abstract Factory
Allows the decoupling of how and what objects are created behind an interface:
● Clients call a factory instance to create objects that implement certain interfaces/classes,
but without knowing their concrete types
○ Button, ComboBox, RadioButton
○ Cache
● Classes that implement the AbstractFactory interface actually decide what concrete
implementation will be used for each type of produced objects
○ MacOSButton/WindowsButton, MacOSComboBox/WindowsComboBox
○ RedisCache/InMemoryCache, etc.
Abstract Factory
Abstract Factory
Advantages:
● Easier to test: test code can use a separate factory that creates mock objects
● Decoupled code:
○ clients don’t need to know about the concrete implementations
○ easy to create new factories for new type families (e.g support Linux
besides Windows and MacOS)
Builder
Builder
Possible solutions:
- create a class with lots of constructors (for all combinations of optional fields)
- cumbersome to write/use (get the parameters in right order, etc)
- may actually be impossible (if optional fields have same type, cannot define a separate constructor
for each of them, as they have same signature)
- OR: create a class with 1 constructor (for mandatory params) + many setters (for all optional fields)
- works, but no place to put final validation (new object may remain inconsistent)
- produced object cannot be made immutable (so it can be change by accident later..)
Better solution:
- use the Builder pattern to delegate the building process to a separate class...
Builder - Principles
Required parts:
- a target class - to build instances of:
- has a single private constructor (with params for all fields, including optional ones)
- has only getters for all fields (no setters)
- note: in more complex forms there is also a Director class, which direct the builder(s) on what to build...
Builder - Example
//2) Has a SINGLE CONSTRUCTOR, PRIVATE, with params for ALL fields! (mandatory+optional)
private Car(int doors, String color, boolean leatherSeats, boolean electricWindows) {
this.doors = doors;
this.color = color;
this.leatherSeats = leatherSeats;
this.electricWindows = electricWindows;
}
//4) SEPARATE BUILDER CLASS, NEEDS to be a STATIC INNER CLASS of it (to have access to Car private constructor)
public static class Builder {
//3) Has a SINGLE CONSTRUCTOR, PUBLIC, with params for all MANDATORY fields
public Builder(int doors, String color) { this.doors = doors; this.color = color; }
//4) Has 'setter-like' methods for all OPTIONAL fields; each one sets the field value (like a regular setter),
// and then also returns 'this', instead of void (for easier chaining of multiple calls later)
public Builder withLeatherSeats (boolean leatherSeats) { this.leatherSeats = leatherSeats; return this;}
public Builder withElectricWindows(boolean electricWindows) { this.electricWindows = electricWindows; return this;}
//5) Has a build() method, to be called at the end to actually build and return an instance of target class
// (based on current builder settings), possibly also running now some final validations
public Car build() {
//…may run some final verifications here…
return new Car(doors, color,leatherSeats, electricWindows);
}
Builder - Example
//Client code:
//First build a Builder instance (with just the default settings here)…
Car.Builder basicBuilder = new Car.Builder(3, "gray");
//Shorter example of declaring and directly using a builder (for just 1 car):
Car car5 = new Car.Builder(4, "red") //build the builder, with values for mandatory fields…
.withElectricWindows(true) //may also configure the optional fields (each returns Builder)
.withLeatherSeats(true) //…
.build(); //call .build() to end the build chain and return the actual Car
Builder - Example
Details to note:
- Car class: the fields and (the single) constructor are all private (and no setters, just getters)
- Car.Builder: is a nested class for Car; it has similar fields, a public constructor (to initialize only the
mandatory fields), some ‘setter’ methods, and the specific build() method
Builder - Usage Examples
- java.util.Calendar:
Calendar cal = new Calendar.Builder()
.setCalendarType("iso8601")
.setWeekDate(2018, 1, MONDAY)
.build();
- java.util.Locale:
Locale loc = new Locale.Builder()
.setLanguage("sr").setScript("Latn").setRegion("RS")
.build();
Adapter
Adapter Pattern
Examples:
- having a Book class and a Printer class that only receives Printable instances:
- we create a BookPrintableAdapter that implements Printable
- the new class “adapts” the API of Book class to match the API of Printable interface
required by the Printer (‘translates’ between them…)
- having a Student class and a Mailer class that receives Addressable instances -> we
create a StudentAddressableAdapter ...
Adapter Pattern - Example
Note:
- the adapter (BookPrintableAdapter) uses the public API of the original class (Book)
- it contains translation code to expose this under the new form expected by target interface (Printable)
Adapter Pattern
Advantages:
Strategy:
● Describe one or more steps of an algorithm in an interface
● Clients use instances of the algorithm without knowing the actual implementation
● The algorithm used can change depending on various factors
Examples:
● The Basket class uses a DiscountCalculator to calculate discounts
● DiscountCalculator is an interface with several methods:
○ applyCoupon(...)
○ applyBasketDiscount(...)
● DiscountCalculator has multiple implementations (regular / happy hour / black friday)
Strategy Pattern - Examples
Examples:
● a Shopping Cart where we have two payment strategies – using Credit Card or
using PayPal
Strategy Pattern – Shopping Cart Example
interface PaymentStrategy {
void pay(int amount);
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid with credit card");
}
}
Strategy Pattern – Shopping Cart Example
@Override
public void pay(int amount) {
System.out.println(amount + " paid using Paypal");
}
}
Strategy Pattern – Shopping Cart Example
class Cart {
private final List<Item> items = new ArrayList<>();
void add(Item... newItems) { items.addAll(Arrays.asList(newItems)); }
int calculateTotal() {
return items.stream().mapToInt(Item::getPrice).sum();
}
//pay by paypal
cart.pay( new PaypalStrategy("myemail@example.com", "mypwd"));
Visitor:
● used when we have to perform an operation on a group of similar objects
● helps to move the operational logic from the objects to another class
Example:
● shopping cart - we can add different types of items (all sharing a base type)
● when we click on checkout button, it calculates the total amount to be paid
● we can have the calculation logic in the item classes or we can move out this
logic to another class using visitor pattern
Visitor – Shopping Cart Example
interface Item {
int accept(CartVisitor visitor);
}
@Override
public int accept(CartVisitor visitor) {
return visitor.visit(this);
}
}
Visitor – Shopping Cart Example
@Override
public int accept(CartVisitor visitor) {
return visitor.visit(this);
}
}
Visitor – Shopping Cart Example
interface CartVisitor {
int visit(Book book);
int visit(Vegetable vegetable);
}
class ShoppingApp {
public static void main(String[] args) {
Item[] items = new Item[]
{new Book(40), new Book(120), new Vegetable(10, 3)};
Benefits:
● if the logic of operation changes, then we need to make change only in the
visitor implementation rather than doing it in all the item classes
● adding a new item to the system is easy, as it will require changes in visitor
interface+implementation, but existing item classes will not be affected
Limitations:
● we should know the return type of visit() methods at the time of designing
otherwise we will have to change the interface and all of its implementations.
● if there are too many implementations of visitor interface, it makes it hard to
extend.
Questions?
Extra reading
● https://www.journaldev.com/1827/java-design-patterns-example-tutorial
● https://java-design-patterns.com/patterns/
● https://www.oodesign.com/
● https://www.byteslounge.com/tutorials/java-builder-pattern-example
● https://www.byteslounge.com/tutorials/java-factory-pattern-example
● https://www.byteslounge.com/tutorials/java-abstract-factory-pattern-example
● https://www.byteslounge.com/tutorials/java-visitor-pattern
● https://www.byteslounge.com/tutorials/java-adapter-pattern-example
● http://tutorials.jenkov.com/java/nested-classes.html
● https://www.geeksforgeeks.org/nested-classes-java/