Ron and Ella Wiki Page

Extremely Serious

Page 28 of 33

Angular Data Bindings

Binding coordinates state transfer between the component's class and its template. The following table shows the different types of Angular data bindings.

TypeDescriptionDirectionExample
InterpolationEvaluates the expression between the double curly braces.One-way

(Component→Template)
<h1>{{header}}</h1>
Property BindingUpdates the property if there's a change from the bound component state.

This is typically denoted by the square bracket surrounding the target property.
One-way

(Component→Template)
<img [src]='imageURL'/>
Event BindingUpdates the bound component state if an event's was fired.

This is typically denoted by the parenthesis surrounding the event property.
One-way

(Component←Template)
<button (click)='onSave'>Save</button>
Two-Way BindingNormally use with the form elements.

This is typically denoted by the combined square and parenthesis surrounding the ngModel property.
Two-way

(Component↔Template)
<input type='text' [(ngModel)]='name'/>

JavaScript Prototypal Inheritance

The JavaScript's facility to provide inheritance is via prototype property of the constructor function (i.e. accessible via __proto__ in the instance context). Thus, we normally use the prototype to make properties and functions possible to be inherited. See the following code to do the inheritance properly.

Code 1 - Proper Way of Implementing Prototypal Inheritance
"use strict";

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

Person.prototype.middleName = "";

Object.defineProperty(Person.prototype, 'fullName', {
  get: function() {
    var name = (this.firstName||"");
    name += (name.length>0 ? " " : "") + (this.middleName||"");
    name += (name.length>0 ? " " : "") + (this.lastName||"");
      return name;
  }, 
  set: function(name) {
    var DELIM = " ";
    var names = (name||"").split(DELIM);
    this.firstName = names[0];
    this.lastName = names[1];
  }
});

function Employee(firstName, lastName, position) {
   Person.call(this, firstName, lastName);
   this.position = position||"";
}

Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;

var person = new Person();
person.fullName = "Ron Webb";
console.log(person.fullName);

var employee = new Employee("Ofelia", "Webb", "Manager");
console.log(employee.fullName);
console.log(employee.position);

Based on preceding code, the Employee is inheriting the Person via the following snippet:

Snippet 1 - Inhering the Person Class
Employee.prototype = Object.create(Person.prototype);

But, we should not stop there. We need to also specify the constructor to use the create the instance of Employee like the following snippet:

Snippet 2 - Specify the Constructor to Create the Employee Instance
Employee.prototype.constructor = Employee;

Lastly, in the constructor implementation of the Employee try to also call the constructor of the Person like the following snippet:

Snippet 3 - Call the Parent's Constructor
function Employee(firstName, lastName, position) {
   Person.call(this, firstName, lastName);
   this.position = position||"";
}

Using ES6 Syntactic Sugar for Inheritance

With ES6, this was greatly simplified because we don't need to deal with the prototype property of the constructor function like the following code:

Code 2 - ES6 implementation of Code 1
"use strict";

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.middleName = "";
  }
  
  get fullName() {
    var name = (this.firstName||"");
    name += (name.length>0 ? " " : "") + (this.middleName||"");
    name += (name.length>0 ? " " : "") + (this.lastName||"");
      return name;  
  }
  
  set fullName(name) {
    const DELIM = " ";
    var names = (name||"").split(DELIM);
    this.firstName = names[0];
    this.lastName = names[1];  	
  }
}

class Employee extends Person {
  constructor(firstName, lastName, position) {
   super(firstName, lastName);
    this.position = position||"";
  }
}

var person = new Person();
person.fullName = "Ron Webb";
console.log(person.fullName);

var employee = new Employee("Ofelia", "Webb", "Manager");
console.log(employee.fullName);
console.log(employee.position);

In ES6, we can now use the class keyword to define a class and a constructor method for its initialization. Regarding the constructor, we can now use the super method to call the parent constructor.

Moreover, implementing getter (i.e. using the get keyword before the function name) and setter (i.e. using the set keyword before the function name) are now very simple. Also the const keyword is now available to define immutable identifier just like what we can see on the fullName setter as we can see in the following snippet:

Snippet 4 - Using the Const Keyword
set fullName(name) {
  const DELIM = " ";
  var names = (name||"").split(DELIM);
  this.firstName = names[0];
  this.lastName = names[1];  	
}

All these enhancements in ES6 related to inheritance, in the background it still using the prototypical way. Remember these are just syntactic sugar.

Changing React Component State

Introduction

React renders component based on any changes on its state (i.e. data).

Updating State with Form Components

In React, a form input component controlled by it is known as controlled component. These components maintains their own state and update itself based on user input as depicted by the following code:

Code 1 - Controlled Text Input
const MessageView = (props) => {
  return(
    <h1>{props.message}</h1>
  );
}

class App extends React.Component {
  state = {
    message : ""
  };

  handleMsgChange = (event) => {
    this.setState({
      message: event.target.value
    });
  }
  
  render() {
    return(
        <div>
          <input type="text" placeholder="Type Something" value={this.state.message} onChange={this.handleMsgChange}/>
          <MessageView message={this.state.message}/>
      </div>
    );
  }
}

ReactDOM.render(<App />, mountNode);

The App component above has a message attribute in state as seen in the following snippet:

Snippet 1 - App Component State
state = {
  message : ""
};

We can bound the message attribute to the text input via the value attribute and listen to the changes on itself using the onChange attribute which in turn updates the state of the message like the following snippet:

Snippet 2 - Controlled Text Input
<input type="text" placeholder="Type Something" value={this.state.message} onChange={this.handleMsgChange}/>

The handleMsgChange that is bound to text input onChange attribute must update the message state of the React App component and could have the following implementation. This makes the React state as the single source of truth.

Snippet 3 - handleMsgChange Implementation
handleMsgChange = (event) => {
  this.setState({
    message: event.target.value
  });
}

This particular implementation will also trigger the re-render of the MessageView component since it is also dependent to the state of the message as we can see in the following snippet:

Snippet 4 - MessageView listening to the State of the Message
<MessageView message={this.state.message}/>

Updating State that may be Asynchronous

React might batch multiple setState function calls into a single update for performance. Thus the correct way to change the React state is by using the setState function that accepts a function instead of object like the following snippet:

Snippet 5 - Updating the React State
this.setState((prevState, props) => {
   //Do the state update here.
})

Let us try to create a reusable ButtonClear component that will clear value of the message attribute with the following snippet.

Snippet 6 - ButtonClear Component
const ButtonClear = (props) => {
  if (props.visible) {
    return(
        <button onClick={props.handleClear}>Clear</button>
    );
  }
  return(null); //Returns nothing
}

Based on the preceding snippet the ButtonClean component will only display the clear button if we tell it to make it visible via its visible attribute. Moreover, it is also expecting to have a handleClear implementation that will be bound to button's onClick attribute.

The handleClear implementation will be provided by the App component like the following snippet with the recommended usage of the setState() function:

Snippet 7 - App Component handleClear Implementation
handleClear = () => {
  this.setState((prevState, props) => {
    return {message: ""}; //Clears the message
  });
}

Thus we can use the ButtonClear component in the App component like the following snippet:

Snippet 8 - Using the ButtonClear in the App Component
<ButtonClear handleClear={this.handleClear} visible={this.state.message!=""}/>

We also instructed it to make the clear button visible only if the message is not empty.

The updated complete code with ButtonClear component can be seen as follows:

Code 2 - The Complete Code
const MessageView = (props) => {
  return(
    <h1>{props.message}</h1>
  );
}

const ButtonClear = (props) => {
  if (props.visible) {
    return(
        <button onClick={props.handleClear}>Clear</button>
    );
  }
  return(null);
}

class App extends React.Component {
  state = {
    message : ""
  };

  handleMsgChange = (event) => {
    this.setState({
      message: event.target.value
    });
  }
  
  handleClear = () => {
    this.setState((prevState, props) => {
      return {message: ""}; //Clears the message.
    });
  }
  
  render() {
    return(
        <div>
          <input type="text" placeholder="Type Something" value={this.state.message} onChange={this.handleMsgChange}/>
          <ButtonClear handleClear={this.handleClear} visible={this.state.message!=""}/>
          <MessageView message={this.state.message}/>
      </div>
    );
  }
}

ReactDOM.render(<App />, mountNode);

Note: We can run both Code 1 and Code 2 using jsComplete Playground

Java Core Functional Interfaces

Description

Functional interface is an interface with a single abstract method (SAM).

List of Core Functional Interfaces

Interface NameArgumentsReturns
BiConsumer<T,U>(T, U)void
BiFunction<T, U, R>(T, U)R
BinaryOperator<T>(T, T)T
BiPredicate<T,U>(T, U)boolean
Consumer<T>Tvoid
Function<T, R>TR
Predicate<T>Tboolean
Supplier<T>T
UnaryOperator<T>TT

Using RxJava PublishSubject Concurrently

Introduction

Implementing hot observable can be achieved with RxJava's PublishSubject. Moreover, we will try to publish data to the subject concurrently safely (i.e. using the synchronized keyword). Thus the complete code at the bottom uses several threads for publishing data and several threads for subscription.

Creating an Instance of PublishSubject

A simple way to create an instance of the PublishSubject instance is by invoking the static method create like the following snippet (see the complete code at the bottom).

Snippet 1 - Invoking the Static Method Create
PublishSubject subject = PublishSubject.create();

Sending Data to PublishSubject

Sending data to a PublishSubject instance can be done by calling its onNext method. Place the actual call in a synchronized block if we are sending data concurrently like the following snippet (see the complete code at the bottom):

Snippet 2 - Sending Data to PublishSubject Instance
synchronized (subject) {
    subject.onNext(String.valueOf(strItem));
}

Subscribing to PublishSubject

To listen to any of the data sent to a PublishSubject instance we can use one of the subscribe method. In addition, we can also opt to use the computation Scheduler (i.e. normally the Scheduler is the one responsible to managing threads in a multi-threaded environment.) via the observeOn method (i.e. for some reason the subscribeOn method is not working with PublishSubject instance.) like the following  snippet (see the complete code at the bottom):

Snippet 3 - Subscribing to PublishSubject Instance
subject.observeOn(Schedulers.computation())
        .subscribe(___item -> {
    System.out.println(_name + " ThreadID:" + Thread.currentThread().getId() + ": " + ___item);
});

The Complete Code

package xyz.ronella.reactive;

import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.IntStream;

public class PublishedObservable {

    public static void main(String ... args) {
        final PublishSubject subject = PublishSubject.create();

        List publishers = new ArrayList<>();
        List subscribers = new ArrayList<>();

        int defaultThreadCount = 3;

        ExecutorService executorPublisher = Executors.newFixedThreadPool(defaultThreadCount);
        ExecutorService executorSubscriber = Executors.newFixedThreadPool(defaultThreadCount);

        class LocalPublisher implements Runnable {

            private long _item;
            private String _name;
            private PublishSubject _subject;

            public LocalPublisher(String name, long itemStart, PublishSubject subject) {
                this._name = name;
                this._item = itemStart;
                this._subject = subject;
            }

            @Override
            public void run() {
                try {
                    while(true) {
                        Thread.sleep(1000);
                        String strItem = String.valueOf(++_item);
                        System.out.println("\n" + _name + " ThreadID:" + Thread.currentThread().getId() + ": " + strItem);
                        synchronized (_subject) {
                            _subject.onNext(String.valueOf(strItem));
                        }
                    }
                } catch (InterruptedException e) {
                    System.out.println(_name + " interrupted.");
                }
            }
        }

        class LocalSubscriber implements Runnable {

            private String _name;
            private PublishSubject _subject;

            public LocalSubscriber(String name, PublishSubject subject) {
                this._name = name;
                this._subject = subject;
            }

            @Override
            public void run() {
                _subject.observeOn(Schedulers.computation())
                        .subscribe(___item -> {
                    System.out.println(_name + " ThreadID:" + Thread.currentThread().getId() + ": " + ___item);
                });
            }
        }

        IntStream.rangeClosed(1, 3).forEach(___idx ->
                subscribers.add(executorSubscriber.submit(new LocalSubscriber("Subscriber " + ___idx, subject))));

        IntStream.rangeClosed(1, 6).forEach(___idx ->
                publishers.add(executorPublisher.submit(new LocalPublisher("Publisher " + ___idx,___idx * 100, subject))));

        subject.subscribe( ___item -> System.out.println("Main - ThreadID:" + Thread.currentThread().getId() + " " + ___item));

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        executorPublisher.shutdownNow();
        executorSubscriber.shutdown();
    }
}

Producer and Consumer Implementations

Introduction

When trying to understand concurrency the producer and consumer implementation is normally the first concept to know. The producer will place in data to a shared resource only if the consumer has already emptied it. Where the producer and consumer are two different workers.

Using Wait and Notify

This is the classic way of implementing producer and consumer. We call the wait method if we want to block the execution and notify if we want to continue. Since theses methods are part of the Object class, every class in java has them. However, to use them the current thread must acquire the a monitor otherwise an IllegalMonitorStateException will be thrown. One of the way to acquire a monitor is to use the synchronized block. This is what we will be using in the following code.

Code 1 - Wait and Notify Implementation
package xyz.ronella.concurrency;

import java.util.Stack;
import java.util.stream.IntStream;

public class ProducerConsumerWaitNotify {

    public static void main (String ... args) {

        final Stack sharedData = new Stack<>();
        final Object monitor = new Object();

        //The data to process.
        final IntStream dataRange = IntStream.range(1, 10);

        Thread producer = new Thread(() -> {

            try {
                dataRange.forEach(___data -> {
                    synchronized (monitor) {
                        try {
                            while (sharedData.isEmpty()) {
                                sharedData.push(___data);
                                System.out.println("Producing " + ___data);

                                monitor.notify();
                            }

                            monitor.wait();
                        } catch (InterruptedException e) {
                            System.out.println("Producer Interrupted.");
                            throw new RuntimeException();
                        }
                    }
                });
            }
            catch (RuntimeException e) {
                System.out.println("Final producer exit.");
            }
        });

        Thread consumer = new Thread(() -> {
            try {
                while (true) {
                    synchronized (monitor) {
                        while (!sharedData.isEmpty()) {
                            System.out.println("Consuming " + sharedData.pop());
                            monitor.notify();
                        }

                        monitor.wait();
                    }
                }
            } catch (InterruptedException e) {
                System.out.println("Consumer Interrupted.");
            }
        });

        consumer.start();
        producer.start();

        Delayer.delay(1000);

        producer.interrupt();
        consumer.interrupt();

        System.out.println("Done");
    }

}

Notice the Delayer.delay(100) (see the Delayer code) near the end of the code. It is only there to delay the exit of the main thread.

Using Lock Conditions

An alternative to implementing producer and consumer is using the lock conditions. Some of the advantages of using Lock interface over synchronized is that it is not required to code it in a single block and can also cross scopes (e.g. you can acquire lock in a method and unlock it in different method.).

To use lock conditions must first create an instance of Lock like the following:

Snippet 1 - Instantiates a Lock
final Lock lock = new ReentrantLock();

Once created, we can now create an instance of Condition for both producer and consumer like the following:

Snippet 2 - Create Producer and Consumer Conditions
final Condition producerCond = lock.newCondition(); final Condition consumerCond = lock.newCondition();

We acquire the monitor using the lock method and release it using unlock method of the Lock instance like the following (see the actual usage on code 2):

Snippet 3 - Acquiring and Releasing a Monitor using Lock Interface
try{
	lock.lock();

	.
	.
	.

}
finally{
	lock.unlock();
}

Knowing all of these, we can now combine them to the following code.

Code 2 - Lock Condition Implementation
package xyz.ronella.concurrency;

import java.util.Stack;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;

public class ProducerConsumerLockCondition {

    public static void main (String ... args) {

        final Stack sharedData = new Stack<>();
        final Lock lock = new ReentrantLock();
        final Condition producerCond = lock.newCondition();
        final Condition consumerCond = lock.newCondition();

        //The data to process.
        final IntStream dataRange = IntStream.range(1, 10);

        Thread producer = new Thread(() -> {

            try {
                lock.lock();
                dataRange.forEach(___data -> {
                    try {
                        while (sharedData.isEmpty()) {
                            sharedData.push(___data);
                            System.out.println("Producing " + ___data);

                            consumerCond.signal();
                        }

                        producerCond.await();
                    } catch (InterruptedException e) {
                        System.out.println("Producer Interrupted.");
                        throw new RuntimeException();
                    }
                });
            }
            catch (RuntimeException e) {
                System.out.println("Final producer exit.");
            }
            finally {
                lock.unlock();
            }
        });

        Thread consumer = new Thread(() -> {
            try {
                lock.lock();
                while (true) {
                    if (!sharedData.isEmpty()) {
                        System.out.println("Consuming " + sharedData.pop());
                        producerCond.signal();
                    }

                    consumerCond.await();
                }
            } catch (InterruptedException e) {
                System.out.println("Consumer Interrupted.");
            }
            finally {
                lock.unlock();
            }
        });

        consumer.start();
        producer.start();

        Delayer.delay(1000);

        producer.interrupt();
        consumer.interrupt();

        System.out.println("Done");
    }
}

Using ArrayBlockingQueue

Another implementation is to just the use an instance of ArrayBlockingQueue and use put and take methods.  The put method blocks until a space (i.e. based on the capacity) is available and the take method blocks until there are some data to take. Thus, we don't need any explicit locking for this.

We define the capacity of an ArrayBlockingQueue on its constructor as we can see as part of the following code.

Code 3 - ArrayBlockingQueue Implementation
package xyz.ronella.concurrency;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.stream.IntStream;

public class ProducerConsumerArrayBlockingQueue {

    public static void main (String ... args) {

        final long delay = 10;
        final BlockingQueue sharedData = new ArrayBlockingQueue<>(1);

        //The data to process.
        final IntStream dataRange = IntStream.range(1, 10);

        Thread producer = new Thread(() -> {

            try {
                dataRange.forEach(___data -> {
                    try {
                        sharedData.put(___data);
                        System.out.println("Producing " + ___data);
                    } catch (InterruptedException e) {
                        System.out.println("Producer Interrupted.");
                        throw new RuntimeException();
                    }
                });
            }
            catch (RuntimeException e) {
                System.out.println("Final producer exit.");
            }
        });

        Thread consumer = new Thread(() -> {
            try {
                while (true) {
                    Thread.sleep(delay);
                    //This will block if the sharedData is empty.
                    int data = sharedData.take();
                    System.out.println("Consuming " + data);
                }
            } catch (InterruptedException e) {
                System.out.println("Consumer Interrupted.");
            }
        });

        consumer.start();
        producer.start();

        Delayer.delay(1000);

        producer.interrupt();
        consumer.interrupt();

        System.out.println("Done");
    }
}

The Delayer Code

package xyz.ronella.concurrency;

public class Delayer {

    private Delayer() {}

    public static void delay(long millis) {
        final Object mainMonitor = new Object();
        Thread mainTimer = new Thread(() -> {
            try {
                Thread.sleep(millis);
                synchronized (mainMonitor) {
                    mainMonitor.notify();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        mainTimer.start();

        synchronized (mainMonitor) {
            try {
                mainMonitor.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

ConcurrentHashMap Parallel Operations

Introduction

In Java 8, the forEach, search and reduce methods of the ConcurrentHashMap have parallel operation capability. Normally, it is indicated by the parameter called parallelismThreshold. This parameter specifies the number of elements before the execution becomes parallel. For example, if the parallelismThreshold is 10 then if the number of elements to process is below 10 then it will be processed in the current thread (i.e. single thread) otherwise it will be processed in parallel.

Using the forEach method

The forEach method has different overloads but we will just be focusing on the simplest one. The one that accepts parallelismThreshold and BiConsumer parameters. To demonstrate parallel execution in action lets start with the ConcurrentHashMap that has items less than the parallelismThreshold like the following snippet (see the complete code at the bottom):

Snippet 1 - Elements less than parallelismThreshold
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

//This will generate a stream with 1 to 9 as integer content.
IntStream.range(1, 10).forEach(item -> map.put(String.valueOf(item), item));

long parallelismThreshold = 10;

BiConsumer<String, Integer> biConsumer = (key, value) -> {
    System.out.println("key: " + key + " value: " + value + " thread: " + Thread.currentThread().getName());
};

map.forEach(parallelismThreshold, biConsumer);

Running the above snippet will not run in parallel execution since the items in the map is less than the parallelismThreshold. But updating the variable parallelismThreshold variable to 9 and run it again. We must see an output like the following output:

Output 1 - Updating the parallelismThreshold to 9 in snippet 1
key: 1 value: 1 thread: main
key: 8 value: 8 thread: ForkJoinPool.commonPool-worker-1
key: 2 value: 2 thread: main
key: 9 value: 9 thread: ForkJoinPool.commonPool-worker-1
key: 3 value: 3 thread: main
key: 4 value: 4 thread: main
key: 5 value: 5 thread: main
key: 6 value: 6 thread: main
key: 7 value: 7 thread: main

Notice the ForkJoinPool.commonPool-worker thread which indicates that the forEach method is operating on parallel.

Using the search method

The search method is the functionality to find an item in the map with parallel support. This methods has siblings namely: searchEntries, searchKeys and searchValues but we will just be focusing on the search method with parallelismThreshold and BiFunction parameters. For the sample usage, see the following snippet (see the complete code at the bottom).

Snippet 2 - Sample usage of search method
BiFunction<String, Integer, Integer> biFunction = (key, value) -> {

    if (key.equals("9")) {
        return value; //Stop searching.
    }
    return null; //Continue searching.
};

System.out.println("Value: " + map.search(parallelismThreshold, biFunction));

We can run the previous snippet after concatenating it with snippet 1. The search will stop when the BiFunction implementation return non-null value. Otherwise, the search will continue. This is good if you have a big map.

Using the reduce method

The reduce method is the one to use if we need to accumulate something from the map items to produce a single result. This also supports parallel operation and has many siblings (e.g. reduceEntries, reduceKeys, reduceValues, etc ...). But, we will only focus on the reduce method itself that accepts parallelismThreshold, transformer (i.e. BiFunction) and reducer (i.e. BiFunction).

The transformer parameter is the one that prepares what the reducer will work on. Like for example if the wanted to work on the values of the map from snippet 1. The transformer would be something like the following:

Snippet 3 - Transformer logic
BiFunction<String, Integer, Integer> transformer = (key, value) -> value;

Knowing what the transformer will give us. We can write a reducer logic what will add all the values returned by the transformer logic like the following:

Snippet 4 - Reducer logic
BiFunction<Integer, Integer, Integer> reducer = (aggr, value) -> aggr + value;

From the snippet above, the aggr parameter is a variable the accumulates the value argument.

Having the transformer and the reducer ready we can invoke the reduce method like the following (see the complete code at the bottom):

Snippet 5 - Invoking the reduce method
System.out.println("Total: " + map.reduce(parallelismThreshold, transformer, reducer));

The Complete Code

package xyz.ronella.concurrency;

import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.stream.IntStream;

public class HashMap {
    public static void main(String ... args) {
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

        //This will generate a stream with 1 to 9 as integer content.
        IntStream.range(1, 10).forEach(item -> map.put(String.valueOf(item), item));

        long parallelismThreshold = 9;

        BiConsumer<String, Integer> biConsumer = (key, value) -> {
            System.out.println("key: " + key + " value: " + value + " thread: " + Thread.currentThread().getName());
        };

        map.forEach(parallelismThreshold, biConsumer);

        BiFunction<String, Integer, Integer> biFunction = (key, value) -> {
            if (key.equals("9")) {
                return value; //Stop searching.
            }
            return null; //Continue searching.
        };

        System.out.println("Value: " + map.search(parallelismThreshold, biFunction));

        BiFunction<String, Integer, Integer> transformer = (key, value) -> value;

        BiFunction<Integer, Integer, Integer> reducer = (aggr, value) -> aggr + value;

        System.out.println("Total: " + map.reduce(parallelismThreshold, transformer, reducer));
    }
}

Range in Java

To create a range in java we can use the following from the java.util.stream package:

  • IntStream.range(int startInclusive, int endExclusive)
  • IntStream.rangeClosed(int startInclusive, int endInclusive)
  • LongStream.range(int startInclusive, int endExclusive)
  • LongStream.rangeClosed(int startInclusive, int endInclusive)

IntStream.range Method Example

IntStream.range(1, 10).forEach(item -> System.out.println(item));

Output

1
2
3
4
5
6
7
8
9

IntStream.rangeClosed Method Example

IntStream.rangeClosed(1, 10).forEach(item -> System.out.println(item));

Output

1
2
3
4
5
6
7
8
9
10

Using CyclicBarrier with Java

CyclicBarrier was designed to allow threads to wait for each other to a certain barrier and doing it again and again (i.e. the reason why it is called cyclic).

Creating a CyclicBarrier aware Task
  1. Create a task that will accepts and instance of CyclicBarrier like the following snippet (i.e. the complete code will be at the bottom).
    public Teller(CyclicBarrier barrier, String message) {
        _barrier = barrier;
        _message = message;
    }
  2. Within the task we must call the await method of the instance of the CyclicBarrier like the following snippet (i.e. the complete code will be at the bottom). The number of call to this method is the one being tracked by the instance of the CyclicBarrier to compare to the number of parties (i.e. normally the first or only argument of the constructor.) specified during it's initialization.
    @Override
    public String call() throws Exception {
        System.out.println("Processing: " + _message);
        _barrier.await();
        return _message;
    }
Using the CyclicBarrier aware Task
  1. Create an instance of CyclicBarrier class with the expected number of parties (i.e. normally this is the first or only argument of the constructor) before it unblocks itself for all the threads waiting then back to blocking. Optionally, we can also pass a second argument of type Runnable that will only be invoked when the barrier unblocks like the following snippet (i.e. the complete code will be at the bottom).
    CyclicBarrier barrier = new CyclicBarrier(2 /*The number of parties. */
    , () -> System.out.println("Barrier open.") /*The logic to call when the barrier unblocks. */
    );
  2. Submit a number of tasks that corresponds to the multiple of the number of parties (e.g. from step 1 it could be multiple of 2) from step 1 like the following snippet (i.e. the complete code will be at the bottom):
    futures.add(executor.submit(new Teller(barrier, "One")));
    futures.add(executor.submit(new Teller(barrier, "Two")));
    futures.add(executor.submit(new Teller(barrier, "Three")));
    futures.add(executor.submit(new Teller(barrier, "Four")));
    
    futures.forEach((Future future) -> {
        try {
            System.out.println(future.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    });
Observation

Upon running the complete code:

  • We must notice that the following message will be executed every multiple of 2 (i.e. observe the processing message):
    Barrier open.
  • The following message (i.e. can be in any order) will never be displayed before One and Two (i.e. can be in any order):
    Three
    Four
The Complete Code
package xyz.ronella.concurrency;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class Barrier {

    public static void main(String[] args) {

        class Teller implements Callable {

            private CyclicBarrier _barrier;
            private String _message;

            public Teller(CyclicBarrier barrier, String message) {
                _barrier = barrier;
                _message = message;
            }

            @Override
            public String call() throws Exception {
                System.out.println("Processing: " + _message);
                _barrier.await();
                return _message;
            }
        }

        ExecutorService executor = Executors.newFixedThreadPool(2);

        CyclicBarrier barrier = new CyclicBarrier(2 /*The number of parties. */
                , () -> System.out.println("Barrier open.") /*The logic to call when the barrier unblocks. */
        );

        List<Future> futures = new ArrayList<>();

        try {
            futures.add(executor.submit(new Teller(barrier, "One")));
            futures.add(executor.submit(new Teller(barrier, "Two")));
            futures.add(executor.submit(new Teller(barrier, "Three")));
            futures.add(executor.submit(new Teller(barrier, "Four")));

            futures.forEach((Future future) -> {
                try {
                    System.out.println(future.get());
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            });
        }
        finally {
            executor.shutdown();
        }
    }
}

Behavioral Design Patterns

Behavioral design patterns focus on how objects and classes interact and communicate with each other. They deal with the responsibilities of objects and the delegation of tasks among them. These patterns help you define clear and efficient ways for objects to collaborate while keeping your codebase flexible and maintainable.

Chain of Responsibility Pattern

The Chain of Responsibility Pattern passes a request along a chain of handlers. Each handler decides either to process the request or to pass it to the next handler in the chain.

Example

// Handler interface
abstract class Handler {
    protected Handler successor;

    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    public abstract void handleRequest(Request request);
}

// Request class
class Request {
    private String requestType;
    private String requestDescription;

    public Request(String requestType, String requestDescription) {
        this.requestType = requestType;
        this.requestDescription = requestDescription;
    }

    public String getRequestType() {
        return requestType;
    }

    public String getRequestDescription() {
        return requestDescription;
    }
}

// Concrete Handlers
class Clerk extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getRequestType().equalsIgnoreCase("Leave")) {
            System.out.println("Clerk: Approving leave request - " + request.getRequestDescription());
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }
}

class Supervisor extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getRequestType().equalsIgnoreCase("Purchase")) {
            System.out.println("Supervisor: Approving purchase request - " + request.getRequestDescription());
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }
}

class Manager extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getRequestType().equalsIgnoreCase("Raise Salary")) {
            System.out.println("Manager: Approving salary raise request - " + request.getRequestDescription());
        } else {
            System.out.println("Manager: Cannot handle this request.");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Handler clerk = new Clerk();
        Handler supervisor = new Supervisor();
        Handler manager = new Manager();

        clerk.setSuccessor(supervisor);
        supervisor.setSuccessor(manager);

        Request request1 = new Request("Leave", "2 days leave");
        Request request2 = new Request("Purchase", "Office supplies");
        Request request3 = new Request("Raise Salary", "Employee X");

        clerk.handleRequest(request1);
        clerk.handleRequest(request2);
        clerk.handleRequest(request3);
    }
}

Command Pattern

The Command Pattern encapsulates a request as an object, thereby allowing you to parameterize clients with queues, requests, and operations. It also enables undoable operations and transactional behavior.

Example

// Receiver
class Light {
    void turnOn() {
        System.out.println("Light is on");
    }

    void turnOff() {
        System.out.println("Light is off");
    }
}

// Command interface
interface Command {
    void execute();
}

// Concrete Commands
class TurnOnLightCommand implements Command {
    private Light light;

    public TurnOnLightCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOn();
    }
}

class TurnOffLightCommand implements Command {
    private Light light;

    public TurnOffLightCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.turnOff();
    }
}

// Invoker
class RemoteControl {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void pressButton() {
        command.execute();
    }
}

public class Main {
    public static void main(String[] args) {
        Light livingRoomLight = new Light();
        Command turnOnCommand = new TurnOnLightCommand(livingRoomLight);
        Command turnOffCommand = new TurnOffLightCommand(livingRoomLight);

        RemoteControl remoteControl = new RemoteControl();

        remoteControl.setCommand(turnOnCommand);
        remoteControl.pressButton();

        remoteControl.setCommand(turnOffCommand);
        remoteControl.pressButton();
    }
}

Interpreter Pattern

The Interpreter Pattern is used to define a grammar for interpreting a language and provides a way to evaluate expressions in the language.

Example

// Context
class Context {
    private String input;
    private int output;

    public Context(String input) {
        this.input = input;
    }

    public String getInput() {
        return input;
    }

    public void setOutput(int output) {
        this.output = output;
    }

    public int getOutput() {
        return output;
    }
}

// Abstract Expression
interface Expression {
    void interpret(Context context);
}

// Terminal Expression
class NumberExpression implements Expression {
    private int number;

    public NumberExpression(int number) {
        this.number = number;
    }

    @Override
    public void interpret(Context context) {
        context.setOutput(number);
    }
}

// Non-terminal Expression
class AddExpression implements Expression {
    private Expression left;
    private Expression right;

    public AddExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public void interpret(Context context) {
        left.interpret(context);
        int leftValue = context.getOutput();
        right.interpret(context);
        int rightValue = context.getOutput();
        context.setOutput(leftValue + rightValue);
    }
}

public class Main {
    public static void main(String[] args) {
        // Example: 1 + 2
        Context context = new Context("1 + 2");
        Expression expression = parseExpression(context.getInput());
        expression.interpret(context);
        System.out.println("Result: " + context.getOutput());
    }

    private static Expression parseExpression(String input) {
        String[] tokens = input.split(" ");
        Expression left = new NumberExpression(Integer.parseInt(tokens[0]));
        Expression right = new NumberExpression(Integer.parseInt(tokens[2]));
        return new AddExpression(left, right);
    }
}

Iterator Pattern

The Iterator Pattern provides a way to access elements of an aggregate object sequentially without exposing the underlying representation.

Example

import java.util.ArrayList;
import java.util.List;

// Iterator interface
interface Iterator<T> {
    boolean hasNext();
    T next();
}

// Aggregate interface
interface Container<T> {
    Iterator<T> createIterator();
}

// Concrete Iterator
class NameIterator implements Iterator<String> {
    private List<String> names;
    private int index;

    public NameIterator(List<String> names) {
        this.names = names;
        this.index = 0;
    }

    @Override
    public boolean hasNext() {
        return index < names.size();
    }

    @Override
    public String next() {
        if (hasNext()) {
            return names.get(index++);
        }
        return null;
    }
}

// Concrete Aggregate
class NameRepository implements Container<String> {
    private List<String> names;

    public NameRepository() {
        this.names = new ArrayList<>();
    }

    public void addName(String name) {
        names.add(name);
    }

    @Override
    public Iterator<String> createIterator() {
        return new NameIterator(names);
    }
}

public class Main {
    public static void main(String[] args) {
        NameRepository nameRepository = new NameRepository();
        nameRepository.addName("Alice");
        nameRepository.addName("Bob");
        nameRepository.addName("Charlie");

        Iterator<String> iterator = nameRepository.createIterator();
        while (iterator.hasNext()) {
            System.out.println("Name: " + iterator.next());
        }
    }
}

Mediator Pattern

The Mediator Pattern defines an object that centralizes communication between objects in a system. It promotes loose coupling by ensuring that objects don't communicate directly with each other.

Example

import java.util.ArrayList;
import java.util.List;

// Mediator interface
interface Mediator {
    void sendMessage(String message, Colleague colleague);
}

// Colleague interface
abstract class Colleague {
    protected Mediator mediator;

    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }

    public abstract void receive(String message);

    public abstract void send(String message);
}

// Concrete Mediator
class ChatRoom implements Mediator {
    private List<Colleague> colleagues = new ArrayList<>();

    @Override
    public void sendMessage(String message, Colleague colleague) {
        for (Colleague c : colleagues) {
            if (c != colleague) {
                c.receive(message);
            }
        }
    }

    public void addColleague(Colleague colleague) {
        colleagues.add(colleague);
    }
}

// Concrete Colleague
class User extends Colleague {
    private String name;

    public User(String name, Mediator mediator) {
        super(mediator);
        this.name = name;
    }

    @Override
    public void receive(String message) {
        System.out.println(name + " received: " + message);
    }

    @Override
    public void send(String message) {
        System.out.println(name + " sent: " + message);
        mediator.sendMessage(message, this);
    }
}

public class Main {
    public static void main(String[] args) {
        ChatRoom chatRoom = new ChatRoom();

        User user1 = new User("Alice", chatRoom);
        User user2 = new User("Bob", chatRoom);
        User user3 = new User("Charlie", chatRoom);

        chatRoom.addColleague(user1);
        chatRoom.addColleague(user2);
        chatRoom.addColleague(user3);

        user1.send("Hello, everyone!");
    }
}

Memento Pattern

The Memento Pattern provides a way to capture and externalize an object's internal state so that it can be restored to that state later.

Example

import java.util.ArrayList;
import java.util.List;

// Memento class
class EditorMemento {
    private final String content;

    public EditorMemento(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }
}

// Originator class
class TextEditor {
    private String content;

    public void write(String content) {
        this.content = content;
    }

    public EditorMemento save() {
        return new EditorMemento(content);
    }

    public void restore(EditorMemento memento) {
        content = memento.getContent();
    }

    public String getContent() {
        return content;
    }
}

// Caretaker class
class History {
    private List<EditorMemento> mementos = new ArrayList<>();

    public void push(EditorMemento memento) {
        mementos.add(memento);
    }

    public EditorMemento pop() {
        if (!mementos.isEmpty()) {
            int lastIndex = mementos.size() - 1;
            EditorMemento lastMemento = mementos.get(lastIndex);
            mementos.remove(lastIndex);
            return lastMemento;
        }
        return null;
    }
}

public class Main {
    public static void main(String[] args) {
        TextEditor textEditor = new TextEditor();
        History history = new History();

        textEditor.write("Hello, World!");
        history.push(textEditor.save());

        textEditor.write("This is a new text.");
        history.push(textEditor.save());

        textEditor.write("Memento Pattern example.");
        System.out.println("Current Content: " + textEditor.getContent());

        // Restore to previous state
        textEditor.restore(history.pop());
        System.out.println("Restored Content: " + textEditor.getContent());

        // Restore to previous state
        textEditor.restore(history.pop());
        System.out.println("Restored Content: " + textEditor.getContent());
    }
}

Null Object Pattern

The Null Object Pattern provides an object as a surrogate for the lack of an object of a given type. It helps eliminate null references in your code.

Example

abstract class Animal {
    protected String name;

    public abstract String getName();
}

// Concrete class representing a real animal
class RealAnimal extends Animal {
    public RealAnimal(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }
}

// Null object representing a "null" animal
class NullAnimal extends Animal {
    @Override
    public String getName() {
        return "No animal found";
    }
}

public class Main {
    public static void main(String[] args) {
        Animal realDog = new RealAnimal("Dog");
        Animal nullAnimal = new NullAnimal();

        System.out.println("Real Animal: " + realDog.getName());
        System.out.println("Null Animal: " + nullAnimal.getName());
    }
}

Observer Pattern

The Observer Pattern defines a one-to-many relationship between objects so that when one object changes state, all its dependents (observers) are notified and updated automatically. This pattern is widely used in event handling systems.

Example

import java.util.ArrayList;
import java.util.List;

// Subject (Observable)
class WeatherStation {
    private List<Observer> observers = new ArrayList<>();
    private int temperature;

    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    public void setTemperature(int temperature) {
        this.temperature = temperature;
        notifyObservers();
    }

    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature);
        }
    }
}

// Observer
interface Observer {
    void update(int temperature);
}

// Concrete Observer
class WeatherDisplay implements Observer {
    @Override
    public void update(int temperature) {
        System.out.println("Temperature is now " + temperature + " degrees Celsius.");
    }
}

public class Main {
    public static void main(String[] args) {
        WeatherStation weatherStation = new WeatherStation();
        WeatherDisplay display1 = new WeatherDisplay();
        WeatherDisplay display2 = new WeatherDisplay();

        weatherStation.addObserver(display1);
        weatherStation.addObserver(display2);

        weatherStation.setTemperature(25);
    }
}

State Pattern

The State Pattern allows an object to alter its behavior when its internal state changes. It involves defining a set of state objects and switching between them as needed.

Example

// State interface
interface State {
    void handleRequest(Context context);
}

// Concrete States
class StateIdle implements State {
    @Override
    public void handleRequest(Context context) {
        System.out.println("Context is in the idle state.");
        context.setState(new StateActive());
    }
}

class StateActive implements State {
    @Override
    public void handleRequest(Context context) {
        System.out.println("Context is in the active state.");
        context.setState(new StateIdle());
    }
}

// Context
class Context {
    private State currentState;

    public Context() {
        currentState = new StateIdle();
    }

    public void setState(State state) {
        currentState = state;
    }

    public void request() {
        currentState.handleRequest(this);
    }
}

public class Main {
    public static void main(String[] args) {
        Context context = new Context();

        context.request(); // Transition to active state
        context.request(); // Transition to idle state
    }
}

Strategy Pattern

The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It allows the client to choose the appropriate algorithm at runtime without altering the code that uses it.

Example

// Strategy interface
interface PaymentStrategy {
    void pay(int amount);
}

// Concrete Strategies
class CreditCardPayment implements PaymentStrategy {
    private String cardNumber;

    public CreditCardPayment(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    @Override
    public void pay(int amount) {
        System.out.println("Paid $" + amount + " with credit card " + cardNumber);
    }
}

class PayPalPayment implements PaymentStrategy {
    private String email;

    public PayPalPayment(String email) {
        this.email = email;
    }

    @Override
    public void pay(int amount) {
        System.out.println("Paid $" + amount + " with PayPal account " + email);
    }
}

// Context
class ShoppingCart {
    private PaymentStrategy paymentStrategy;

    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void checkout(int totalAmount) {
        paymentStrategy.pay(totalAmount);
    }
}

public class Main {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        cart.setPaymentStrategy(new CreditCardPayment("1234-5678-9876-5432"));
        cart.checkout(100);

        cart.setPaymentStrategy(new PayPalPayment("example@example.com"));
        cart.checkout(50);
    }
}

Template Method Pattern

The Template Method Pattern defines the skeleton of an algorithm in the superclass but lets subclasses override specific steps of the algorithm without changing its structure.

Example

// Abstract class defining the template method
abstract class PaymentProcessor {
    public void processPayment() {
        authenticate();
        validatePaymentInfo();
        performPayment();
        sendConfirmationEmail();
    }

    abstract void authenticate();

    abstract void validatePaymentInfo();

    abstract void performPayment();

    void sendConfirmationEmail() {
        System.out.println("Sending payment confirmation email.");
    }
}

// Concrete subclass
class CreditCardPaymentProcessor extends PaymentProcessor {
    @Override
    void authenticate() {
        System.out.println("Authenticating credit card payment...");
    }

    @Override
    void validatePaymentInfo() {
        System.out.println("Validating credit card payment information...");
    }

    @Override
    void performPayment() {
        System.out.println("Processing credit card payment...");
    }
}

public class Main {
    public static void main(String[] args) {
        PaymentProcessor paymentProcessor = new CreditCardPaymentProcessor();
        paymentProcessor.processPayment();
    }
}

Visitor Pattern

The Visitor Pattern represents an operation to be performed on elements of an object structure. It lets you define a new operation without changing the classes of the elements on which it operates.

Example

// Visitor interface
interface Visitor {
    void visit(Book book);
    void visit(Video video);
}

// Visitable interface
interface Visitable {
    void accept(Visitor visitor);
}

// Concrete Visitable classes
class Book implements Visitable {
    private String title;

    public Book(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class Video implements Visitable {
    private String title;

    public Video(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// Concrete Visitor
class DiscountVisitor implements Visitor {
    @Override
    public void visit(Book book) {
        System.out.println("Applying a 10% discount to the book: " + book.getTitle());
    }

    @Override
    public void visit(Video video) {
        System.out.println("Applying a 15% discount to the video: " + video.getTitle());
    }
}

public class Main {
    public static void main(String[] args) {
        Visitable[] items = {new Book("Design Patterns"), new Video("Java Basics")};
        Visitor discountVisitor = new DiscountVisitor();

        for (Visitable item : items) {
            item.accept(discountVisitor);
        }
    }
}
« Older posts Newer posts »