0% found this document useful (0 votes)
93 views50 pages

JAVA Development: Design Patterns

The document discusses design patterns in Java development. It provides descriptions of common design patterns including Singleton, Factory Method, Abstract Factory, and Builder. The Singleton pattern ensures only one instance of a class is created and provides global access to that instance. Factory Method creates object instances through a static method rather than with a constructor. Abstract Factory allows decoupling object creation behind a common interface. Builder is used to build complex objects by delegating the construction to a separate builder class.

Uploaded by

Cosmin Grosu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
93 views50 pages

JAVA Development: Design Patterns

The document discusses design patterns in Java development. It provides descriptions of common design patterns including Singleton, Factory Method, Abstract Factory, and Builder. The Singleton pattern ensures only one instance of a class is created and provides global access to that instance. Factory Method creates object instances through a static method rather than with a constructor. Abstract Factory allows decoupling object creation behind a common interface. Builder is used to build complex objects by delegating the construction to a separate builder class.

Uploaded by

Cosmin Grosu
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 50

JAVA Development

Design Patterns
Design Patterns
Design Patterns

Design Pattern = a general reusable solution to a recurring design problem

Advantages:
● identify classes of similar problems where patterns can be applied
○ don’t reinvent the wheel every time
○ gives you a more robust/tested solution to your problem
● refer to solutions by name
○ easier to communicate to others
○ easier to identify when reading code
Design Anti-Patterns

Design Anti-Pattern: a general solution (to a class of problems) that should be


avoided in practice.

Advantages:
● identify that you should not apply a certain solution
○ don’t reinvent the wheel badly every time
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

General principles for a Singleton class:


● private constructor -> prevents creating multiple instances
● private static field -> for storing the only instance
● public static method -> for obtaining the instance

class ConnectionPool {
private static ConnectionPool instance; //need to somehow init this!
private ConnectionPool() {}

public static ConnectionPool getInstance() {


return instance;
}
}
Singleton Pattern

Creating the instance - naive implementation (1):

public static ConnectionPool getInstance() {


if (instance == null) {
instance = new ConnectionPool();
}
return instance;
}

Is this thread-safe?
What happens if two threads call getInstance() before initialization at the same
time?..
Singleton Pattern

Creating the instance - naive implementation (2):

public synchronized static ConnectionPool getInstance() {


if (instance == null) {
instance = new ConnectionPool();
}
return instance;
}

This fixes the multithreading issue, but synchronization is expensive!


● we pay the price for every call to getInstance() (even if it’s actually needed only for
the first access...)
Singleton Pattern

Creating the instance - using a static field initializer:

class ConnectionPool {

private static ConnectionPool instance = new ConnectionPool();

private ConnectionPool() {}
public static ConnectionPool getInstance() { return instance; }
}

Advantages of static field initializers:


● only called the first time the class is used
● guaranteed to only happen once! (the JVM does the synchronization for us)
● no synchronization require for subsequent calls to getInstance()
Singleton Pattern

Creating the instance: using a static initializer block:

class ConnectionPool3 {

private static ConnectionPool3 instance;


static {
//...more complex initialization here!...
instance = new ConnectionPool3();
}
//...
}

Advantages: same as with field initializer, but allows more complex initialization code.
Singleton Pattern

Singleton is also considered an Anti-Pattern (depending on context/usage):


● many times it turns out we do want multiple instances
● makes code hard to test
○ e.g the test code may want to use a different ConnectionPool that just fakes
connections
● in many cases there are better alternative solutions (like Dependency Injection…)
Factory Method
Factory Method

Factory Method
- Create/obtain class instances by using a static method

MyClass.create(...)

Differences from using constructors (calling new() ):


● The factory method may actually return an existing instance (if creating a new
one is not really needed/wanted)
● Returned instance may be a subclass of the class where the method is declared
● Multiple factory methods may have the same set of parameters
Factory Method

Examples in the Java libraries:

● java.time.ZonedDateTime , 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:
● An AbstractFactory interface declares methods for creating families of objects
○ createButton, createRadioButton, createComboBox
○ createCache

● 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

● Decoupling of 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

Problem: we need to build an object:


- with lots of attributes (some optional, some mandatory)
- and/or with a complex build process (need to validate the combination of optional fields used, etc..)

Some possible solutions:


- create a class with lots of constructors (for all combination 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) + 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..)

A better solution:
- use the Builder pattern to delegate the building process to a separate class...
Builder - Principles

Needed parts:
- a class to build instances of:
- should have a single private constructor (with params for all fields, including optional ones)
- should have only getters for all fields (no setters)

- a corresponding Builder class:


- should be a static inner class of first class (so it can access its private c’tor)
- should have copies of all fields of first class (to store Builder’s state/settings..)
- should have a single (public) constructor, with params for all mandatory fields
- should have a kind of ‘setter’ methods for all optional fields; each such setter will set a field’s
value, but will also return current builder instance (this) instead of void (for easy chaining of calls)
- should have a build() method which will be called at the end and build the actual target object
(based on settings from builder fields) and return it

- note: in more complex form of the pattern, there is also a Director class, which the client code calls
first, and which direct the builder(s) on what to build
Builder - Example

public class Car { //the target class (to be built)

//1) Has many fields (some mandatory + some optional)


private int doors;
private String color;
//optional:
private boolean leatherSeats;
private boolean electricWindows;

//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;
}

//3) Fields are read-only, so ONLY GETTERS here (no setters)


public int getDoors() { return doors; }
public String getColor() { return color; }
public boolean isLeatherSeats() { return leatherSeats; }
public boolean isElectricWindows() { return electricWindows; }
Builder - Example

public class Car { …

//1) SEPARATE BUILDER CLASS, NEEDS to be a STATIC INNER CLASS of it (to have access to Car private constructor)
public static class Builder {

//2) Has COPIES of ALL FIELDS of target class (Car)


private int doors;
private String color;
private boolean leatherSeats;
private boolean electricWindows;

//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");

//…then may (re)use it to build multiple cars (with same settings)


Car car1 = basicBuilder.build();
Car car2 = basicBuilder.build();

//Use another builder for building cars with other settings:


Car.Builder luxuryBuilder = new Car.Builder(4, "black")
.withSatNav(true)
.withElectricWindows(true)
.withLeatherSeats(false);
Car car3 = luxuryBuilder.build();
Car car4 = luxuryBuilder.build();

//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 pattern in Java API

Examples of Builder pattern usage in Java libs:

- java.lang.StringBuilder: (one difference here: it’s not a inner class of String)


String str = new StringBuilder(20) //build the builder instance (with an initial capacity)
.append("abc") //append some stuff…
.append(123) //…
.toString(); //call the final 'build' method to produce the String

- 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

Adapter (aka Wrapper, Translator):


- convert the interface of a class into another interface expected by a client
- especially useful when the interfaces are not under your control (e.g is a library)

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 functionality of the Book class to match the
interface required by the Printer class
● Having a Student class and a Mailer class that receives Addressable
instances, we create a StudentAddressableAdapter ...
Adapter Pattern

Adapter Pattern Diagram


Adapter Pattern

Advantages:

● Allows merging two independently-developed APIs together


○ both of them may come from external libraries

● Allows you to keep two APIs with different responsibilities separated:


○ e.g Mailer and Student

Examples from the Java libraries:


● Arrays.asList: allow arrays to be used by all APIs that require Lists
● ByteArrayInputStream: wraps a Byte Array as an input stream
Strategy
Strategy Pattern

Strategy:
● Describe one or more steps of an algorithm in an interface
● Clients use instances of the algorithm without knowing the 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:

● Collections.sort() method that takes a Comparator parameter


○ based on the different implementations of Comparator interfaces, the
collection items are sorted in different ways

● 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);
}

class CreditCardStrategy implements PaymentStrategy {


private final String cardNumber, cvv, dateOfExpiry;
CreditCardStrategy(...) {...}

@Override
public void pay(int amount) {
System.out.println(amount + " paid with credit card");
}
}
Strategy Pattern – Shopping Cart Example

class PaypalStrategy implements PaymentStrategy {


private final String emailId, password;
PaypalStrategy(...) {...}

@Override
public void pay(int amount) {
System.out.println(amount + " paid using Paypal");
}
}
Strategy Pattern – Shopping Cart Example

class Item {
private final int price;
//other fields...

//constructor, getters...
}
Strategy Pattern – Shopping Cart Example

class ShoppingCart {

private final List<Item> items = new ArrayList<>();


void add(Item... newItems) { items.addAll(Arrays.asList(newItems)); }

void pay(PaymentStrategy paymentStrategy) {


int amount = calculateTotal();
paymentStrategy.pay(amount);
}

int calculateTotal() {
return items.stream().mapToInt(Item::getPrice).sum();
}
}
Strategy Pattern – Shopping Cart Example

public class ShoppingMain {


public static void main(String[] args) {

ShoppingCart cart = new ShoppingCart();


cart.add(new Item("1234", 10), new Item("5678", 40));

//pay by paypal
cart.pay(new PaypalStrategy("myemail@example.com", "mypwd"));

//pay by credit card


cart.pay(new CreditCardStrategy("567890123456","786","12/15"));
}
}
Strategy Pattern – Shopping Cart Example
Visitor
Visitor

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(ShoppingCartVisitor visitor);
}

class Book implements Item {


private int price;
Book(int price) { this.price = price; }
int getPrice() { return price; }

@Override
public int accept(ShoppingCartVisitor visitor) {
return visitor.visit(this);
}
}
Visitor – Shopping Cart Example

class Vegetable implements Item {

private int pricePerKg;


private int weight;

Vegetable(int pricePerKg, int weight) {


this.pricePerKg = pricePerKg;
this.weight = weight;
}
int getPricePerKg() { return pricePerKg; }
int getWeight() { return weight; }

@Override
public int accept(ShoppingCartVisitor visitor) {
return visitor.visit(this);
}
}
Visitor – Shopping Cart Example

interface ShoppingCartVisitor {
int visit(Book book);
int visit(Vegetable vegetable);
}

class ShoppingCartVisitorImpl implements ShoppingCartVisitor {


public int visit(Book book) {
return book.getPrice() > 100 ?
book.getPrice() - 5 :
book.getPrice();
}
public int visit(Vegetable vegetable) {
return vegetable.getPricePerKg() * vegetable.getWeight();
}
}
Visitor – Shopping Cart Example

class ShoppingCartClient {
public static void main(String[] args) {
Item[] items = new Item[]
{new Book(40), new Book(120), new Vegetable(10, 3)};

ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();


int total = 0;
for (Item item : items) {
total += item.accept(visitor);
}
System.out.println("total: " + total);
}
}
Visitor – Shopping Cart Example
Visitor – benefits & limitations

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/

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy