Structural Design Pettern - JongkukJayLee/JavaSpringDesignPattern GitHub Wiki

Adapter design pattern

Reference:

Definition

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

-GoF Design Patterns: Elements of Reusable Object-Oriented Software

Benifit:

  • allows you to communicate and interact with two or more incompatible objects
  • promotes the reusability of older existing functionalities in your application

Suppose you have different types of electric plugs such as cylindrical and rectangular plugs, as shown in the following figure. You can use an adapter in between to fit a rectangular plug in a cylindrical socket assuming voltage requirements are met:

(Source: Spring 5 Design Patterns)

UML diagram:

  • The Target Interface: This is the desired interface class that will be used by the clients
  • The Adapter class: This class is a wrapper class that implements the desired target interface and modifies the specific * request available from the Adaptee class
  • The Adaptee class: This is the class that is used by the Adapter class to reuse the existing functionalities and modify them for desired use
  • Client: This class will interact with the Adapter class

Create Strategy:

(1) Create two interfaces and concrete classes, which share the same method (in this case doAction)

  • We can say one is a source interface, and another is a target interface.
  • For example, a source interface is a legacy class, and a target interface is a new class.

(2) Create an Adapter (Wrapper) class implementing the target interface

(3) Inject the source class to the target class

(4) Override the methods of the target class, which redefine the methods of the source class.

In the below source code, interfaces of Apple and Orange have different behaviors, but through redefining eat() method of Apple with peel() and eat(), we can reuse Orange interface like Apple interface in the same manner.

public class AppleAdapter implements Apple {
    private final Orange orange;

    public AppleAdapter(Orange orange){
        super();
        this.orange = orange;
    }

    @Override
    public String getVariety() {
        return orange.getVariety();
    }

    @Override
    public void eat() {
        orange.peel();
        orange.eat();
    }
}

Use in Spring:

  • Used often in Spring integration when dealing with Channel Adapters for communication with different systems
  • Used in internal operations of AspectJ and used during load

(Source: Spring: Design Patterns from Linkedin Learning)

__Other use cases of Adapter design pattern in Spring __

  • JpaVendorAdapter
  • HibernateJpaVendorAdapter
  • HandlerInterceptorAdapter
  • MessageListenerAdapter
  • SpringContextResourceAdapter
  • ClassPreProcessorAgentAdapter
  • RequestMappingHandlerAdapter
  • AnnotationMethodHandlerAdapter
  • WebMvcConfigurerAdapter

The Decorator pattern

Reference:

Definition

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub classing for extending functionality.

-GoF Design Patterns

  • Adding functionalities and responsibilities to an object dynamically at runtime
  • Using Composition instead of inheritance
  • Inherited base classes compose new behavior and responsibility by becomming additive or decorated
  • Allows an object to be open for extension and closed for modification while still adding responsibilities

Benifit:

  • Composition (Has-a relationship) over inheritance (Is-a relationship)
  • Add behavior without ocde modifications, support non breaking changes
  • Can also remove behavior through encapsulation via decoration
  • Known as Wrapper

UML diagram:

The classes and objects participating in this pattern are:

  • Component (Account): It is an interface for objects that can have responsibilities added to them dynamically
  • ConcreteComponent (SavingAccount): It is a concrete class of component interface and it defines an object to which additional responsibilities can be attached
  • Decorator (AccountDecorator): It has a reference to a Component object and defines an interface that conforms to the interface of the component
  • ConcreteDecorator (SeniorCitizen and Privilege): It is a concrete implementation of Decorator and it adds responsibilities to the component

Create Strategy:

(1) Create Base Class

  • Create Abstract Base Class (Pizza.java).
import java.math.BigDecimal;

public abstract class Pizza {
    protected String description;

    public String getDescription(){
        return description;
    }

    public abstract BigDecimal getCost();
}
  • Create Concrete Class (ThickCrustPizza.java).
import java.math.BigDecimal;

public class ThickCrustPizza extends Pizza{
    public ThickCrustPizza(){
        super();
        this.description = "Thick Crust Pizza";
    }

    @Override
    public BigDecimal getCost() {
        return new BigDecimal(15.00);
    }
}

(2) Create Abstract Decorator Class (PizzaIngredient.java)

package com.frankmoley.lil.designpatternsapp.decorator;

public abstract class PizzaIngredient extends Pizza {
    public abstract String getDescription();
}

(3) Create Concrete Decorator Class (Pepperoni.java)

import java.math.BigDecimal;

public class Pepperoni extends PizzaIngredient {
    private Pizza pizza;
    public Pepperoni(Pizza pizza){
        super();
        this.pizza = pizza;
    }

    @Override
    public String getDescription() {
        return this.pizza.getDescription() + " + pepperoni";
    }

    @Override
    public BigDecimal getCost() {
        return (new BigDecimal(1.50)).add(this.pizza.getCost());
    }
}

(4) Create Client class

import org.junit.Test;

public class DecoratorTest {

    @Test
    public void testDecorator(){
        Pizza pizza = new ThickCrustPizza();
        System.out.println(pizza.getDescription());
        System.out.println(pizza.getCost());

        Pepperoni pepperoni = new Pepperoni(pizza);
        System.out.println(pepperoni.getDescription());
        System.out.println(pepperoni.getCost());

        Pepperoni doublePepperoni = new Pepperoni(pepperoni);
        System.out.println(doublePepperoni.getDescription());
        System.out.println(doublePepperoni.getCost());
    }
}

Result:

Use in Spring:

  • The framework itself uses decorators
  • Injecting decorated objects is difficult in Spring because of how bean references work
  • The use of @Qualifier annotation becomes required

(Source: Spring: Design Patterns from Linkedin Learning)

Other use cases of Decorator design pattern in Spring

The Spring Framework uses the Decorator design pattern to build important functionalities such as transactions, cache synchronization, and security-related tasks.

  • Weaving the advice into the Spring application. It uses the Decorator pattern via the CGLib proxy. It works by generating a subclass of the target class at runtime.
  • BeanDefinitionDecorator : It is used to decorate the bean definition via applied custom attributes.
  • WebSocketHandlerDecorator: It is used to decorate a WebSocketHandler with additional behaviors.

The Proxy pattern

Provide a surrogate or placeholder for another object to control access to it.

-GOF Design Patterns

  • Use of a intermediary object in place of a real object
  • Intermediary protects the real object
  • Intermediary controls the instantiation of the real object
  • Intermediary can add behavior to real object

UML Diagram

  • Subject: Actual interface to be implemented by Proxy and RealSubject.
  • RealSubject: Real implementation of Subject. It is a real object that represented by the proxy.
  • Proxy: It is a proxy object and it is also the implementation of the real object Subject. It maintains the references to the real object.

Why Use This Pattern?

  • Expensive operations need to be protected and only called when needed

  • Behavior needs to be added to a method when it is called in specific situations

  • Remote object access

  • This pattern hides the actual object from the outside world.

  • This pattern can improve the performance because it is creating an object on demand.

Creation Strategy

(1) Start with an interface

(2) Create real object to extend the interface

(3) Create proxy object to extend the interface and keep and handle the real object

(4) Create the object, add behavior, other protections as needed

Implementations?

  • When doing Spring development, seldom if ever would you use a raw Java proxy
  • With Spring, you would leverage AOP
  • @Transactional, @Casheable, others

Use in Spring

  • Every bean you create gets a proxy around it since Spring 4
  • Additional proxies are added, usullay through annotation
  • Creating proxies in Spring usually revolves around aspect-oriented programming
  • See Spring: Framework in Depth

Proxy pattern in the Spring Framework

Spring Framework uses the Proxy design pattern in the Spring AOP module transparently. As I have discussed in Spring AOP, you create proxies of the object to apply cross cutting concern across the point cut in the Spring application. In the Spring, other modules also implement the Proxy pattern, such as RMI, Spring's HTTP Invoker, Hessian, and Burlap.