Adapter Design Pattern in Java

Reading Time: 4 minutes

Description

Adapter Design pattern is listed underneath the Structural patterns. As you understand from the name it is designated to act like an adapter. When you look for this pattern there are different descriptions and examples, however I find the below illustration very clear what the pattern stands for;

Adapter Pattern Illustration

As you study the illustration we see three elements here;

  • Client: Expects to work with the Adaptee, however it is not compatible with the client,
  • Adapter: Wraps the Adaptee’s logic and provides a way to utilize adaptee,
  • Adaptee: An interface or an abstract class that has legacy methods which are not compatible with the client.

The stand point of the pattern is to work the clients with the incompatible interfaces by not chancing or enhancing the methods of the adaptee via the Adapter classes/interfaces. On the other hand also Adapters known as a Wrapper classes. Furthermore, the adapter pattern also practices the Open Close principle in the S.O.L.I.D. in a way that we will not change the existing code unit that will be closed, however we will wrap it by using an adapter. In the real world examples you will see US/Europe electricity socket voltage adapters and all in one memory card, card reader a computer and so on.

Last of all the Adapter Pattern may have some similarities to;

  • Façade Pattern: It provides a simple interface and methods for complex structure,
  • Decorator Pattern: Enhances an interface and adds more functionalities

However, the Adapter pattern does neither of those, but provides a way to the client to work with an incompatible interface by either encapsulating the logic or delegating it. Observe the following to apprehend the topic.

Forming the Pattern

The adapter pattern can be form in two different ways;

Class Adapter

Adapter pattern class adapter uml diagram

The adapter class utilizes Inheritance. The Adapter implement/extends the Adaptee interface/abstract class and overrides the methods,

Object Adapter

adapter pattern object adapter uml diagram

The adapter class utilizes Composition. The adapter has an instance of the Adaptee interface/abstract class and delegates the request to it.

 

Example

For the sake of the simplicity, I’ll emphasis on an old main frame that needs an adapter for classes that work on the Strings with tabs after each enter.

The objects and the roles we have:

  • MainFrameSender: it is the main interface that has we will wrap, it only accepts a String as a parameter
  • MainFrameSenderImpl: it is the implementation of the Main Frame interface,
  • ClassMainFrameSenderAdapter: The adapter class that is formed with the Class Adapter format.The Adapter will convert the Content object into the String that the interface accepts,
  • ObjectMainFrameSenderAdapter: The adapter class that is formed with the ObjectAdapter format. The Adapter will convert the Content object into the String that the interface accepts,
  • Content: This is the Pojo class that will be used to carry the data by the clients,
  • Main: It is used as a client and expose the capabilities of the each adapter implementation.

MainFrameSender.java

public interface MainFrameSender {
    void sendToMainFrame(String content);
}

MainFrameSenderImpl.java

public class MainFrameSenderImpl implements MainFrameSender {
    @Override
    public void sendToMainFrame(String content) {
        System.out.println("sending the content to the mainframe: " + content);
    }
}

ClassMainFrameSenderAdapter.java

public class ClassMainFrameSenderAdapter implements MainFrameSender {

    @Override
    public void sendToMainFrame(String content) {
        System.out.println("sending the content to the mainframe: " + content);
    }

    public void sendToMainFrame(Content content) {
        final StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(content.getMessageId());
        stringBuilder.append("\t");
        stringBuilder.append(content.getHeader());
        stringBuilder.append("\t");
        stringBuilder.append(content.getContent());
        sendToMainFrame(stringBuilder.toString());
    }
}

ObjectMainFrameSenderAdapter.java

public class ObjectMainFrameSenderAdapter {

    private MainFrameSender mainFrameSender = new MainFrameSenderImpl();

    public void sendToMainFrame(Content content) {
        final StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(content.getMessageId());
        stringBuilder.append("\t");
        stringBuilder.append(content.getHeader());
        stringBuilder.append("\t");
        stringBuilder.append(content.getContent());
        mainFrameSender.sendToMainFrame(stringBuilder.toString());
    }
}

Content.java

public final class Content {
    private int messageId;
    private String header;
    private String content;

    public Content(int messageId, String header, String content){
        this.messageId=messageId;
        this.header=header;
        this.content=content;
    }

    public int getMessageId(){
        return messageId;
    }

    public String getHeader(){
        return header;
    }

    public String getContent(){
        return content;
    }

    @Override
    public String toString() {
        return "Content{" +
                "messageId=" + messageId +
                ", header='" + header + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        Content content = new Content((int)Math.random(), "Message 1","this message sent via Class Adapter");
        MainFrameSender classMainFrameSenderAdapter = new ClassMainFrameSenderAdapter();
        ((ClassMainFrameSenderAdapter) classMainFrameSenderAdapter).sendToMainFrame(content);

        Content content2 = new Content((int)Math.random(), "Message 2","this message sent via Object Adapter");
        ObjectMainFrameSenderAdapter objectMainFrameSenderAdapter = new ObjectMainFrameSenderAdapter();
        objectMainFrameSenderAdapter.sendToMainFrame(content2);
    }
}

Console Output

sending the content to the mainframe: 0	Message 1	this message sent via Class Adapter
sending the content to the mainframe: 0	Message 2	this message sent via Object Adapter

Mediator Design Patter in Java

Reading Time: 4 minutes

Definition

The Mediator Pattern falls into the Behavioral Pattern. In simple terms, there is centrally a mediator that handles communication between object in a loosely coupled way, thus objects shall not talk to each other directly. Furthermore, often developers get confused with the Observer pattern, and they seem pretty similar. However, we can break the ultimate difference down like this:

  • Observer Pattern: The objects are not interested in each other, so the relationship is one-to-many, many objects are interested in the state change of one object,
  • Mediator Pattern: The objects are interested to interact with many other objects, so the relation is many to many.

To better understand the deal in the Mediator pattern please study the below illustration;

Before The Mediator Pattern Applied

After The Mediator Pattern Applied

Rules of the Pattern

In this pattern there are two components that we need to create and mind;

Colleagues: These objects are primarily the targets that will not talk to each other directly and have the same base type abstract class or interface that will inherit the same attributes. Furthermore, they will have have the knowledge of the mediator component, which means that each of them will have access an instance to the mediator pattern, rather than having instances to other colleague objects.

Mediator: The centralized component that manages the communication between colleague components.

Example Code Snippet

To better understand the concept, I have developed a simple HR Organization suite. Here the HR is the mediator that knows all the participant Organization units and handles the communication between the Organization components. Then last of all we have the Organization base type that has all the common attributes, Manager and Employee objects are the child classes for the organization, they are separated and have their own functions. The communication is eliminated.

You will see some actions;

  • Registration: A new organization type is created, then it is instantly registered in the HR list,
  • Announcement: HR makes the announcement to all the whole organization regardless of the Organization instance is Manager or Employee,
  • Initiation of the Resignation: Here when an employee resigns, the employee will not talk to the Manager directly, but HR that initiates the process and talks to the Manager,
  • Surprise Party Preparation: After the resignation process is approved by the manager, the HR throws a surprise party without notifying the resigning employee and makes an announcement to the whole Organization.

HR.java

public interface HR {
    void registerOrganization(Organization organization);
    void makeAnnouncementToOrganization(final String message);
    void initiateResignationProcess(Organization employee, Organization manager);
}

HRImplementation.java

import java.util.HashSet;
import java.util.Set;

public class HRImplementation implements HR {
    private final Set<Organization> listOfOrganization = new HashSet<Organization>();

    @Override
    public void registerOrganization(Organization organization) {
        listOfOrganization.add(organization);
    }

    @Override
    public void makeAnnouncementToOrganization(String message) {
        System.out.println("Making announcement to the whole organization");
        for (Organization organization : listOfOrganization) {
            organization.receiveAnnouncement(message);
        }
    }

    @Override
    public void initiateResignationProcess(Organization employee, Organization manager) {
        System.out.println("Employee " + employee.getName() + " would like to resign from the company");
        System.out.println("HR is notifying the manager");
        boolean managerResponse = ((Manager) manager).decideAndFinalizeResignation(employee);
        if (managerResponse) {
            System.out.println(manager.getName() + " has approved " + employee.getName() + "\'s resignation") ;
            prepareSurpriseParty(employee);
        }
    }

    private void prepareSurpriseParty(Organization resigningEmployee) {
        System.out.println("Preparing the surprise party for " + resigningEmployee.getName());
        for (Organization organization : listOfOrganization) {
            if (organization != resigningEmployee)
                organization.receiveAnnouncement("Guys " + resigningEmployee.getName() + " is resigning his position, and you " + organization.getName() + " are invited to his surprise party");
        }
    }
}

Organization.java

public abstract class Organization {
    private final HR hr;
    private final String name;

    public Organization(HR hr, String name) {
        this.hr = hr;
        this.name = name;
        this.hr.registerOrganization(this);
    }

    public void receiveAnnouncement(final String message) {
        System.out.println(this.getClass().getSimpleName() + " " + name + " received the announcement " + message);
    }

    public String getName() {
        return name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Organization that = (Organization) o;

        return name.equals(that.name);
    }

    @Override
    public int hashCode() {
        return name.hashCode();
    }
}

Manager.java

public class Manager extends Organization {
    private HR hr;
    private String managerName;

    public Manager(HR hr, String name) {
        super(hr, name);
        this.hr = hr;
        this.managerName = name;
    }

    public boolean decideAndFinalizeResignation(Organization employee) {
        System.out.println("Manager " + managerName + " is considering " + employee.getName() + "\'s resignation");
        return true;
    }
}

Employee.java

public class Employee extends Organization {
    private HR hr;
    private String employeeName;

    public Employee(HR hr, String name) {
        super(hr, name);
        this.hr = hr;
        this.employeeName = name;
    }

    public void giveResignation(Organization manager){
        System.out.println("Employee " + employeeName + " would like to resign");
        hr.initiateResignationProcess(this, manager);
    }
}

Test.java

public class Test {
    public static void main(String[] args) {
        HR hr = new HRImplementation();
        Organization konrad = new Manager(hr, "Konrad");
        Organization tugrul = new Employee(hr, "Tugrul");
        Organization oguz = new Employee(hr, "Oguz");
        Organization altan = new Employee(hr, "Altan");

        hr.makeAnnouncementToOrganization("Welcome guys :)");
        ((Employee) tugrul).giveResignation(konrad);
    }
}

Console Output

Making announcement to the whole organization
Manager Konrad received the announcement Welcome guys 🙂
Employee Altan received the announcement Welcome guys 🙂
Employee Oguz received the announcement Welcome guys 🙂
Employee Tugrul received the announcement Welcome guys 🙂
Employee Tugrul would like to resign
Employee Tugrul would like to resign from the company
HR is notifying the manager
Manager Konrad is considering Tugrul’s resignation
Konrad has approved Tugrul’s resignation
Preparing the surprise party for Tugrul
Manager Konrad received the announcement Guys Tugrul is resigning his position, and you Konrad are invited to his surprise party
Employee Altan received the announcement Guys Tugrul is resigning his position, and you Altan are invited to his surprise party
Employee Oguz received the announcement Guys Tugrul is resigning his position, and you Oguz are invited to his surprise party

In Contemporary Examples

  • java.util.Timer class schedule methods,
  • Java Executor executor method,
  • java.lang.reflect.Method invoke method,
  • JMS and Message Brokers heavily utilizes this pattern along with the Observer Pattern,
  • Spring’s MVC Pattern makes use in the Dispatcher Servlet where mediator handles all the web requests as well as the controller objects.

Builder Pattern in Java

Reading Time: 6 minutes

The Builder pattern eliminates building the complexity of the target object as well as eases the construction by providing such methods that will assemble the object same as building a set of blocks.
By implementing the Builder pattern, there won’t be any need of larger constructors, parameter objects or custom types. Furthermore, the Builder pattern is listed as a Creational Pattern. In JDK this pattern can be seen in java.lang.StringBuilder‘s append method.

In real life examples, the pattern consists of;

  • Client: is the endpoint that makes a request to the product,
  • Product: The actual complex object itself which is assembled by the concrete builders,
  • Director: Has only one responsibility to make a call to the Builder itself,
  • Builder: Hold the common methods for Concrete Builders of how the product will be shaped,
  • Concrete Builder: They build creates a specific Product, provides ways to decorate the Product as well as constructs the product and returns it.

Advantages:
-Removing multiple and complex constructor creations,
-Providing a method that checks whether the required fields are filled,
-Providing a mechanism to check whether given parameters meet the requirements
-Immutability, this is mostly the developers are looking forward to striving and benefiting it

Disadvantages:
-For each specific Product, there must be a concrete builder created,

For the sake of better apprehending the pattern which is in a way complex, I’ll be giving two examples an Intermediate that gives an overall look to quickly understanding the pattern
and an advanced example which demonstrates all the Roles that are defined above.

Intermediate Example

In this example you will study a simple Person object that will be constructed by a static Person builder class.
The direct construction of the Person object is disallowed by marking the constructor as private. In addition, I have included a simple business logic within the build method that will give you an idea how business logic can be implemented properly.
Last of all, I have created test cases to see the possible behaviors and outcome of the object.

Person Class

package com.tugrulaslan.builderpattern;

import java.util.Date;

public class Person {
    /**
     * Required Fields
     */
    private final String firstName;
    private final String lastName;
    private final Date dateOfBirth;
    /**
     * Optional Fields
     */
    private final String phoneNumber;
    private final String city;

    /**
     * The private Constructor accepts only the builder which holds the data to initiate the fields in the Person class,
     * If optional field are left off null, they are assigned to empty values to prevent NullPointerException
     *
     * @param personBuilder
     */

    private Person(final PersonBuilder personBuilder) {
        this.firstName = personBuilder.firstName;
        this.lastName = personBuilder.lastName;
        this.dateOfBirth = personBuilder.dateOfBirth;
        this.phoneNumber = personBuilder.phoneNumber == null ? "" : personBuilder.phoneNumber;
        this.city = personBuilder.city == null ? "" : personBuilder.city;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public Date getDateOfBirth() {
        return dateOfBirth;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public String getCity() {
        return city;
    }

    public static class PersonBuilder {
        /**
         * Required Fields, will be marked as final in the builder class
         */
        private final String firstName;
        private final String lastName;
        private final Date dateOfBirth;
        /**
         * Optional Fields, will not be marked as final in the builder class
         */
        private String phoneNumber;
        private String city;

        /**
         * Constructor of static PersonBuilder that initiates the required fields
         *
         * @param firstName   A String type field, corresponds to the firstName field in the Person object, required,
         * @param lastName    A String type field, corresponds to the lastName field in the Person object, required ,
         * @param dateOfBirth A java.util.Date type field, corresponds to the dateOfBirth field in the Person object, required.
         */
        public PersonBuilder(final String firstName, final String lastName, final Date dateOfBirth) {
            this.firstName = firstName;
            this.lastName = lastName;
            this.dateOfBirth = dateOfBirth;
        }

        /**
         * @param phoneNumber A String type field, correspond to the phoneNumber field in the Person object, optional.
         * @return returns the same PersonBuilder object
         */
        public PersonBuilder phoneNumber(String phoneNumber) {
            this.phoneNumber = phoneNumber;
            return this;
        }

        /**
         * @param city A String type field, correspond to the city field in the Person object, optional.
         * @return returns the same PersonBuilder object
         */
        public PersonBuilder city(String city) {
            this.city = city;
            return this;
        }

        /**
         * Person builder object that has all the required business rules
         *
         * @return A Person object unless the business rules are met, otherwise exceptions will be thrown
         */
        public Person buildPerson() {
            Person person = new Person(this);
            if (person.firstName == null || person.firstName.length() == 0) {
                throw new IllegalArgumentException("First Name cannot be null or empty");
            } else if (person.lastName == null || person.lastName.length() == 0) {
                throw new IllegalArgumentException("Last Name cannot be null or empty");
            } else if (person.dateOfBirth == null) {
                throw new IllegalArgumentException("Birth date cannot be null");
            } else {
                return person;
            }
        }
    }
}

Person Class Test Case

package com.tugrulaslan.test;

import com.tugrulaslan.builderpattern.Person;
import org.junit.Test;

import java.util.Date;

import static org.junit.Assert.assertEquals;

public class PersonTest {

    private final static String FIRST_NAME = "Tugrul";
    private final static String LAST_NAME = "Aslan";
    private final static Date DATE_OF_BIRTH = new java.util.Date();
    private final static String PHONE_NUMBER = "+48754345325";
    private final static String CITY = "Wrocław";

    @Test(expected = java.lang.IllegalArgumentException.class)
    public void testRequiredFieldInvalidValue() {
        Person person = new Person.PersonBuilder(null, LAST_NAME, null).buildPerson();
    }

    @Test
    public void testProperValuesOptionalValuesEmpty() {
        Person person = new Person.PersonBuilder(FIRST_NAME, LAST_NAME, DATE_OF_BIRTH).buildPerson();
        assertEquals(person.getFirstName(), FIRST_NAME);
        assertEquals(person.getLastName(), LAST_NAME);
        assertEquals(person.getDateOfBirth(), DATE_OF_BIRTH);
        assertEquals(person.getPhoneNumber(), "");
        assertEquals(person.getCity(), "");
    }

    @Test
    public void testProperValuesOptionalValuesFilledIn() {
        Person person = new Person.PersonBuilder(FIRST_NAME, LAST_NAME, DATE_OF_BIRTH)
                .phoneNumber(PHONE_NUMBER)
                .city(CITY)
                .buildPerson();
        assertEquals(person.getFirstName(), FIRST_NAME);
        assertEquals(person.getLastName(), LAST_NAME);
        assertEquals(person.getDateOfBirth(), DATE_OF_BIRTH);
        assertEquals(person.getPhoneNumber(), PHONE_NUMBER);
        assertEquals(person.getCity(), CITY);
    }
}

Advanced Example

Advanced example compared to the Intermediate one, is fairly exposes all roles in depth. To better apprehend the topic I have given a kebab example where I’ll walk you through making kebab in a code level from the scratch.
Test cases will prove the outcome and each builder will properly assemble an example of the specific kind of kebab. To redefine each role that is used in this example;

  • Client: KebabTest,
  • Product: Kebab,
  • Director: Chef,
  • Builder: KebabBuilder,
  • Concrete Builder: AdanaKebab, UrfaKebab and ChickenKebab.

KebabTest

package com.tugrulaslan.test;

import com.tugrulaslan.builderpattern.*;
import org.junit.Test;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

public class KebabTest {

    @Test
    public void adanaKebabTestCase() {
        Chef chef = new Chef();
        KebabBuilder adanaKebabBuilder = new AdanaKebab();
        chef.setKebabBuilder(adanaKebabBuilder);
        chef.makeKebab();
        Kebab adanaKebab = chef.getKebab();
        assertNotNull(adanaKebab);
        assertEquals(adanaKebab.getKebabName(), "Adana Kebab");
        assertEquals(adanaKebab.getMeatType(), MeatType.REDMEAT);
        assertTrue(adanaKebab.isSpicy());
        assertEquals(adanaKebab.getPrice(), 10);
    }

    @Test
    public void urfaKebabTestCase() {
        Chef chef = new Chef();
        KebabBuilder urfaKebabBuilder = new UrfaKebab();
        chef.setKebabBuilder(urfaKebabBuilder);
        chef.makeKebab();
        Kebab urfaKebab = chef.getKebab();
        assertNotNull(urfaKebab);
        assertEquals(urfaKebab.getKebabName(), "Urfa Kebab");
        assertEquals(urfaKebab.getMeatType(), MeatType.REDMEAT);
        assertFalse(urfaKebab.isSpicy());
        assertEquals(urfaKebab.getPrice(), 9);
    }

    @Test
    public void chickenKebabTestCase() {
        Chef chef = new Chef();
        KebabBuilder chickenKebabBuilder = new ChickenKebab();
        chef.setKebabBuilder(chickenKebabBuilder);
        chef.makeKebab();
        Kebab chickenKebab = chef.getKebab();
        assertNotNull(chickenKebab);
        assertEquals(chickenKebab.getKebabName(), "Chicken Kebab");
        assertEquals(chickenKebab.getMeatType(), MeatType.CHICKEN);
        assertTrue(chickenKebab.isSpicy());
        assertEquals(chickenKebab.getPrice(), 7);
    }
}

Kebab

package com.tugrulaslan.builderpattern;


/**
 * Kebab class is in the Product Role
 */
public class Kebab {
    private String kebabName;
    private MeatType meatType;
    private boolean spicy;
    private int price;

    public String getKebabName() {
        return kebabName;
    }

    public void setKebabName(String kebabName) {
        this.kebabName = kebabName;
    }

    public MeatType getMeatType() {
        return meatType;
    }

    public void setMeatType(MeatType meatType) {
        this.meatType = meatType;
    }

    public boolean isSpicy() {
        return spicy;
    }

    public void setSpicy(boolean spicy) {
        this.spicy = spicy;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

Chef

package com.tugrulaslan.builderpattern;

/**
 * Chef class is in the Director Role
 */
public class Chef {
    private KebabBuilder kebabBuilder;

    public void setKebabBuilder(KebabBuilder kebabBuilder) {
        this.kebabBuilder = kebabBuilder;
    }

    /**
     * @return kebab object that is assembled by the concrete kebab builder class
     */
    public Kebab getKebab() {
        return kebabBuilder.kebab;
    }

    /**
     * The void method that calls all the available methods in the concrete class
     */
    public void makeKebab() {
        kebabBuilder.createNewKebab();
        kebabBuilder.assignKebabName();
        kebabBuilder.assignMeatType();
        kebabBuilder.makeSpicy();
        kebabBuilder.setKebabPrice();
    }
}

KebabBuilder

package com.tugrulaslan.builderpattern;

/**
 * KebabBuilder class is in the Builder Role
 */
public abstract class KebabBuilder {
    protected Kebab kebab;

    /**
     * @return current Kebab Object
     */
    public Kebab getKebab() {
        return kebab;
    }

    /**
     * Returns a new instance of Kebab object
     */
    public void createNewKebab() {
        kebab = new Kebab();
    }

    /**
     * An abstract template to assign a name to the Kebab
     */
    public abstract void assignKebabName();

    /**
     * An abstract template to assign the Kebab's Meat Type
     */
    public abstract void assignMeatType();

    /**
     * An abstract template to the Kebab spicy or not
     */
    public abstract void makeSpicy();

    /**
     * An abstract template to set Kebab's price
     */
    public abstract void setKebabPrice();
}

AdanaKebab

package com.tugrulaslan.builderpattern;

/**
 * Adana Kebab concrete class is in the Concrete Builder Role that represents its specifications.
 */
public class AdanaKebab extends KebabBuilder {

    @Override
    public void assignKebabName() {
        kebab.setKebabName("Adana Kebab");
    }

    @Override
    public void assignMeatType() {
        kebab.setMeatType(MeatType.REDMEAT);
    }

    @Override
    public void makeSpicy() {
        kebab.setSpicy(true);
    }

    @Override
    public void setKebabPrice() {
        kebab.setPrice(10);
    }
}

UrfaKebab

package com.tugrulaslan.builderpattern;

/**
 * Urfa Kebab concrete class is in the Concrete Builder Role that represents its specifications.
 */
public class UrfaKebab extends KebabBuilder {

    @Override
    public void assignKebabName() {
        kebab.setKebabName("Urfa Kebab");
    }

    @Override
    public void assignMeatType() {
        kebab.setMeatType(MeatType.REDMEAT);
    }

    @Override
    public void makeSpicy() {
        kebab.setSpicy(false);
    }

    @Override
    public void setKebabPrice() {
        kebab.setPrice(9);
    }
}

ChickenKebab

package com.tugrulaslan.builderpattern;

/**
 * Chicken Kebab concrete class is in the Concrete Builder Role that represents its specifications.
 */
public class ChickenKebab extends KebabBuilder{

    @Override
    public void assignKebabName() {
        kebab.setKebabName("Chicken Kebab");
    }

    @Override
    public void assignMeatType() {
        kebab.setMeatType(MeatType.CHICKEN);
    }

    @Override
    public void makeSpicy() {
        kebab.setSpicy(true);
    }

    @Override
    public void setKebabPrice() {
        kebab.setPrice(7);
    }
}

 

Proxy Design Pattern and Proxy Objects

Reading Time: 5 minutes

Proxy Design Pattern

This pattern is listed as a Structural pattern in the tree of Design Patterns by GoF. It is more focused on intercepting and delegating client request.

Proxy Objects

Proxy objects are not a separate concept of the Proxy Design pattern, but the outcome of it. I am separately handling this because it eases to understand the case.  In the context Proxy Objects are known as the replica of the class they wrap, they are either created statically or dynamically.

Proxy objects do not modify the original class that they wrap, but apply some logic and call the original method in the proxy’s handler method. Clients first access to the Proxy Objects, it will intercept the requests and delegate the request only when required to the actual object.

The usage of proxy design pattern offers numerous advantages;

  • Access Control and limitation to the real objects. In a situation where the direct access to an enormous file or a remote connection might be expensive, thus Proxies will aid this issue by using lazy loading,
  • Providing  data caching, security, package access functionalities,
  • Proxy objects save the amount of memory used and have a very low memory footprint of the real object. For example in RMI, clients are concerned as Stubs that only make calls to the remote methods. So clients have smaller object footprints.

Feasible scenarios of the proxy pattern

1.Virtual Proxy: This is a situation where the creation of the real object is expensive like big file/network resources,

2.Protection Proxy: In this scenario proxy object acts as a security point where the access rights of the client to the real object are checked.

3.Remote Proxy: A local surrogate object of remote proxy object that resides in a different address space. The method calls on the local surrogate object results in the remote object. This situation is visible in Remote Method Invocation.

Example Class

Purpose

In this example, we will be simulating two concepts;
1.Security: Unauthorized access must be banned to the direct access to the real object,
2.Access to Object: The access to the real object will be monitored, we imagine the txt file is big, thus instantiation will be handled by the proxy for low memory usage.

Code Snippet

enum AccessRight {
    ADMIN, REGULAR, ANONYMOUS;
}

interface MyFile {
    void readFile();
}

class ActualFile implements MyFile {

    private final String fileName;

    public ActualFile(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void readFile() {
        System.out.println("Invoked class: " + this.getClass());
        System.out.println("Reading file: " + fileName + "\n");
    }
}

class ProxyFile implements MyFile {

    private ActualFile actualFile = null;
    private final String fileName;
    private final AccessRight accessRight;

    public ProxyFile(String fileName, AccessRight accessRight) {
        this.fileName = fileName;
        this.accessRight = accessRight;
    }

    @Override
    public void readFile() {
        System.out.println("Invoked class: " + this.getClass());
        //Checking the access rights
        if (accessRight == AccessRight.ADMIN) {
            if (actualFile == null) {
                actualFile = new ActualFile(fileName);
                System.out.println("instantiating the " + actualFile.getClass() + " class");
                actualFile.readFile();
            } else {
                System.out.println("returning the existing " + actualFile.getClass() + " class, no instantiation");
                actualFile.readFile();
            }
        } else {
            System.err.println("You have insufficient access to this class");
            return;
        }
    }
}

public class SampleFileReader {
    public static void main(String[] args) {
        MyFile regularUser = new ProxyFile("userdata.txt", AccessRight.REGULAR);
        MyFile adminUser = new ProxyFile("userdata.txt", AccessRight.ADMIN);

        regularUser.readFile();

        adminUser.readFile();
        adminUser.readFile();
    }
}

Explanation

Console Output:
Invoked class: class ProxyFile
You have insufficient access to this class
Invoked class: class ProxyFile
instantiating the class ActualFile class
Invoked class: class ActualFile
Reading file: userdata.txt

Invoked class: class ProxyFile
returning the existing class ActualFile class, no instantiation
Invoked class: class ActualFile
Reading file: userdata.txt

Ways of Creating Proxy Objects

a)Static Proxy: The programmer is responsible of creating a proxy object for every class,
b)Dynamic Proxy: Java is responsible of creating proxy objects dynamically that is natively achieved via reflection since JDK 1.3 or 3rd party libraries.

Purpose

We will be creating a Dynamic proxy for our audit logger that will wrap file reader class. It is assumed that audit logger is generic and must be called before the file reading is being performed

Code Snippet

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface MyFile {
    void readFile(boolean readOnly);
}

class ActualFile implements MyFile {

    private final String fileName;

    public ActualFile(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void readFile(boolean readOnly) {
        System.out.println("Invoked class: " + this.getClass());
        System.out.println("Reading file: " + fileName + "\n");
    }
}

class AuditLogger implements InvocationHandler {

    private Object targetActualObject;

    public AuditLogger(Object targetActualObject) {
        this.targetActualObject = targetActualObject;
    }

    public Object getTargetActualObject() {
        return targetActualObject;
    }

    public void setTargetActualObject(Object targetActualObject) {
        this.targetActualObject = targetActualObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("in " + this.getClass());
        System.out.println("====Proxied Class Information====");
        System.out.println("1. Proxied class: " + proxy.getClass());
        System.out.println("2. Invoked method on the proxied class: " + method.getName());
        System.out.println("3. Passed arguments on the method of the proxied class: " + args.toString());
        System.out.println("=================================");
        Object object = method.invoke(targetActualObject, args);
        return object;
    }
}

public class SampleFileReaderInvocationHandler {
    public static void main(String[] args) {
        ActualFile myFile = new ActualFile("userdata.txt");
        //myFile.getClass().getInterfaces() can also be replaced with new Class[]{MyFile.class}
        MyFile proxyObject = (MyFile) Proxy.newProxyInstance(SampleFileReaderInvocationHandler.class.getClassLoader(), myFile.getClass().getInterfaces(), new AuditLogger(myFile));
        proxyObject.readFile(true);
    }
}

Explanation

Console Output:
in class AuditLogger
====Proxied Class Information====
1. Proxied class: class $Proxy0
2. Invoked method on the proxied class: readFile
3. Passed arguments on the method of the proxied class: [Ljava.lang.Object;@3e34a1fc
=================================
Invoked class: class ActualFile
Reading file: userdata.txt

1.the newProxyInstance method of the Proxy object will dynamically generate a class which will implement the given interfaces as the second method argument.
2.then the proxied object will call the method invoke, the second parameter Method in the invoke method will refer to the designated method which is “readFile()” on the base class “ActualFile”

Dynamic Proxies at a glance

Dynamic proxies are created during run-time, a bit different than the proxy pattern and it makes use of byte code manipulation, the reflection class and compiling the Java code generated dynamically.
Furthermore, there are levels of the run-time code manipulation;
1.Low Level: This is the era of machine instruction’s level using such tools as ASM,
2.High Level: Briefly this is the Byte code. Having a new class not available as a byte code, but there will be a necessity of byte code generation in run-time and the class loader will load this byte code
working on Java classes only such tools as CGlib, AspectJ, ByteBuddy, Javassist

**Byte code: It is the Java program’s outcome after the successful compilation with the files extension of .class. This is where Java promise’s comes in cross-platform play “Write once, run anywhere”.
The bytecode is processed by JVM instead of the CPU. Still the compiled java classes are not fully compiled rather they remain as intermediate codes that have to be executed and interpreted by JVM.

**ClassLoader: Java ClassLoaders are used to load a compiled classes/bytecodes upon the request in run-time into the JVM and memory.

The purpose of dynamic proxying differs in such situtaions for example transactions, AOP, dynamic object mocking for unit testing etc.
It has been always on the scene and part of our day-to-day software development like Hibernate lazy loading entities, Spring AOP etc.

Elements of JDK Dynamic Proxies;

-java.lang.reflect.Proxy: The class enables to create a dynamic proxy for the passed interface,
-java.lang.reflect.InvocationHandler: The interface that is implemented by the Custom class where the actions are taken every time the application invokes a method on the proxy. Each Proxy class is associated with an InvocationHandler interface.

Strategy Pattern

Reading Time: < 1 minute

This pattern is listed in Behavioral pattern which allows you to define sets of algorithms in the parent class which will be extended by sub classes in which all the given algorithms are interchangeable. The best way to observe this pattern is to study the below example.

public abstract class Employee {

    public abstract int calculateSalary(int salary);
}

class Manager extends Employee {

    @Override
    public int calculateSalary(int salary) {
        return salary * 4;
    }
}

class Engineer extends Employee {

    @Override
    public int calculateSalary(int salary) {
        return salary * 3;
    }
}

class Technician extends Employee {

    @Override
    public int calculateSalary(int salary) {
        return salary * 2;
    }
}

class Main {

    public static void main(String[] args) {
        Employee employee;
        int managerSalary;
        int engineerSalary;
        int technicianSalary;

        //Calculate Manager's Salary and print the value
        employee = new Manager();
        managerSalary = employee.calculateSalary(1000);
        System.out.println("Manager's Salary: " + managerSalary);

        //Calculate Engineer's Salary and print the value
        employee = new Engineer();
        engineerSalary = employee.calculateSalary(1000);
        System.out.println("Engineers's Salary: " + engineerSalary);

        //Calculate Technician's Salary and print the value
        employee = new Technician();
        technicianSalary = employee.calculateSalary(1000);
        System.out.println("Technician's Salary: " + technicianSalary);
    }
}

As you’ve evaluated the example, the parent class has a globally defined method called calculateSalary and it is calculations vary for each employee, in each employees class we alter the contents and return value of the regarding method so that we can proceed the calculation for each personnel.