Java Streams

Design Patterns in Functional Paradigm

April 19, 2020

After acquainting the readers with the implementation of various concepts and techniques of functional programming in previous blogs, this blog will make you learn a very interesting concept which is Design Patterns. We will cover various patterns and their implementation in Functional paradigm.

First let us discuss what are design patterns. Design pattern can be any well described solution for the most commonly occurred problems while designing a software. It provides some standard approach to solve a recurring problem. It saves time, makes code easy to understand, faster to develop and debug.

Design patterns are divided into 3 parts

  1. Creational : It provides algorithm to instantiate an object in the best possible way.
  2. Structural : It provides different way to create a class structure.
  3. Behavioural : It provides better interaction between objects.

There are 23 Design patterns mentioned in a classical book named Gand of Four, and most of them have been used extensively in building the JDK libraries.

We will discuss the following design patterns in this blog. Most of them are covered in GOF, but few are also mentioned in other ones like Fluent Interfaces is mentioned in a book by Martin Fowler who is a famous author and architect.

  • Iterator Design pattern
  • Strategy Design pattern
  • Decorator Design Pattern
  • Fluent Interfaces
  • Command Design Pattern
  • Factory Pattern
  • Builder Pattern

Iterator design pattern

It is a behavioural design pattern that focusses on how objects interact with one another.

The iterator design pattern is about accessing elements of a container, without exposing the structure of the object.

For example, to traverse any collection, traditionally we used for loop, in which everything about the structure of the object can be known with the index. But, with iterator the internal representation of the object which is being traversed is not exposed. Like it doesn’t matter it is a list or set, it will be iterated in the same way with its corresponding iterator.

So this pattern enables to get a way to access the elements of a collection object in sequential manner without any need to know its internal representation. Example of this pattern are Examples of this pattern are the built-in Iterator interface java.util.Iterator, and Enumeration java.util.Enumeration.

Lets look at the UML diagram of this pattern

Iterator is an interface that has further concrete implementations for example, ListIterator, SetIterator and many more.
Now there are collection and concrete collection components.
In every Collection type, for example List, Set, Queue there will be a factory iterator method, that will be implemented by their concrete implementations and that returns an instance of the corresponding concrete iterator.
For example in case of a list and its implementations, the underlying instance is a list iterator. The list iterator is an implementation of the iterator interface that understands how to iterate over the various list objects in the collection’s API.

Now to implement this pattern, lets say we have some container that stores some data

In Object Oriented style, we would have to implement Iterable interface to make the class iterable and then we would have to write the implementation for that factory iterator method that also include the implementations for next() and hasNext() methods as we need them to iterate over the container.

class MyContainer implements Iterable<String> {
    @Override
    public Iterator<String> iterator() {
          return new Iterator<String>() {
              @Override
               public boolean hasNext (){
               }
              @Override
               public String next (){
               }
          };
    }
}
 

In functional implementation, there is no need to implement iterable interface. Because the next() hasNext() are no longer required here, the conditional statements and assignments will not be used here.

Now let us implement iterator in Functional programming with the help of a Lambda and predefined functional interface Consumer.

We will create a Container MyArrayElements which takes an Object Array Elements of size 5.

We also need to create an Internal Iterator so that outside world does not know the implementation of iteration. So we will create a method forEach() to iterate over this container.

import java.util.function.Consumer;

public class MyArrayList{
    Object[] elements = new Object[5];  

    public MyArrayList(Object[] elements) {
        this.elements = elements;
   }

   public void forEach(Consumer<Object>  action) {
       for(int i=0; i<elements.length; i++) {
                  action.accept(elements[i]);
     }
  }
}

The forEach() method will invoke the Accept() of Consumer on each element present inside the MyArrayList.

Now let us invoke and try accessing the data using the forEach().

public class IteratorPatternDemo {
     public static void main(String[] args) {
       MyArrayList list = newMyArrayList(new Object[] ( 1,3,5,,7,8});
    
       list.forEach(System.out.println);
    }
}

Thus consumer’s accept method is called internally by forEach of MyArrayList. And the output will be all the elements

Output :1 3 5 7 8

So that’s how we created an internal iterator using consumer and higher order function

Thus ewe have noticed that the functional implementation for internal iterator is shorter and simpler as compared to the object oriented design.

Strategy Design pattern

It is a behavioral pattern. This pattern is used when we have multiple solutions or algorithms for a specific task, and we or the client decides that which implementation will be used at runtime.

Thus this pattern can eliminate the conditional statements in the application.

For example, there may be multiple sorting techniques to sort elements of collection, and it is the decision of the user to decide which algorithm he wants to use.
The best example in JDK itself is the Comparator API. The sort method of Collections Utility class takes 2 args, first is the list to sort, and second one is a comparator implementation. So based on comparator’s different implementations, the objects get sorted in different ways.
So the client can specify the sorting strategy or the Comparator implementation which is to be used at runtime.

Collections.sort(List list, Comparator c)

Let’s look at that UML diagram now.

This is the UML diagram for the strategy pattern. The context receives a request and it decides which strategy to use based on the situation. Each strategy is contained within its own class. So, there is a base interface and all the concrete strategy classes which defines individual strategies implement this interface.

In functional paradigm there is no need to create multiple concrete classes for different strategies. Because here the Lambdas can be used to provide any implementation on the fly.
So, Let’s look at this pattern in an example

Suppose we have history of purchased stocks by a person, which includes all the purchase transactions i.e the stock symbol, the current price of 1 unit of the stock, and the number of purchased units. So we have an arrayList that’s holding all the data

Suppose the user bought the following stocks: Apple, Microsoft, Google, Amazon and some other day he again bought Google and so on. So the class StrategyPattern contains the add function.

import java.util.ArrayList;
public class StrategyPattern {
       public static void main (String[] args) {
     
             List<Stock> stockList = new ArrayList<>();

            stockList.add(new Stock("AAPL", 318.65, 10));
            stockList.add(new Stock("MSFT", 166.86, 45));
            stockList.add(new Stock("Google", 99, 12.5));
            stockList.add(new Stock("AMZ", 1866.74, 45));
            stockList.add(new Stock("GOOGL", 1480.20, 3.5));
            stockList.add(new Stock("AAPL", 318.65, 8));
            stockList.add(new Stock("AMZ", 1866.74, 9));
       }
}

Now we have to filter the data on various criteria. So the class for the Stock having the 3 mentioned fields.

  1. The symbol
  2. Current Price and
  3. The number of purchased units.
class Stock {
                  private String symbol;
                  private double price;
                  private double units;

                 public Stock(String symbol, double price, double units) {
                        this.symbol = symbol;
                        this.price = price;
                        this.units = units;
                 }
                 public String getSymbol() {
                        return symbol;
                 }
                 public void setSymbol(String symbol) {
                        this.symbol = symbol;
                 }
                 public double getPrice() {
                        return price;
                 }
                 public void setPrice(double price) {
                        this.price = price;
                 }

                  public double getUnits() {
                           return units; 
                  }

                 public void setUnits(double units) {
                     this.units = units;
                }

                @Override
                public String toString() {
                     return "Stock [symbol=" + symbol + ", price=" + price + ", units=" + units + "]";
                }
}

Now to define different filtering strategies we create a class StockFilters And in this class, we write a method to filter the stock. So, let us write a method that will filter the data on the basis of the symbol of the stock, bySymbol() which takes the stockList and the symbol and returns a list of Stocks.

Lets us also create an arrayList object that we are going to return from this method and the method which iterates through the list and find the stocks with this symbol..

public class StockFilters {

      public static List<Stock> bySymbol(List<Stock> list, String symbol) {
                      List<Stock> filteredData = new ArrayList<>();
                      for (Stock stock : list) {
			     if (stock.getSymbol().equals(symbol))) 
                                	filteredData.add(stock);
	              }
		      return filteredData;
      }
}

So we have created a filter to filter our data by name. Now let us invoke the method

public class StrategyPattern {
     public static void main(String[] args) {
            List<Stock> stockList = new ArrayList<>();

            stockList.add(new Stock("AAPL", 318.65, 10));
            stockList.add(new Stock("MSFT", 166.86, 45));
            stockList.add(new Stock("Google", 99, 12.5));
            stockList.add(new Stock("AMZ", 1866.74, 45));
            stockList.add(new Stock("GOOGL", 1480.20, 3.5));
            stockList.add(new Stock("AAPL", 318.65, 8));
            stockList.add(new Stock("AMZ", 1866.74, 9));
       
            StockFilters.bySymbol(stockList,"AMZ").forEach(System.out::println);
   }
}

When we execute this code, the output is we get all the Amazon stocks.

Stock [symbol=AMZ, price=1866.74, units=45.0]

Stock [symbol=AMZ, price=1866.74, units=9.0]

Till now, we had only one filter, there was no other strategy to filter the stocks but suppose we want to filter the stocks by the price and we want to get the data of all the stocks having current price above a certain value.
For that we have the StockFilters class that is completely dedicated to have filters in it. So we can create one more filter in that class. We add the method public byPriceAbove, in the StockFilters class.

public static List<Stock> byPriceAbove(List<Stock> list, double price)
{
		List<Stock> filteredData = new ArrayList<>();
		for (Stock stock : list) {
			if (stock.getPrice() > price) {
				filteredData.add(stock);
			}
		}
		return filteredData;
}

When we invoke this from the main function

StockFilters.byPriceAbove(stockList, 300) . forEach(System.out::println);

So when we execute this, we get all the stocks which have price greater than 300.

The above approach however tells us that we shall keep on adding various filters as we come across different filtering requirements. But we have a better alternative here with the use of Lambda. We can just create one method, that takes the needed strategy at runtime .

So let us create a method filter in the class StockFilters, that takes the data and a predicate to filter that data.

public static List<Stock> filter(List<Stock> list, Predicate<Stock> p) {
		List<Stock> filteredData = new ArrayList<>();
		for (Stock stock : list) {
			if (p.test(stock))
				filteredData.add(stock);
	        }
		return filteredData;
}

So now to filter stock at any basis, we just need to invoke this function, which is a higher order function, that will take two arguments: the stock list and the predicate to filter out elements on specific test condition.

StockFilters.filter(stockList, stock -> stock.getSymbol().equals("AMZ")). forEach(System.out::println);;

This will give the output which is all the Amazon stocks. For filtering the data on basis of price, we need to change the Lambda and the predicate only.

Decorator Design Pattern

This pattern is used to modify functionality of an object at runtime without affecting other instances of the same class.
So basically in this pattern, we have functionality wrappers and we keep on wrapping the object into these wrappers one after the other according to the additional decorations or the additional functionalities we want.

If we want to modify the behaviour of an object, we can use inheritance or composition but the problem with this approach is that this is done at compile time, we can’t add any new functionality or remove any existing one at run time. So for this, decorator pattern is used.

Now let us observe the UML diagram for this pattern:

Here is the UML diagram for this decorator pattern. The Component class is an interface, although it can be an abstract class, that has a concrete instance represented here in the ConcreteComponent of this diagram. The ConcreteComponent is the component who’s functionality we are going to modify i.e we are going to decorate, and the Decorator is the base decorator or wrapper that we will extend and create other decorators from. Now it should be of note that both the ConcreteComponent and the Decorator extend the component so that they can be treated the same. From here we can then create multiple ConcreteDecorators to decorate our object and provide functionality as we develop.

It utilizes composition and inheritance to achieve this, while implementing that in object oriented paradigm, we get into more complex and heavy-weight syntax.

Let us see how can we do this functionally.

Suppose we have a burger, and we have to modify this burger object at runtime using the decorator pattern. Then we have some methods to modify the functionality or say to add decorations.
First is addVeggies() method that will add the veggies on the current burger, so it is adding one decoration on the top of the previous version and then it returns a fresh instance of the Burger class.
And then similar to addVegies, we have addCheese(), which is adding another decoration on the previous version of the burger.
And finally in this class we have a toString() implementation, that is printing the type of the burger.

public class Burger {

	private String burgerType;
	
	public Burger(){
		this.burgerType = "";
	}
	
	private Burger(String type){
		this.burgerType = type;
	}
		
	public Burger addVeggies() {
		System.out.println("Adding veggies to the burger");
		return new Burger(this.burgerType += " Veggie");
	}
	
	public Burger addCheese() {
		System.out.println("Adding cheese to the burger");
		return new Burger(this.burgerType += " Cheese");
	}
	
	
	public String toString() {
		return String.format("%s",burgerType +" burger");
	}

}

Now suppose there is a burger shop, that prepares all kind of burger i.e, basically it will do the job of adding the decorations. So lets take here a field, it is a function decorator(), that takes a burger and returns a burger. Now, for applying the decorations, this class needs the base Burger on which it is going to apply the decorations. So it takes a baseBurger and apply the decorations on this burger, so the baseBurger should be the input to the decoration function, so that it can apply that decoration on this base burger. Now, we will tell the decoration function that he want at runtime..
So, we are going to create the constructor here that will set the decoration field.

import java.util.function.Function;

@SuppressWarnings("unchecked")
public class BurgerShop {
	Function<Burger, Burger> decoration;
        public BurgerShop(Function<Burger, Burger> decoration) {
		this.decoration = decoration;
	}
	public Burger use(Burger baseBurger) {
			
			System.out.println(baseBurger);
			return decoration.apply(baseBurger);
		}
}

Now let us create the DecoratorPattern class and invoke the function

public class DecoratorPattern {
	public static void main(String[] args) {
		Burger myBurger = new BurgerShop(burger -> burger.addCheese())
				.use(
                                         new BurgerShop(burger -> burger.addVeggies())
						.use(
                                                       new Burger()
                                                       )

                                          )
		System.out.println("Finally i got : "+ myBurger);
	}

}

When we execute this

Output :

Base Burger : burger
Adding veggies to the burger
Base Burger : Veggie burger
Adding Cheese to the burger
Finally i got Veggie Cheese burger

Thus we have seen that using functional programming we get very concise, easy to understand and less complex code.

Fluent Interfaces

A fluent interface is a way of implementation by which we get easy-readable, and flowing interface. This pattern is not from the gang of four design patterns but the reason we have taken it here is that it’s a very important pattern if we look at functional programming.

Suppose there is an api to add and place orders.

Let us create the Order class. In this class we have two fields, first is a list in which we can add items that we want to purchase and the second is the address to which we want to deliver the items. Then we have method add that can add the item to the bucket. Then we have a method deliverAt that sets the delivery location at which we want to get the items delivered and last method in this class is, place, that places the order.

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

class Order{
	
              private List<String> cart = new ArrayList<>();
	      private String address = "";
		
               public Order add(String order) {
		     System.out.println(order);
		     return this;
	      }
	
	       public Order deliverAt(String location){
		       System.out.println("at "+location);
		      return this;
	     }
	
	public static void place(Consumer<Order> cons) {
		Order order = new Order();
		cons.accept(order);
		System.out.println("Order placed!");
	}
}

Now let us create a FluentShopping class. Suppose i am going to pick some items for me and i will place the order for those items. I want to buy shoes and headphones for me. So we set the delivery address and then order the items.

public class FluentShopping {

	public static void main(String[] args) {
		Order myOrder = new Order();

		myOrder.add("shoes");
		myOrder.add("Headphones");
				
		myOrder.deliverAt("Street no 45, Jodhpur"); 		

		myOrder.place();
	}

}

When we execute this code.

We get the output as:

Shoes and Headphones at “Street no 45, Jodhpur”

Order Placed

So this is the normal way, we write the code. Now to make it fluent, if we observe this code, we are using the myOrder object to do the job continuously. We are using myOrder.addShoes, myOrder.addHeadphones, myOrder.deliverAtThisAddress and myOrder.place
However here is a sequence of operations one after the other on the same myObject. So to refactor this or to improve this code, we can use method chaining and then we could apply all the operations in a single chain.

So if we are invoking the add(), it should again return the order object so that i can again invoke the next method on it and similarly this deliverAt method should also return the order object. The place is the last call, so it don’t have to return anything.

So lets modify the implementation of these 2 methods to return an order back.

public class FluentInterfacesPattern {

	public static void main(String[] args) {
				
		Order.place(order -> order
				.add("Shoes")
				.add("Jacket").deliverAt("my place")
				.add("Airpods")
				.add("Tee").deliverAt("her place")
				);
	}

}

Factory Design Pattern

Next, we will discuss about factory design pattern which is a creational pattern that focusses on on how objects are created. It deals with creating objects without exposing the instantiation logic. The client does not even know which type of object is being created.

For example, Calendar class in JDK. The getInstance() method of this class returns the instance of Calendar class. If we check which type of the instance is, it shows java.util.GregorianCalendar. So the Gregorian calendar is the actual concrete implementation that’s being called underneath this factory instance, of which client is completely unaware.

Calendar cal = Calendar.getInstance();
System.out.println(cal);

So The factory design pattern is a way to instantiating a class inside a designated method that we call a factory method. The class having this method is a factory class.

Let us see the UML diagram of this pattern

There’s some product having concrete products, and there is a ProductFactory class having factory method which returns the instance of the specific type of product. Now there can be multiple product implementations, of which the client is unaware, as there’s only factory thats exposed to the client and rest everything is just referenced through the interface product.

Now, we will discuss how to implement this pattern with the help of a problem statement. Suppose, we have to decide the flooring that we should use in any housing unit. And the criteria to decide on that is the climate or you can say temperature variation of that particular city or region. Like if you live in a hot climate, concrete floors can be a choice. Because concrete floors are cool compared to most other kinds. And similarly for other climates, we should choose the floor accordingly.

So as product we have an interface Flooring with method installation() and there are three implementations to Flooring interface i.e WoodenFlooring, CorkFlooring, and ConcreteFlooring. These all are overriding the installation() method

Now let us create the factory class FlooringFactory, where we will create a static factory method to get our Flooring object of any of these three implementation. This will take minimum and maximum temperature as parameters.

import java.util.function.Supplier();
public class FlooringFactory { 
            public static Flooring getFlooring(int minTemperature, int maxTemperature) {
                  Supplier<Flooring> flooring;
                  if(minTemperature <= 5 && maxtemperature <=20) {
                            flooring = () -> new WoodenFlooring();
                  }else if(minTemperature<=5 && maxTemperature >=45) {
                            flooring = () -> new corkFlooring();
                 }else {
                            flooring = () -> new ConcreteFlooring();
                 }
                 return  flooring.get();
           }
}

This will return the appropriate flooring object based on the temperature as passed by the user as input.

public class WoodenFlooring implements Flooring {
     @Override
     public void installation() {
          System.out.println("The Wooden flooring is installed! ");
     }
}
public class CorkFlooring implements Flooring {
     @Override
     public void installation() {
          System.out.println("The Cork flooring is installed! ");
     }
}
public class ConcreteFlooring implements Flooring {
     @Override
     public void installation() {
          System.out.println("The concrete flooring is installed! ");
     }
}

In our driver class,

public class FactoryMethodPattern {
     public static void main(String[] args) {
          Flooring floor = FlooringFactory.getFlooring(-1, 18);
          floor.installation();
     }
}

Output:
The Wooden flooring is installed!

This is the implementation of factory pattern which is used to create instances of different classes of same type using functional way.

Next let us discuss about the next pattern.

Builder Design Pattern

Builder pattern is all about providing flexibility in Object Creation. So, Its a flexible solution to various complex object creation problems. The problems are considered complex if they have a lots of arguments to a constructor or lots of setters.

And with Builder pattern we also ensure the immutability of the object after its creation.

Examples for builder pattern in JDK are StringBuilder and StringBuffer.
Append method of these classes returns the instance of itself after adding values to it.

java.lang.StringBuilder;
java.lang.StringBuffer;

StringBuilder str = new StringBuilder();

str.append("Basics");
str.append("Strong");

Let’s see the UML diagram of builder pattern now.

The product defines the type of complex object that is going to be generated, and the builder defines all of the steps that must be taken to create the product correctly. There may be any number of concrete builder classes that actually contain the functionality to create that particular complex product.

Now there’s Director class that controls the algorithm to generate product. If there are multiple concrete builders, the director will capture the specific concrete builder object first, that is to be used to create product and then it calls the method of concrete builder in correct order to generate the product object like buildPiece1() 2, 3.. and on completion it calls the getCompleteProduct(). that will return the product.

So for implementing builder pattern functionally we will consider Mobile as the complex product, that we are going to create and we will create builder class to simplify its creation.

Let us create a Mobile class, with the attributes Ram, Storage, Camera, Battery, Display etc. and in order to make this class immutable, we have kept all the fields final. Also, let us create a constructor to initialize the attributes.

public class Mobile {

	final int ram, storage;	//in GB
	final int battery;	//mAh
	final int camera;	//MP
	final String processor;
	final double screenSize;    //display

	public Mobile(int ram, int storage, int battery, int camera, String processor, double screenSize) {
	this.ram = ram;
	this.storage = storage;
	this.battery = battery;
	this.camera = camera;
	this.processor = processor;
	this.screenSize = screenSize;
	}

	public int getRam() {
		return ram;
	}

	public int getStorage() {
		return storage;
	}

	public int getBattery() {
		return battery;
	}

	public int getCamera() {
		return camera;
	}

	public String getProcessor() {
		return processor;
	}

	public double getScreenSize() {
		return screenSize;
	}
	
	public String toString() {
		return String.format("Specifications- RAM: %d Storage: %d Battery: %d Camera: %dMP Processor: %s Display: %f''", ram, storage, battery, camera, processor, screenSize);
	}
}

Now if we want to create the object for this class, we need to pass all these parameters to constructor. And suppose if we don’t want to provide all the fields to construct objects, then we would have to create a different constructor with only those parameters that we want.
Basically what we are doing, we are creating different constructors for different combinations of input parameters to the constructor.
So to solve this problem and to provide more flexibility we can use the builder pattern here.

Now let us create the builder class for mobile. This builder class will have steps to create mobile. So we have all mobile attributes here.

In imperative style, we create setters for every attribute in this builder and then create the object of product, so that client will be able to create mobile object by invoking the setters created with builder object.

But to implement this in functional way, we will create one method that will take consumer of mobileBuilder. And after setting fields, we will create method for building the object.

import java.util.function.Consumer;
public class MobileBuilder {
	
	int ram, storage;	//in GB
	int battery;		//mAh
	int camera;		//MP
	String processor;
	double screenSize;	//display
	
	public MobileBuilder with(Consumer<MobileBuilder> buildFields) {
	        buildFields.accept(this);
	        return this;
	    }

	public Mobile createMobile() {
	        return new Mobile(this);
	    }
}

Now let us modify the constructor of Mobile Class. The modified constructor will not have all the fields, instead it will have a MobileBuilder object which will provide all the fields. Thus parameters to the constructor will be reduced, and also we won’t need to create any other constructor.

public Mobile(MobileBuilder builder) {
		this.ram = builder.ram;
		this.storage = builder.storage;
		this.battery = builder.battery;
		this.camera = builder.camera;
		this.processor = builder.processor;
		this.screenSize = builder.screenSize;
	}

Now let us create the driver class, i.e the BuilderDemo class and let us create the mobile object using MobileBuilder

public class BuilderDemo {
	public static void main(String[] args) {
             MobileBuilder builder = new MobileBuilder();	
             Mobile myMobile = builder..with(myBuilder -> {
			myBuilder.ram = 4;
			myBuilder.battery = 4000;
			myBuilder.processor = "A12 Bionic";
		}).createMobile();
		System.out.println(myMobile);
      }
}

Here while passing this argument to with method, we can pass values to any attribute we want. It is not necessary to provide every value. So we won’t be needing more constructors in mobile class and also there’s no need to pass null for any parameter. For other fields for which client may not provide value, we can assign some default value in builder class.

When we invoke createMobile() after setting all values, it this will create and return mobile object with its complete state. When we execute the code, we get following output.

Output:
Specifications-Ram 4 Storage:0 battery 4000 Camera:0MP Processor A12bionic Display:0.000

The values which are not supplied will be set to default values.

So this was builder pattern, which helps in simplifying object creation for
immutable classes. and we implemented this method in functional way by creating one higher order function and using Supplier. Let us discuss the next pattern

Command Pattern

Command design pattern is a behavioural design pattern which is about encapsulating a request as an object. In this pattern we write code that sequences and executes methods based on run-time decisions.

For example consider all the implementations of Runnable interface in JDK. like creating thread by implementing runnable. So each implementation that we give to runnable is a different command on which the methods of runnable get executed.

So this pattern is a way to encapsulate a request as an object, parameterize clients with different requests and perform corresponding operations.

Lets see the UML diagram for this pattern

There is a command interface that defines execute() operation to get called by an invoker. Invoker is responsible to control the command to carry out requests only.
Every request here is a different command, which we define by concrete commands. and there’s a receiver who performs actual work. It consists of operations action() that will be called by the concrete Commands. This makes invoker to be independent of how requests are being performed.

Let us take an example to understand it. Suppose we have a class AC, that is having few actions upon it, that we’ll be calling such as turnOn, turnOff, IncTemperature, decTemp etc. and there will be invoker, thats remote, that can control the command for more than one acs, like the invoker would be able to handle every command from client to receiver, that’s the AC.

public class AC {
	public void turnOn() {
	System.out.println("turning on AC");
	}
	
	public void turnOff() {
		System.out.println("turning off AC");
	}
	
	public void incTemp() {
		System.out.println("Increasing temperature");
	}
	
	public void decTemp() {
		System.out.println("Decreasing temperature");
	}
}

Now let us create ACAutomationRemote, which will invoke the command and execute it by invoking execute method on passed command

class ACAutomationRemote{
	Command command;
	
	public void setCommand(Command command) {
		this.command = command;
	}
		public void buttonPressed() {
		command.execute();
	}
}

Now in previous implementation we needed to create concrete command classes for every possible request, like StartAC, OffAC etc that will implement this Command interface, and inside those concrete command classes, we used to encapsulate AC object and operation
together as command and pass their objects to invoker that’s the ACAutomationRemote that will invoke particular
command given by user.

But post java 8, we have lambdas, which can replace the all concrete command classes like StartAC, StopAC and others. so we will pass lambda as a command to the invoker.

public class CommandDesignDemo {

	public static void main(String[] args) {
		AC ac1 = new AC();
		AC ac2 = new AC();
		ACAutomationRemote remote = new ACAutomationRemote();


		remote.setCommand(() -> ac2.turnOn());
		remote.buttonPressed();
		
		remote.setCommand(()-> ac1.turnOff());		
		remote.setCommand(ac1::incTemp);


		remote.buttonPressed();

 	}

}

Thus in command pattern, here we can add any number of new action methods to receiver without changing the client code and with implementation with lambda, the entire pattern become much simpler,
as we no longer need any concrete implementations for every command/action.

So now to summarize, we have studied various design patterns in this blog and implemented every pattern in functional programming using various use cases.

Hope after reading this blog, you have understood the design patterns well.

Stay Connected for the next blog !!