Skip to content

Repository contains small Java projects and examples as solutions for common problems, for instance design patterns, concurrent problems etc

Notifications You must be signed in to change notification settings

CR4YU/JavaExamples

Repository files navigation

Java Examples

Java examples and solutions of common problems.

ReaderWriterProblem

Implementation of common computing problem in concurrency.
Used ReadWriteLock - two related locks for writing and reading.

Writer thread:

public void run() {
	while(true) {
		lock.writeLock().lock();
		try {
			int newValue = generateValue();
			sharedObj.write(newValue);
			logWriteMade(newValue);
		} finally {
			lock.writeLock().unlock();
		}
		waitRandomTime();
	}
}

At first writer thread locks a write lock, then generates a value (int in this example) and then assigns it to shared object. At the end thread releases the lock and waits some time before next while loop iteration.

Reader thread:

public void run() {
	while(true) {
		lock.readLock().lock();
		try {
			int newValue = sharedObj.read();
			logReadMade(newValue);
		} finally {
			lock.readLock().unlock();
		}
		waitRandomTime();
	}
}

Reader thread is very similar. Main loop takes following steps: acquires read lock, reads value from shared object and finally releases the lock.

ProducerConsumerProblemSimple

Another problem in concurrency.
Threads are synchronised by wait() and notifyAll() functions - that's why I called this solution simple.

Producer thread:

public void run() {
	while(true) {
		Object newOBj = requestNewObject();
		synchronized (objList) {
			waitUntilProductionPossible();
			objList.add(newOBj);
			objList.notifyAll();
			logNewObjectProduced(newOBj);
		}
	}
}

private void waitUntilProductionPossible() {
	while(objList.size() == LIST_CAPACITY) {
		try {
			objList.wait();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

Producer creates new object and adds to shared queue.

Consumer thread works similarly:

public void run() {
	while(true) {
		Object obj = null;
		synchronized (objList) {
			waitUntilConsumptionPossible();
			obj = objList.remove(0);
			objList.notifyAll();
			logConsumptionMade(obj);
		}
		processObject(obj);
	}
}
private void waitUntilConsumptionPossible() {
	while(objList.isEmpty()) {
		try {
			objList.wait();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

Singleton

Implementation of Singleton design pattern using double checked locking.
Works correctly in concurrent environment.

public class Singleton {

    private volatile static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if(instance == null) {
            synchronized (Singleton.class) {
                if(instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Strategy

Implementation of Strategy design pattern.

Strategy pattern defines a set of algorithms that can be swapped at run time to carry out a specific behaviour. This type of design pattern comes under behavior pattern.

Abstract class Unit has two fields:

AttackBehaviour attackBehaviour;
DefenceBehaviour defenceBehaviour;

And methods to perform specific behaviours:

public void performAttack() {
	attackBehaviour.attack();
}

public void performDefence() {
	defenceBehaviour.defend();
}

AttackBehaviour and DefenceBehaviour are interfaces:

public interface AttackBehaviour {
	void attack();
}
public interface DefenceBehaviour {
	void defend();
}

Implementation of AttackBehaviour example:

public class AttackWithSword implements AttackBehaviour {
	public void attack() {
		System.out.println("I will use my sword to cut my enemies.");
	}
}

Now we extend the Unit class and assigning specific behaviours.

public class Warrior extends Unit {
	public Warrior() {
		attackBehaviour = new AttackWithSword();
		defenceBehaviour = new DefendWithShield();
	}
}

Testing behaviours of Warrior:

public class StrategyDemo {
	public static void main(String[] args) {
		Unit warrior = new Warrior();
		warrior.performAttack();
		warrior.performDefence();
}

Results:

>I will use my swords to cut my enemies.
>Your attack has no power against my big steel shield.

We can easily assign any other behaviour to warrior:

warrior.setAttackBehaviour(new AttackWithBow());

Observer

Implementation of Observer design pattern.

Observer pattern is used when there is one-to-many relationship between objects such as if one object is modified, its depenedent objects are to be notified automatically. Observer pattern is one of behavioral patterns.

At first step we create Subject and Observer interfaces:

public interface Subject {
	void registerObserver(Observer o);
	void removeObserver(Observer o);
	void notifyObservers();
}
public interface Observer {
	void update(WeatherConditions weatherConditions);
}

Our observed subject is WeatherData.

public class WeatherData implements Subject {

	private List<Observer> observers;
	private WeatherConditions weatherConditions;

	public WeatherData() {
		observers = new ArrayList<>();
		weatherConditions = new WeatherConditions();
	}
	
	public void registerObserver(Observer o) {
		observers.add(o);
	}
	
	public void removeObserver(Observer o) {
		observers.remove(o);
	}
	
	public void notifyObservers() {
		observers.forEach(o -> o.update(weatherConditions));
	}
	
	public void measurementsChanged() {
		notifyObservers();
	}
	
	public void setMeasurements(float temperature, float humidity, float pressure) {
		weatherConditions.setTemperature(temperature);
		weatherConditions.setHumidity(humidity);
		weatherConditions.setPressure(pressure);

		measurementsChanged();
	}
}

Observed subject has list of its observers and methods to manage them (register/remove). Crucial method is notifyObservers() to tell all observers "Hey everybody, my state has changed so I give you some new data". Every observer uses this data his own way. For example:

public class CurrentWeatherConditionsDisplay implements Observer, DisplayWeather {
	
	private float temperature;
	private float humidity;
	private Subject weatherData;
	
	public CurrentWeatherConditionsDisplay(Subject weatherData) {
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}
	
	public void update(WeatherConditions weatherConditions) {
		this.temperature = weatherConditions.getTemperature();
		this.humidity = weatherConditions.getHumidity();
		display();
	}
	
	public void display() {
		System.out.println("Current conditions: " + temperature 
			+ "F degrees and " + humidity + "% humidity");
	}
}

As you can see this observer takes two values from WeatherConditions object and displays them his way.
Other observer takes only temperature value and displays statistics:

public class StatisticsDisplay implements Observer, DisplayWeather {

	private float maxTemp = 0.0f;
	private float minTemp = 200;
	private float tempSum = 0.0f;
	private int numReadings = 0;
	private WeatherData weatherData;

	public StatisticsDisplay(WeatherData weatherData) {
		this.weatherData = weatherData;
		weatherData.registerObserver(this);
	}

	public void update(WeatherConditions weatherConditions) {
		float temp = weatherConditions.getTemperature();
		tempSum += temp;
		numReadings++;

		if (temp > maxTemp) {
			maxTemp = temp;
		}
 
		if (temp < minTemp) {
			minTemp = temp;
		}

		display();
	}

	public void display() {
		System.out.println("Avg | Max | Min temperature = " +
				avg() + "|" + maxTemp + "|" + minTemp);
	}

	private float avg() {
		return tempSum / numReadings;
	}
}

Now if we would like to create new observer all we have to do is implement Observer interface, register as observer to observed subject and create method update() which is executed every time the subject changes his state.

Let's run a simple demo:

	public static void main(String[] args) {
		WeatherData weatherData = new WeatherData();
	
		new CurrentWeatherConditionsDisplay(weatherData);
		new StatisticsDisplay(weatherData);
		new ForecastDisplay(weatherData);
		new HeatIndexDisplay(weatherData);

		weatherData.setMeasurements(80, 65, 30.4f);
		weatherData.setMeasurements(82, 70, 29.2f);
		weatherData.setMeasurements(78, 90, 29.2f);
	}

Result:

>Current conditions: 80.0F degrees and 65.0% humidity
>Avg | Max | Min temperature = 80.0|80.0|80.0
>Forecast: More pressure, better weather
>Heat index: 82.95535
>Current conditions: 82.0F degrees and 70.0% humidity
>Avg | Max | Min temperature = 81.0|82.0|80.0
>Forecast: Less pressure, rainy weather
>Heat index: 86.90124
>Current conditions: 78.0F degrees and 90.0% humidity
>Avg | Max | Min temperature = 80.0|82.0|78.0
>Forecast: Same pressure as before
>Heat index: 83.64967

Decorator

Implementation of Decorator design pattern.

This pattern allows to add new functionality to an existing object without editing its structure. Decorator comes under structural pattern. It acts like wrapper to existing class.

At first we create supertype for both decorators and decorated classes.

public abstract class Beverage {

	String description = "Unknown Beverage";
  
	public String description() {
		return description;
	}
 
	public abstract double cost();
}

Now we create class that will be decorated. Every coffee type has its own price.

public class Cappuccino extends Beverage {

	public Cappuccino() {
		description = "Cappuccino Coffee";
	}

	@Override
	public double cost() {
		return 0.89;
	}
}

Now Decorator - abstract class extending Beverage.

public abstract class Decorator extends Beverage {
	public abstract String description();
}

And finally we create specific decorators.

public class Milk extends Decorator {

	Beverage beverage;

	public Milk(Beverage beverage) {
		this.beverage = beverage;
	}

	@Override
	public String description() {
		return beverage.description() + ", Milk";
	}

	@Override
	public double cost() {
		return 0.10 + beverage.cost();
	}
}

Every decorator contains reference to decorated object.

Let's test cafe:

public class CoffeeDemo {
 
	public static void main(String args[]) {
		Beverage beverage = new Espresso();
		System.out.println(beverage.description() + " $" + beverage.cost());
 
		Beverage beverage2 = new DarkRoast();
		beverage2 = new Mocha(beverage2);
		beverage2 = new Mocha(beverage2);
		beverage2 = new Whip(beverage2);
		System.out.println(beverage2.description() + " $" + beverage2.cost());
 
		Beverage beverage3 = new Cappuccino();
		beverage3 = new Soy(beverage3);
		beverage3 = new Mocha(beverage3);
		beverage3 = new Whip(beverage3);
		System.out.println(beverage3.description() + " $" + beverage3.cost());
	}
}

Take a look at needed steps to create and decorate coffees. At first we create specific type of coffee e.g. Cappuccino. Then we wrap the coffee into decorator:

beverage3 = new Mocha(beverage3);

Now if we call cost() method on that coffee it will return cost of coffee plus cost of decorator (mocha).

Result of running demo:

>Espresso $1.99
>Dark Roast Coffee, Mocha, Mocha, Whip $1.49
>Cappuccino Coffee, Soy, Mocha, Whip $1.34

Factory method

Implementation of Factory Method design pattern.

This pattern is one of the most used design patterns in Java. Factory pattern comes under creational pattern - it's one of the best ways to create an objects.

In Factory pattern, we keep object creation logic from client and refer to created object using a common interface.

First step is to create two abstract classes.

public abstract class Pizza {

	String name;
	String dough;
	String sauce;
	ArrayList<String> toppings = new ArrayList<>();
 
	void prepare() {
		System.out.println("Preparing " + name);
	}
  
	void bake() {
		System.out.println("Bake for 25 minutes at 350");
	}
 
	void cut() {
		System.out.println("Cutting the pizza into diagonal slices");
	}
  
	void box() {
		System.out.println("Place pizza in official PizzaStore box");
	}
 
}

We want to make many kinds of pizza. Of course every pizza has it's name, must be cut or boxed. Now we want abstract class that represents pizza store.

public abstract class PizzaStore {
 
	abstract Pizza createPizza(String item);
 
	public Pizza orderPizza(String type) {
		Pizza pizza = createPizza(type);
		System.out.println("--- Making a " + pizza.getName() + " ---");
		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}
}

Every pizza store has its own pizza kinds. We will implement two different pizza stores: ChicacoPizzaStore and NYPizzaStore. Every store has to implement its own version of factory method.

public class ChicagoPizzaStore extends PizzaStore {

	Pizza createPizza(String item) {
		switch (item) {
			case "cheese":
				return new ChicagoStyleCheesePizza();
			case "veggie":
				return new ChicagoStyleVeggiePizza();
			case "clam":
				return new ChicagoStyleClamPizza();
			case "pepperoni":
				return new ChicagoStylePepperoniPizza();
		}
		return null;
	}
}

createPizza() is our factory method. It returns reference to the particular pizza type according to the given string parameter. Below there is example of ChicagoStyleCheesePizza.

public class ChicagoStyleCheesePizza extends Pizza {

	public ChicagoStyleCheesePizza() { 
		name = "Chicago Style Deep Dish Cheese Pizza";
		dough = "Extra Thick Crust Dough";
		sauce = "Plum Tomato Sauce";
 
		toppings.add("Shredded Mozzarella Cheese");
	}
 
	void cut() {
		System.out.println("Cutting the pizza into square slices");
	}
}

Testing pizza stores:

public class PizzaTestDrive {
 
	public static void main(String[] args) {
		PizzaStore nyStore = new NYPizzaStore();
		PizzaStore chicagoStore = new ChicagoPizzaStore();
 
		Pizza pizza = nyStore.orderPizza("cheese");
		System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
		pizza = chicagoStore.orderPizza("cheese");
		System.out.println("Joel ordered a " + pizza.getName() + "\n");

	}
}

Results:

>--- Making a NY Style Sauce and Cheese Pizza ---
>Preparing NY Style Sauce and Cheese Pizza
>Tossing dough...
>Adding sauce...
>Adding toppings: 
>Grated Reggiano Cheese 
>Bake for 25 minutes at 350
>Cutting the pizza into diagonal slices
>Place pizza in official PizzaStore box
>Ethan ordered a NY Style Sauce and Cheese Pizza

>--- Making a Chicago Style Deep Dish Cheese Pizza ---
>Preparing Chicago Style Deep Dish Cheese Pizza
>Tossing dough...
>Adding sauce...
>Adding toppings: 
>Shredded Mozzarella Cheese 
>Bake for 25 minutes at 350
>Cutting the pizza into square slices
>Place pizza in official PizzaStore box
>Joel ordered a Chicago Style Deep Dish Cheese Pizza

AbstractFactory

Implementation of Abstract factory design pattern.

Type of creational design pattern. It uses super-factory to create other factories so it's usually called factory of factories.

In Abstract Factory pattern an interface is responsible for creating a factory of related objects without explicitly specifying their classes. Each generated factory can give the objects as per the Factory pattern.

Command

Implementation of Command design pattern.

Command pattern falls under behavioral pattern category. It encapsulates requests as objects which allows to parameterize various objects with various requests and handling operations which can be undone.

First step is creating interface Command with one method.

public interface Command {
	void execute();
}

Every class that implements this interface is specific command on specific object. For example command to turn the light on:

public class LightOnCommand implements Command {

	Light light;

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

	public void execute() {
		light.on();
	}
}

LightOnCommand knows exactly hot to turn on the light.

Now to activate the light we have to do following:

Light livingRoomLight = new Light("Living Room");
LightOnCommand livingRoomLightOn = new LightOnCommand(livingRoomLight);
livingRoomLightOn.execute();

Adapter

Implementation of Adapter design pattern.

This type of design pattern comes under structural pattern. Adapter converts interface of some class to other interface that is compatible with client requirements. It allows two classes work together which could not cooperate before due to interface incompatibility.

As example let's take ducks.

public interface Duck {
	void quack();
	void fly();
}
public interface Turkey {
	void gobble();
	void fly();
}

Let's try to adapt Turkey to act like Duck. We need adapter fo this:

public class TurkeyAdapter implements Duck {

	Turkey turkey;
 
	public TurkeyAdapter(Turkey turkey) {
		this.turkey = turkey;
	}
    
	public void quack() {
		turkey.gobble();
	}
  
	public void fly() {
		for(int i = 0; i < 5; i++) {
			turkey.fly();
		}
	}
}

Adapter implements target interface and contains reference to adapted object.
Test:

public static void main(String[] args) {
	MallardDuck duck = new MallardDuck();

	WildTurkey turkey = new WildTurkey();
	Duck turkeyAdapter = new TurkeyAdapter(turkey);
  
	System.out.println("The Turkey says...");
	turkey.gobble();
	turkey.fly();

	System.out.println("\nThe Duck says...");
	testDuck(duck);
 
	System.out.println("\nThe TurkeyAdapter says...");
	testDuck(turkeyAdapter);
}
 
static void testDuck(Duck duck) {
	duck.quack();
	duck.fly();
}

Results:

>The Turkey says...
>Gobble gobble
>I'm flying a short distance

>The Duck says...
>Quack
>I'm flying

>The TurkeyAdapter says...
>Gobble gobble
>I'm flying a short distance
>I'm flying a short distance
>I'm flying a short distance
>I'm flying a short distance
>I'm flying a short distance

Facade

Implemenation of Facade deisgn pattern.

Facade pattern keeps the complexities of the system from the client and provides an interface which the client can access the system. Facade comes under structural pattern.

This pattern involves a single class which provides simplified API required by client which makes usage of system much easier.

Our facade class:

public class HomeTheaterFacade {

	Amplifier amp;
	Tuner tuner;
	DvdPlayer dvd;
	CdPlayer cd;
	Projector projector;
	TheaterLights lights;
	Screen screen;
	PopcornPopper popper;
 
	public HomeTheaterFacade(
			Amplifier amp,
			Tuner tuner,
			DvdPlayer dvd,
			CdPlayer cd,
			Projector projector,
			Screen screen,
			TheaterLights lights,
			PopcornPopper popper) {
 
		this.amp = amp;
		this.tuner = tuner;
		this.dvd = dvd;
		this.cd = cd;
		this.projector = projector;
		this.screen = screen;
		this.lights = lights;
		this.popper = popper;
	}
 
	public void watchMovie(String movie) {
		System.out.println("Get ready to watch a movie...");
		popper.on();
		popper.pop();
		lights.dim(10);
		screen.down();
		projector.on();
		projector.wideScreenMode();
		amp.on();
		amp.setDvd(dvd);
		amp.setSurroundSound();
		amp.setVolume(5);
		dvd.on();
		dvd.play(movie);
	}

	public void endMovie() {
		System.out.println("Shutting movie theater down...");
		popper.off();
		lights.on();
		screen.up();
		projector.off();
		amp.off();
		dvd.stop();
		dvd.eject();
		dvd.off();
	}

	public void listenToCd(String cdTitle) {
		System.out.println("Get ready for an audiopile experence...");
		lights.on();
		amp.on();
		amp.setVolume(5);
		amp.setCd(cd);
		amp.setStereoSound();
		cd.on();
		cd.play(cdTitle);
	}

	public void endCd() {
		System.out.println("Shutting down CD...");
		amp.off();
		amp.setCd(cd);
		cd.eject();
		cd.off();
	}

	public void listenToRadio(double frequency) {
		System.out.println("Tuning in the airwaves...");
		tuner.on();
		tuner.setFrequency(frequency);
		amp.on();
		amp.setVolume(5);
		amp.setTuner(tuner);
	}

	public void endRadio() {
		System.out.println("Shutting down the tuner...");
		tuner.off();
		amp.off();
	}
}

As you can see our facade can control each device end merge many actions to one method.

Composite

Implementation of Composite design pattern.

SpringBootAppContacts

Simple Spring Boot application to manage contact list. All contacts are stored in H2 Database. For presentation layer I used thymeleaf - modern server-side Java template engine. Alt text

SpringBootRestAppMusicLibrary

Simple REST service implemented with Spring Boot.

Application allows to manage music library using HTTP requests. There are two methods:

  • GET - get all artists with their albums
  • POST - add new artist with his albums

As database used H2. For ORM used Hibernate.

After running Spring Boot application we can test our REST method: Alt text

About

Repository contains small Java projects and examples as solutions for common problems, for instance design patterns, concurrent problems etc

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published