Skip to main content

GoF patterns

Why learn GoF Design Patterns?

Design patterns help you find out patterns in your code. It helps to visualize your code at a higher level and decompose it into logical units.  

What are Design Patterns?

Design patterns are canonical solutions to recurring problems. They are different from a library that is called from your code. Neither are they framework which is a complicated collection of libraries. Frameworks typically calls your code.

The 24 design patterns covered in GoF book can be divided into three categories.

  1. Creational Patterns. These patterns seek to answer - "How should objects be created?".  Examples are - Factory, Abstract Factory, Singleton, Builder, Prototype, Dependency Injection. They usually seek to decouple the construction of an object from its use. There are advantages to doing this.
    • Hide implementation of an object, only reveal its interface. 
    • Defer instantiation until run-time. 
    • Allow creation of finite number of instances. 
    • Have families of related objects that must be used together.   
  2. Behavioral Patterns. These patterns seek to answer - "How should objects behave and interact with each other?" Examples are - Strategy, Template, Iterator, Command, Chain of Responsibility, Memento, Visitor, State, Mediator, Observer.
    It seeks to define a behavior that objects within the logical unit present to the outside world. 
  3. Structural Patterns. These patterns seek to answer - "How should classes behave and interact with each other?" Examples are - Decorator, Adapter, Facade, Composite, Flyweight, Bridge, Proxy.
    These patterns are made up of logical unit that seeks to define structure within the unit.
There are underlying design principles that these patterns seek to implement.
  1. Rely on Interfaces, not Implementations. This forces us to write loosely coupled code where implementation can be changed without changing the interface.This prevents clients, which rely on the interface, to suffer any ill-effects if implementation changes. Never make any assumptions about the guts of any unit. Examples are - Decorator, Iterator, Adapter.
  2. The Open/Closed principle. Classes should be open for extension but closed for modification. Once you've written a class, never add or modify anything to it. The class functionality can be extended in new ways - Inheritance (e.g., Template Pattern), Delegation (e.g., Observer, MVC, Chain of Responsibility), Composition (e.g., Strategy).
  3. Principle of Least Knowledge, aka Demeter's Law. An object should only trust and make calls to its own methods, methods of objects that are passed in as parameters to it's methods and methods of objects created inside the class.
  4. Dependency Inversion and Hollywood Principle. Depend on abstractions, never on details. Don't call us, we'll call you. The high-level components call low-level components.

1. Strategy Pattern

Strategy design pattern decouples the algorithm that can be used to achieve an objective from the objective itself. There can be several different ways in which the objective can be achieved. We should be able to plugin the way that we are interested in at runtime.

The basic idea behind Strategy patterns is to make it easy to vary the behavior of a class at runtime, and do so using composition rather than inheritance. 

Let us take an example. We want to sort a list of strings. There are many ways, a.k.a algorithms, to sort the list - lexicographic, etc. 

// Step 1: Create a list of strings
List<String> listOfStrings = new ArrayList<String>();

// Step 2: Populate the list with data file
// Step 3: Sort this list of strings in lexicographical order
Collections.sort(listOfStrings, new Comparator<String>() {
    public int compare(String s1, String s2) {
        if (s1.equals("God Almighty") && !s2.equals("God Almighty")) {
            return 1;
        } else if (s2.equals("God Almighty") && !s1.equals("God Almighty")) {
            return -1;
        }
        return s1.compareTo(s2);
    }
});      


This is similar to Dependency Injection principle - both specify the behavior of an object using composition, i.e., by setting a member variable of the object.

2. Decorator Pattern

Decorator pattern is useful when a class is constantly being modified to implement new interfaces. Java IO makes heavy use of this design pattern. 

The defining characteristics of Decorator pattern are 
  • A large number of classes, all implementing the same interface
  • An object of each class can be created from an object of a common interface
  • Classes representing independent variables, that know nothing about each other's internal details
In Java IO, inputs and outputs are represented as streams. Inputs can be - Files, Memory Buffers (Arrays), Pipes, Network Connections, System.in (Keyboard Input). Likewise, outputs can be - Files, Memory Buffers (Arrays), Pipes, Network Connections, System.out and System.error (Screen output).

Streams are data flows - input streams originate from inputs and output streams from outputs. Java provides objects to encapsulate inputs, outputs and streams. The problem is that there are so many different types of inputs and outputs. How does Java ensure that all of these objects know to work with each other? We want to mix and match different types of inputs and outputs. 

All input streams derive from a common abstract class InputStream which contains standard operations shared by all streams - read(), close(), reset(). Then, for each type of input stream (i.e., each independent variable), there is a separate class deriving from InputStream - FileInputStream, ObjectInputStream, AudioInputStream, GZIPInputStream, ByteArrayInputStream, etc. Everyone of these objects can be constructed from an object of type InputStream. InputStream objects can be chained to get yet more InputStream objects. This chaining of objects - all of which descend from the same interface or abstract base class, to get more objects of the same base class, is the hallmark of the Decorator pattern.

ObjectInputStream ois = new ObjectInputStream(
                           new GZIPInputStream(
                               new BufferedInputStream(
                                  new FileInputStream("/serializedObject.gz")
                               )
                            )
                        );             

To read the object, we don't need to know the details about inner objects.

Object someObject = ois.readObject();
ois.close();  

3. Factory Pattern

Factory pattern is used when we have multiple objects to create. Using this pattern, we create object without exposing the creation logic to the client and refer to newly created object using a common interface.

The basic idea of Factory patterns is to 
  • Decouple instantiation of a class from its use
  • Create a mechanism for many alternative implementations to be instantiated from a single method
As a concrete example, let us say we are using MSSQL Server database in our application and we want to switch over to Oracle database. A dumb way to do this is:

// Pre-switchover
IDatabase database = new MSSQLDatabase();
  
// Post-switchover
IDatabase database = new OracleDatabase();

This is not smart because the switchover involves modifying code, re-building and re-releasing into production.

A smart way to do this is to use Factory pattern - hide the database creation details behind the factory class.

IDatabase database = DatabaseFactory.getDatabase();
You simply ask a Factory object to give you the correct type of database object and it magically knows how to. What is this Factory object and how does it know what type of object to instantiate? The Factory object reads in the name of the class to instantiate from a config file, and uses reflection to instantiate the correct object. Reflection is one possible way of implementing the Factory pattern that has the benefit of not requiring code to be recompiled. Note that the instantiation is using a static method on the Factory class.

public class DatabaseFactory {
    public static IDatabase getDatabase() {
        String databaseClassName = readFromConfig("database-class-name");
        IDatabase database = (IDatabase) Class.forName(databaseClassName).newInstance();
        return database;
    }

    private String readFromConfig(String key) {
        // read the config file 
        // return value for the key
    }
}

4. Abstract Factory Pattern

While the Factory pattern is a way to create any one of a bunch of classes that implement the same interface, the Abstract Factory patterns is a way to create groups of related classes that implement different interfaces. Both of these patterns abstract away the creation and implementation details from the user.
 
public abstract class DatabaseFactory {
    abstract IDatabase getDatabase();
    
    private String readFromConfig(String key) {
        // read the config file
        // return the value corresponding to key
    }
}

public MSSQLServerDatabaseFactory extends DatabaseFactory {
    public IDatabase getDatabase() {
        return new MSSQLServerDatabase();        
    }
}

public OracleDatabaseFactory extends DatabaseFactory {
    public IDatabase getDatabase() {
        return new OracleDatabase();        
    }
}

AbstractDatabaseFactory databaseFactory = new MSSQLServerDatabaseFactory();
IDatabase database = databaseFactory.getDatabase();
Abstract Factory pattern comes in handy when we don't want to use reflection, and also if we have different versions of Oracle and MSSQL databases.

To summarize, use Factory pattern when
  • You have a bunch of alternative classes - only one will be used at a time
  • And you would like to make it as easy as possible to add new choices, and pick between those choices

5. Singleton Pattern

There are situations where exactly one object of a particular type is needed - where it would be bad to have any more than one object of a particular class. Examples are device drivers, registry setting managers, or other system-wide shared objects.

A singleton object must satisfy two attributes -
  1. Exactly one (well actually, at most one) instance of the object should exist
  2. Everyone ought to be able to access that one singleton object
public class Singleton {
    // we maintain just one private static instance
    // this is the singleton object
    private static Singleton singleton;

    private Singleton() {
        // nobody can instantiate outside this class
        // as the constructor is private 
    }
    
    public static synchronized Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}
In the above code, we have marked the getter as synchronized, else two threads might interfere with each other and create two instances of singleton. Marking a getter a synchronized can lead to quite a performance hit. There are two ways to get around it -
  1. Eagerly instantiate the singleton (no need to synchronize getter after that)
  2. Use Double-Checked Locking and mark the member variable as volatile
// Option 1
public class Singleton {
    private volatile static Singleton singleton = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() { return singleton; } 
The drawback of this option is that it instantiates the object even without any client requesting it. This can be a problem if the singleton instance consumes lots of resources.

Double-checked locking is a software design pattern used to reduce the overhead of acquiring a lock by first testing the locking criterion without actually acquiring the lock.

//Check 1 of the double-checked lock
if (singleton == null) {
    synchronized (Singleton.class) {
        // Check 2 of the double-checked lock
        if (singleton == null) {
            singleton = new Singleton();
        }
    }
}
It is important to mark the singleton member variable as volatile. Declaring  a volatile Java variable means that the value of this variable will never be cached thread-locally, all reads and writes will go straight to "main memory". This makes sure a thread does not see a partially constructed object.

Java-specific fine print. Technically it is possible to end up with two singleton object despite all these precautions, if your code uses multiple class loaders.

6. Adapter Pattern

Adapter is used to bridge between two incompatible systems. An adapter takes in an object that implements an interface, and gives out an object that implements a different interface. For example, an adapter can read in an array and ouput a List object.

List<String> list = Arrays.asList(new String[]{"red", "green", "blue"});  

7. Facade Pattern 

In architecture, facade refers to the front of a building, i.e., beautiful exterior of a building which people see. The interior "messy" details are completely hidden behind this eye-pleasing facade.

In software, a facade is a simple interface available to users to perform complicated tasks. Multiple systems can integrate and work together behind a facade, however the user will see only the simple interface.

URL url = new URL("https://www.brainyquote.com/topics/simple-quotes");
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
String line;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}
reader.close();

Java Net library assumes that the user does not know or care about the details of how the contents of a URL are obtained. It handles all the complexity and provides a simple interface for the user to work with. In fact, this design pattern in used extensively in most of the Java libraries, e.g., Reflection, Networking, Database access, File access, Playing media file, etc.

Adhering to the Facade pattern ensures that the principle of least knowledge (Demeter's Law) is applied when interacting with a system.

8. Template Pattern

Template design pattern is a commonly used design pattern especially when we use frameworks or integrate with libraries. It provides users the ability to plugin customizations within complicated algorithms. 

Frameworks widely use Dependency Injection principle. The principle states:
  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend on details. Details should depend on abstractions.
In other words, high-level code says to low-level code: "Don't call us, we'll call you". Frameworks are, in fact, natural extension of the Template pattern.

We can find examples of Template pattern in frameworks. In many frameworks, a significant portion of the code is boilerplate code. For example, when executing a query on a database, the same series of steps must be completed:
  1. Establish a connection
  2. Execute query
  3. Perform cleanup
  4. Close the connection
  5. Map results
These steps are an ideal scenario for the Template method pattern. We can provide the missing step by supplying a callback method. This callback mechanism is precisely the approach that Spring uses with the JdbcTemplate class. The RowMapper interface is used to convert a single row of SQL data into a domain object of type T.

public class JdbcTemplate {
 
    public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
        return result(query(sql, new RowMapperResultSetExtractor<>(rowMapper)));
    }
 
    // Other methods...
}

We can provide logic for how to convert a single row:
public class BookRowMapper implements RowMapper<Book> {
 
    @Override
    public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
 
        Book book = new Book();
        
        book.setId(rs.getLong("id"));
        book.setTitle(rs.getString("title"));
        book.setAuthor(rs.getString("author"));
        
        return book;
    }
}
With this converter, we can then query a database using the JdbcTemplate and map each resulting row:
JdbcTemplate template = // create template...
template.query("SELECT * FROM books", new BookRowMapper());

9. Iterator Pattern

Iterator pattern defines ability to iterate or loop over collections. The basic point of Iterator pattern is that it separates walking over a collection from the implementation of the collection.

Every Java collection implements  the interface Iterable<T>. This interface has a single method Iterator iterator(). In other words, every Java collection offers a way to get a corresponding Iterator object. The Iterator object offers a way to get every element of a collection in sequence. This is completely independent of how the collection is implemented. But it is still type-safe, i.e., it will return objects of the type specified in the collection.

class SomeClass implements Iterable<String> {
}

class Main {
    public void method() {
        SomeClass someClass = new SomeClass();
        ...
        for (String s : someClass) {
            // do something
        }
    }
}

Iterators can be external or internal. In case of external iterators, the iterator object is different from the collection. In case of internal iterators, the collection itself is the iterator.

// External Iterators
List<String> alphabets = Arrays.asList(new String[]{"a", "b", "c"});
for (String letter: alphabets) {
    System.out.println(letter.toUpperCase());
}

Iterator<String> iterator = alphabets.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next().toUpperCase());
}
// Internal Iterators
List<String> alphabets = Arrays.asList(new String[]{"a", "b", "c"});
alphabets.forEach(l -> l.toUpperCase());

10. MVC Paradigm

The Model-View-Controller is an architectural pattern that is used for the design of User Interfaces. It consists of three logical entities 
  1. Model - underlying data which we want to represent
  2. View - representation of the data in some visually pleasing form
  3. Controller - logic that serves as the interaction between the view and the model  and carries information back and forth
The MVC paradigm uses other design patterns, e.g., Observer pattern, to handle user interactions with the view and the controller units.

11. Observer Pattern

In Observer pattern, there are variables (Publishers) that announce changes (Events) to their state and there are other objects (Subscribers) that subscribe to listen to those changes and react (Callback).

This pattern is used in frameworks and other patterns like the MVC pattern. Below is an example of how to use java.util to implement the Observer pattern.
@SpringBootApplication
public class ObserverApplication {

    public static void main(String[] args) {
        try(ConfigurableApplicationContext ctx 
                 = SpringApplication.run(ObserverApplication.class, args)){
            Observable observable = ctx.getBean(Observable.class);
            observable.notifyObservers("Hello");
        }
    }

    @Bean
    public Observable myObservable(){
        return new MyObservable();
    }

    @Bean
    MyObserver observer1(){
        return new MyObserver(1);
    }

    @Bean
    MyObserver observer2(){
        return new MyObserver(2);
    }

    @Autowired
    public void configureObservers(Observable myObservable, 
                                   MyObserver observer1, 
                                   MyObserver observer2){
        myObservable.addObserver(observer1);
        myObservable.addObserver(observer2);
    }
   static class MyObserver implements Observer {
        int id;

        public MyObserver(int id) {
            this.id = id;
        }

        @Override
        public void update(Observable o, Object arg) {
            System.out.println("Observer: " + id + ", 
                               Received object: " + arg);
        }
    }

    static class MyObservable extends Observable {
        @Override
        public void notifyObservers(Object arg){
            this.setChanged();
            super.notifyObservers(arg);
        }
    }
}

12. Command Pattern

A Command is a series of operations encapsulated as one object. This cohesive action object then can be passed around and executed at any point of time. This decoupling of "what" needs to be executed from "when", is achieved by the Command pattern.

Command objects lie at the heart of the Command pattern. A Command object has a single method, and whatever state is needed for that method to execute successfully. We can think of Command object as a function that can be passed around like an object.

Lambda functions in Java are an example of Command pattern. Java is an object-oriented language. It has support for some functional programming features. In Java, all code must be in a class. This is an excellent way to force an object-oriented design to Java projects. But, this can be an overkill for code for one-off use. Having to create a Java class for this in order for it to live there is unnecessary. Anonymous classes are an excellent way to encapsulate little bits of behavior into objects. Anonymous classes are ubiquitous in Java, e.g., listeners. It turns out that a very high proportion of anonymous classes simply consisted of objects that implement an interface with just one function. For example, Comparator is an Interface with a single method. Interfaces with just one method can actually be encapsulated using single functions. Such single method interfaces are called "Functional Interfaces".
For this type of usage, shorthand for a function might be more valuable then shorthand for a class. This is where Lambda functions come in. Lambda functions are simply anonymous functions. They are useful for scalaility as they are self-composed units that can executed independently. Also, they can be chained togther to perform complex operations.
// Map
Optional<String> optionalIsbn = books.entrySet().stream()
  .filter(e -> "Effective Java".equals(e.getValue()))
  .map(Map.Entry::getKey)
  .findFirst();

// Filter
List<Customer> charlesWithMoreThan100Points = customers
  .stream()
  .filter(c -> c.getPoints() > 100 && c.getName().startsWith("Charles"))
  .collect(Collectors.toList());

// ForEach
Stream.of("cat", "dog", "elephant", "fox", "rabbit", "duck")
  .takeWhile(n -> n.length() % 2 != 0)
  .forEach(System.out::println);

Threading in Java uses Command pattern. The Runnable or Callable interface defines the Command object that can be executed by the calling thread.

// Using Runnable
Runnable runnable = () -> {
    System.out.println("hello");
};

Thread thread = new Thread(runnable);
thread.start();

// Using Callable
ExecutorService executor = Executors.newSingleThreadExecutor();

Future<String> future = executor.submit(() -> {
    System.out.println("hello");
    return "done";
});
String result = future.get();

13. Composite Pattern

The Composite pattern allows us to treat a composition of objects exactly like one object. The basic idea is to compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.

In JUnit, we want to support suites of suites of suites of tests.

Composite introduces the following participants:

  • Component: declares the interface we want to use to interact with our tests.
  • Composite: implements this interface and maintains a collection of tests.
  • Leaf: represents a test case in a composition that conforms to the Component interface.
The pattern tells us to introduce an abstract class which defines the common interface for single and composite objects. The primary purpose of the class is to define an interface. When applying Composite in Java we prefer to define an interface and not an abstract class. Using an interface avoids committing JUnit to a specific base class for tests. All that is required is that the tests conform to this interface. We therefore tweak the pattern description and introduce a Test interface:

public interface Test {
    public abstract void run(TestResult result);
}

TestCase corresponds to a Leaf in Composite and implements this interface as we have seen above.

Next, we introduce the Composite participant. We name the class TestSuite. A TestSuite keeps its child tests in a Collection:

public class TestSuite implements Test {
    private Vector fTests= new Vector();
}

The run() method delegates to its children:

public void run(TestResult result) {
    for (Enumeration e = fTests.elements(); e.hasMoreElements(); ) {
        Test test = (Test)e.nextElement();
        test.run(result);
    }
}

Finally, clients have to be able to add tests to a suite, they can do so with the method addTest:

public void addTest(Test test) {
    fTests.addElement(test);
}

Notice how all of the above code only depends on the Test interface. Since both TestCase and TestSuite conform to the Test interface we can recursively compose suites of test suites. All developers can create their own TestSuites. We can run them all by creating a TestSuite composed of those suites.

Here is an example of creating a TestSuite:

public static Test suite() {
    TestSuite suite= new TestSuite();
    suite.addTest(new MoneyTest("testMoneyEquals"));
    suite.addTest(new MoneyTest("testSimpleAdd"));
}

14. Builder Pattern

The Builder patterns is an object creation pattern. It allows us to create an object bit-by-bit using a couple of properties at a time. The Builder pattern is a good choice when degsigning classes whose constructors or static factories would have more than a handful of parameters, espectially if many of the parameters are optional or of identical type.

public class Employee {

    private final String firstName;    //required

    private final String lastName;    //required

    private final int age;    //required

    private final int personalId; // required

    private final String phone;    //optional

    private final String address;    //optional

    private final String mail;    //optional

    public static class EmployeeBuilder {

        private final String firstName;    //required

        private final String lastName;    //required

        private final int age;    //required

        private final int personalId; // required

        private String phone;    //optional

        private String address;    //optional

        private String mail;    //optional

        public EmployeeBuilder(

       String firstName, String lastName, int age, int personalId) {

            this.firstName = firstName;

            this.lastName = lastName;

            this.age = age;

            this.personalId = personalId;

        }

        public EmployeeBuilder setAddress(String address) {

            this.address = address;

            return this;

        }

        public EmployeeBuilder setPhone(String phone) {

            this.phone = phone;

            return this;

        }

        public EmployeeBuilder setMail(String mail) {

            this.mail = mail;

            return this;

        }

        public Employee build() {

            // call the private constructor in the outer class

            return new Employee(this);

        }

    }

    private Employee(EmployeeBuilder builder) {

        this.firstName = builder.firstName;

        this.lastName = builder.lastName;

        this.age = builder.age;

        this.personalId = builder.personalId;

        this.phone = builder.phone;

        this.address = builder.address;

        this.mail = builder.mail;

    }

    public String getFirstName() {

        return firstName;

    }

    public String getLastName() {

        return lastName;

    }

    public int getAge() {

        return age;

    }

    public int getPersonalId() {

        return personalId;

    }

    public String getPhone() {

        return phone;

    }

    public String getAddress() {

        return address;

    }

    public String getMail() {

        return mail;

    }

}

We can create an Employee object from client code as follows:

public class EmployeeTest {
    public static void main(String[] args) {
        Employee employee = 
new Employee.EmployeeBuilder("Cristiano", "Ronaldo", 33, 7)
                .setPhone("0045-1234556")
                .setAddress("Juventus")
                .setMail("CR@Juventus.org").build();
    }
}

15. Chain of Responsibility Pattern

Avoid coupling the sender to the receiver by allowing more than one receiving element to handle the request. The sender interacts only with the first receiver in the queue. 

One classic use case of this pattern is in handling mouse clicks. UI Applications are often built on the Composite pattern, meaning that windows contain other windows. The nested windows pass the mouse click action down from one to another until some window has a handler for a mouse click.

Another example is exception handling in Java.

public static void methodOne(int randomNumber) throws
                        IOException, NullPointerException {
    if (randomNumber == 1) {
        throw new IOException("IO Exception");
    } else if (randomNumber == 2) {
        throw new NullPointerException("Null Pointer Exception");
    }
}

public static void methodTwo(int randomNumber) throws 
                    IOException {
    try {
        methodOne(randomNumber);
    } catch(NullPointerException npe) {
        System.out.println("Null Pointer Exception 2");
    }
}

public static void methodThree(int randomNumber) {
    try {
        methodTwo(randomNumber);
    } catch(IOException ioe) {
        System.out.println("IO Exception 3");
    }
}

public static void main(String[] args) {
    int randomNumber = (int) Math.ceil(Math.random() * 10);
    methodThree(randomNumber);
}

16. Memento Pattern

The basic idea of Memento pattern is to allow objects to know how to save their state and go back to that saved state. 

Java has built-in support for the Memento pattern via the Serializable interface.

public class MyClass implements Serializable {

    private String string;

    private List<String> list = new ArrayList<>();

    private transient HTMLWriter htmlWriter = new HTMLWriter();
}

So long as all member variables implement Serializable interface (and the built-in classes all do), just calling out that the class implements Serializable is enough. If a member variable belongs to a class that does not implement Serializable, we can just mark that member as "transient", meaning that Java should not write out its value to file.

MyClass myClass = new MyClass();

List<String> list = Arrays.asList(new String[]{"Anthos","Pothos","Aramis"});

myClass.setList(list);

myClass.setString("The Three Musketeers");


// Serialize

FileOutputStream fileOut = new FileOutputStream("MyClass.obj");

ObjectOuputStream out = new ObjectOutputStream(fileOut);

out.writeObject(myClass);

out.close();

fileOut.close();


// Deserialize

FileInputStream fileIn = new FileInputStream("MyClass.obj");

ObjectInputStream in = new ObjectInputStream(fileIn);

MyClass myClass = (MyClass) in.readObject();

in.close();

fileIn.close();

Objects marked Serializable know how to save their state to a file, and this is an implementation of the Memento pattern. The Serializable interface is one easy way to implement the Memento pattern, you could re-implement it in other ways too.

17. Visitor Pattern

The Visitor pattern specifies how to visit nodes of a structure. A typical use case of the Composite pattern is to visit each node in the tree and do something with it. This is where Visitor pattern comes in handy. The Visitor pattern involves having an object that knows how to traverse the tree of a Composite object. The Visitor object sits outside the Composite object - which allows us to have any number of Visitor classes for a given Composite object. Each Visitor class defines its own traversal of the Composite tree, as well as its own unique operation on each node.

Visitor object breaks encapsulation of the Composite structure it is visiting as it knows the internal details of the structure. This means tight coupling between the visitor and the visited object.

Example is NIO2 FileVisitor.

18. State Pattern

The State pattern, is a behavioral software design pattern, also known as the objects for states pattern. This pattern is used to encapsulate varying behvior for the same object based on its internal state. This can be a cleaner way for an object to change its behavior at runtime without resorting to large monolithic conditional statements and thus improve maintainability.
 
Spring Statemachine is a framework for application developers to use state machine concepts with Spring applications. The idea is that your application may exist in a finite number of states and certain predefined triggers can take your application from one state to the next. Such triggers can be based on either events or timers.

It is much easier to define high level logic outside of your application and then rely on the state machine to manage state. You can interact with the state machine by sending an event, listening for changes or simply request a current state.

static enum States {

    STATE1, STATE2

}


static enum Events {

    EVENT1, EVENT2

}


public StateMachine<States, Events> buildMachine() throws Exception {

    Builder<States, Events> builder = StateMachineBuilder.builder();


    builder.configureStates()

        .withStates()

            .initial(States.STATE1)

            .states(EnumSet.allOf(States.class));


    builder.configureTransitions()

        .withExternal()

            .source(States.STATE1).target(States.STATE2)

            .event(Events.EVENT1)

            .and()

        .withExternal()

            .source(States.STATE2).target(States.STATE1)

            .event(Events.EVENT2);


    return builder.build();

}


StateMachine<States, Events> stateMachine = buildMachine();

stateMachine.start();

stateMachine.sendEvent(Events.EVENT1);

stateMachine.sendEvent(Events.EVENT2);

19. Flyweight Pattern

The Flyweight design pattern is a memory optimization design. The main objective is to help conserve memory so that we don't pull in large data-structures into main memory when the program is running. This can be a huge performance improvement. There might be a case in your program where instances of a class all share most of their state. A flyweight is an object that minimizes memory use by sharing as much data as possible with other similar objects. It is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory. Often parts of the object can be shared, and it is common practice to hold them in external data structure and pass them to the flyweight objects temporarily when they are used.

Java uses String Interning to store strings. Strings in Java are immutable which means it stores only one copy of each distinct string value. To test if two strings are the same, simply test if they are the same object. So, string variables are actually references to these immutable strings. If we modify a string variable, a new underlying string value is created and the string variable is set to point to that new value. If we have many variables having the same string value, they are referring to the same string object. This is a way to conserve memory. This is an example of Flyweight pattern.

20. Bridge Pattern

The Bridge pattern helps decouple the abstraction and implementation by creating two separate class hierarchies. With the bridge pattern, the abstraction maintains a Has-A relationship with the implementation instead of a IS-A relationship. The Has-A relationship is achieved through composition where the abstraction maintains a reference of the implementation and forwards client requests to it.

public abstract class Message {

    MessageSender messageSender;

    public Message(MessageSender messageSender){

        this.messageSender=messageSender;

    }

     abstract public void send();

}


public class TextMessage extends Message{

    public TextMessage(MessageSender messageSender){

        super(messageSender);

    }

    @Override

    public void send(){

      messageSender.sendMessage();

    }

}


public class EmailMessage extends Message{

    public EmailMessage(MessageSender messageSender){

        super(messageSender);

    }

    @Override

    public void send(){

        messageSender.sendMessage();

    }

}

public interface MessageSender {

    public void sendMessage();

}


public class TextMessageSender implements MessageSender {

    @Override

    public void sendMessage(){

        System.out.println("TextMessageSender: Sending text message...");

    }

}


public class EmailMessageSender implements MessageSender{

    @Override

    public void sendMessage(){

        System.out.println("EmailMessageSender: Sending email message...");

    }

}

MessageSender textMessageSender=new TextMessageSender();

Message textMessage=new TextMessage(textMessageSender);

textMessage.send();

21. Mediator Pattern

The idea behind the Mediator design pattern is to have a central object that encapsulates how a set of objects interact.

Mediator pattern is used to reduce communication complexity between multiple objects. This pattern provides a mediator object which normally handles all the communications between different objects and supports easy maintainability of the code by loose coupling.

A great real world example of mediator pattern is traffic control room at airports. If all flights will have to interact with each other for finding which flight is going to land next, it will create a big mess.
Rather flights only send their status to the tower. These towers in turn send the signals to conform which airplane can take-off or land. We must note that these towers do not control the whole flight. They only enforce constraints in the terminal areas.

Another good example of mediator pattern is a chat application. In a chat application we can have several participants. It’s not a good idea to connect each participant to all the others because the number of connections would be really high. The best solution is to have a hub where all participants will connect; this hub is just the mediator class.

Example code.

22. Prototype Pattern

Using the prototype pattern, you do not create a new object for each client requesting the object. Instead, you start by creating a single object, called a prototype and make copies of it for each client requesting the object. In Java, this is achieved through object cloning, a way to make a copy of an object with the same state as the original object.

Java provides the Cloneable interface to mark objects that permit cloning. This interface is a marker interface and therefore does not contain any method declaration. When implemented in a class, Cloneable marks that objects of the class can be cloned. To perform cloning, you need to call the protected clone() method of the Object class through a call to super.clone().

If an object calls super.clone() but does not implements Cloneable, the method throws an exception of type, CloneNotSupportedException.

A call to super.clone() performs a shallow copy where all the fields values of the original object are copied to the new object. If a field value is a primitive type, a shallow copy copies the value of the primitive type. But, if a field value is a reference type, then only the reference is copied, and not the referred object itself. Therefore, both the original and its clone refer to the same object and if either one modifies the referred object, the modification will be visible to the other. This might result in unexpected behavior in an application. In such situation, you should perform a deep copy that makes copies of dynamically allocated memory pointed to by the reference type fields. In a deep copy, the original and the copied objects are independent of each other and therefore the objects can update their own fields without worrying about any referencing problems.

When using the prototype pattern, should we go for shallow copy or for deep copy? There is no hard and fast rule, it all depends on the requirement. If an object has only primitive fields or immutable objects (whose state cannot change, once created), use a shallow copy. When the object has references to other mutable objects, then either choose shallow copy or deep copy. For example, if the references are not modified anytime, avoid deep copy and go for shallow copy. But, if you know that the references will be modified and the modification might affect the intended behavior of the application, then you need to go for deep copy.

When you go for a deep copy, you will need to override the Object.clone() method in all the member classes and then recursively clone their objects. Alternatively, you can get a deep copy by serializing an object and then restoring it back through Java object serialization. Using serialization is a simple approach, keep in mind serialization is resource intensive and can produce unexpected results if not done properly.

Example code.

23. Proxy Pattern

The Proxy pattern uses a proxy (surrogate) object “in place of” another object. The objective of a proxy object is to control the creation of and access to the real object it represents. A common use of a proxy is to defer the cost of instantiating of an object (that is expensive to create) until it is actually needed by clients. The Proxy Pattern is used to create a representative object that controls access to another object, which may be remote, expensive to create or in need of being secured.

Java RMI provides an example of a remote proxy. Java supports the communication between the two objects residing at two different locations (or two different JVMs) using RMI. RMI is Remote Method Invocation which is used to build the client and service helper objects, right down to creating a client helper object with the same methods as the remote service. Using RMI you don’t have to write any of the networking or I/O code yourself. With your client, you call remote methods just like normal method calls on objects running in the client’s local JVM.

Proxies might also be useful if expensive method calls can be cached. For instance the proxy for a command object could maintain a cache. This technique is called Memoization, and can lead to big performance savings for computationally intensive commands, or those involving file or database IO. 

Comments

Popular posts from this blog

Supporting OpenTracing jaeger in spring boot applications

This page describes code changes in a typical spring-boot based application to support OpenTracing and jaeger. Instrumenting a tracer If you are creating a simple spring boot application that uses  spring-boot-starter-web , by default, the application does not support writing traces to jaeger. To support jaeger tracing, the first thing is to modify the build.gradle to add dependency of jaeger: dependencies {      implementation  'org.springframework.boot:spring-boot-starter-web'      implementation  'io.opentracing.contrib:opentracing-spring-web-starter:3.0.1'      // support opentracing jaeger      implementation  'io.opentracing.contrib:opentracing-spring-jaeger-starter:3.1.2'      testImplementation( 'org.springframework.boot:spring-boot-starter-test' ) {          exclude group:  'org.junit.vintage' , module:  'junit...

HOWTO on implementing scanning into a CI Pipeline

Introduction As a part of the Software Security Assurance guidelines, we are required to perform various types of security scanning.  Security scanning is a specialized kind of testing that attempts to identify potential software bugs that could make a deployed application vulnerable to attack and compromise.  As with any testing, the tests can be manual or automatic, and they wan be performed at various points in the development cycle.   We can classify security scanning into three general categories: Static Application Security Testing (SAST)   - performed against source code / configuration data a static scan looks for common logic errors that might lead to system compromise. Dy namic Application Security Testing  (DAST)  - Performed against a running system, a dynamic scan looks for vulnerable software / configurations that might lead to system compromise. Security Monitoring  - Deployed as a part of the system, a security monitor co...

Fortify Tooling User Guide

  Introduction The fortify-tools container is located within a shared repository in OCIR and requires a JWT to be able to access.  The variable WF_JWT will need to be set to a valid MAT You will want to choose one of three ways to use the Fortify SCA tooling: Integration Description Using the Fortify Tools Standalone to Scan a Project This is for using the tooling without integration in GitLab CI or Jenkins CI. Using the Fortify Tools In GitLab CI to Scan a Project This is for using the tooling against a project that whose code is hosted in GitLab and whose CI engine is GitLab CI. Using the Fortify Tools In Jenkins CI to Scan a Project This is for using the tooling against a project that whose code is hosted in GitLab and whose CI engine is Jenkins CI. Using the Fortify Tools Standalone to Scan a Project Simple Usage Run the Fortify Tools in a container docker run -t --rm -v <path to project source root directory>:/var/fortify/src phx.ocir.io/oraclegbudevcorp/cn-shared/s...