Fazrin/Design Patterns in Java

Created Sat, 22 Oct 2022 14:04:38 +0530 Modified Fri, 22 Dec 2023 18:51:58 +0000
3986 Words

Design patterns in java are best practices which are used to resolve some known issues. Design patterns can be divided into 3 different types. Here we have listed down some of the widely used design patterns in Java.

What is Singleton Design Pattern?

Implementation of Singleton Design Pattern

Now you know what is the Singleton Design Pattern. Next step would be to implement it. There are different ways you can implement Singleton. Before you start implementation, you need to understand what are the different ways to create an Object.

Different Ways to create an Object

To make a singleton class you have to disable all these ways to create an object. One way to achieve this is to make the constructor of a given class private. But when you make the constructor private there is no way to create even a single object of the class. So first make a constructor private and then prepare a way to create an object(Single) of that class.

Implementation One

Here we will create a private Constructor and also a static method to create an object of the same class.

class JBT {

	/*
	 * This variable will be used to hold reference of JBT class.
	 */
	private static JBT instance = null;

	/*
	 * As private constructor is used so can not create object of this class
	 * directly. Except by using static method of same class.
	 */
	private JBT() {

	}

	/*
	 * This method will be used to get instance of JBT class. This method will
	 * check if there is aready an object of class create or not if not then it
	 * will create an Obect of JBT class and return the same else it will return
	 * the existing Object.
	 */
	static JBT createInstance() {
		if (instance == null){
                     instance = new JBT();
                     return instance;
              }
		else
			return instance;
	}

	int i;
}
Problem

What will happen if 2 different thread enters the createInstance method at the same time when the instance is null. In that case, threads will create 2 different objects of JBT. Which is against the Singleton pattern. In the next approach, this problem can be solved.

Implementation Two

In this approach, we will make createInstance method synchronized so only one thread is allowed in that class and only one object will be created instead of Two.

class JBT {

	/*
	 * This variable will be used to hold reference of JBT class.
	 */
	private static JBT instance = null;

	/*
	 * As private constructor is used so can not create object of this class
	 * directly. Except by using static method of same class.
	 */
	private JBT() {

	}

	/*
	 * This method will be used to get instance of JBT class. This method will
	 * check if there is aready an object of class create or not if not then it
	 * will create an Obect of JBT class and return the same else it will return
	 * the existing Object.
	 * 
	 * Now method is marked as synchronized hence only one threa will be allowed
	 * to enter in this method hence only one object will be created.
	 */
	static synchronized JBT createInstance() {
		if (instance == null){
instance = new JBT();
return instance;
}
		else
			return instance;
	}

	int i;
}
Problem

The moment we use synchronized keyword it will create a problem for our multi-threaded application in terms of performance. So on one side, we are resolving the problem on another side we are creating one more problem. In the next approach, we will solve both this problem.

Implementation Three
class JBT {

	/*
	 * This variable will be used to hold reference of JBT class.
	 * 
	 * Here we are creating the instance of JBT class and assigning the
	 * reference of that object to instance.
	 */
	private static JBT instance = new JBT();

	/*
	 * As private constructor is used so can not create object of this class
	 * directly. Except by using static method of same class.
	 */
	private JBT() {

	}

	/*
	 * This method will be used to get instance of JBT class. This method will
	 * check if there is already an object of class create or not if not then it
	 * will create an Object of JBT class and return the same else it will
	 * return the existing Object.
	 * 
	 * synchronized keyword is not required here.
	 */
	static JBT createInstance() {
		/*
		 *  As instance is already create and class loading time hence we can
		 *  directly return the same without creating any object.
		 */
		return instance;
	}

	int i;
}
Problem

Here we are creating the object of JBT when class gets loaded. The object gets created even when it is not required. The object should be created only when it is required(Lazy Loading). In the next approach, we will try to resolve this problem by using Double checking locking.

Implementation Four
package com.jbt;

public class SingletonExample {

}

class JBT {

	/*
	 * This variable will be used to hold reference of JBT class.
	 * 
	 * Here we are creating the instance of JBT class and assigning the
	 * reference of that object to instance.
	 */
	private static JBT instance = null;

	/*
	 * As private constructor is used so can not create object of this class
	 * directly. Except by using static method of same class.
	 */
	private JBT() {

	}

	/*
	 * This method will be used to get instance of JBT class. This method will
	 * check if there is already an object of class create or not if not then it
	 * will create an Object of JBT class and return the same else it will return
	 * the existing Object.
	 * 
	 * Now block is marked as synchronized instead of whole method. So synchronized
	 * part will be used only once and that is when object is null. 
	 */
	static JBT createInstance() {
		if (instance == null)
		{
			synchronized(JBT.class){
				if (instance == null){
                                   instance = new JBT();
                                   return instance;
                             }
			}
		}

			return instance;
	}

	int i;
}
Problem

All problem has been solved in this approach still synchronized keyword is used(Once) and that should be avoided.

Implementation Five (Initialization on Demand)
class JBT {

	/*
	 * As private constructor is used so can not create object of this class
	 * directly. Except by using static method of same class.
	 */
	private JBT() {

	}

	/*
	 * Here static inner class is used instead of Static variable. It means
	 * Object will be lazy initialized.
	 */
	private static class LazyInit {
		private static final JBT instance = new JBT();
	}

	/*
	 * Whenever object JBT is required this method will be invoked and it will
	 * return the instance of JBT.
	 */
	static JBT createInstance() {
		return LazyInit.instance;
	}

	int i;
}
Problem

What about Object creation using Clone and Serialization?? Next approach will handle that problem.

Implementation Six

To stop cloning of Object we will implement the Cloneable interface and throw CloneNotSupportedException. Also Serialized interface will be implemented and readObject will be used to return only one object at all time.

class JBT implements Cloneable, Serializable{

	@Override
	protected Object clone() throws CloneNotSupportedException {
		return new CloneNotSupportedException();
	}

	protected Object readResolve() {
		return createInstance();
		}

	/*
	 * As private constructor is used so can not create object of this class
	 * directly. Except by using static method of same class.
	 */
	 private  JBT() {

	}

	/*
	 * Here static inner class is used instead of Static variable. It means
	 * Object will be lazy initialized.
	 */
	private static class LazyInit {
		private static final JBT instance = new JBT();
	}

	/*
	 * Whenever object JBT is required this method will be invoked and it will
	 * return the instance of JBT.
	 */
	static JBT createInstance() {
		return LazyInit.instance;
	}

	int i;
}

So this is the final implementation for singleton class. One more approach is there(ENUM). That I will discuss later.

Back to top

Why should I care?

/**

  • This class contains the AppStore where we “create” iOS apps.
  • @author Fazrin

*/ public class AppStore { public void orderApp() {

   final App app = new App();

   app.develop();
   app.test();
   app.debug();
   app.deliver();

} }eval(ez_write_tag([[728,90],‘javabeginnerstutorial_com-medrectangle-3’,’ezslot_3’,110,‘0’,‘0’]));

/**

  • Describes the available app types for our store.
  • @author Fazrin

*/ public enum AppType { IOS, WATCH, TV }

App app; switch (type) { case IOS: app = new IOSApp(); break; case TV: app = new TVApp(); break; case WATCH: app = new WatchApp(); break; default: app = new IOSApp(); break; }

app.develop(); app.test(); app.debug(); app.deliver(); }eval(ez_write_tag([[336,280],‘javabeginnerstutorial_com-medrectangle-4’,’ezslot_4’,113,‘0’,‘0’]));

App app; switch (type) { case IOS: app = new IOSApp(); break; case TV: app = new TVApp(); break; case GLASS: app = new GlassApp(); break; default: app = new GlassApp(); break; }

app.develop(); app.test(); app.debug(); app.deliver(); }

/**

  • A simple Factory which encapsulates (hides) which concrete implementation to use for a given AppType.
  • @author Fazrin

*/ public class AppFactory { public App createApp(AppType type) { App app; switch (type) { case IOS: app = new IOSApp(); break; case TV: app = new TVApp(); break; case GLASS: app = new GlassApp(); break; default: app = new GlassApp(); break; } return app; } }

final App app = AppFactory.createApp(type);

app.develop(); app.test(); app.debug(); app.deliver(); }

/**
 * This method creates an app for our app store. Seems silly and broken by design to have the App develop itself but
 * for this example it fits the purpose.
 */
void developApp();

}

Now add a simple implementation of this interface which implements the contract method:

/**

  • @author Fazrin

*/ public class IOSApp implements App {

@Override
public void developApp() {
    System.out.println("Developing an iOS app");
}

}

public static void main(String... args) {
    final App app = new IOSApp();
    app.developApp();
}

}eval(ez_write_tag([[728,90],‘javabeginnerstutorial_com-medrectangle-3’,’ezslot_2’,110,‘0’,‘0’]));

/**
 * A required constructor to set the delegate for this app.
 *
 * @param delegate
 *            the delegate which should be decorated.
 */
public TVAppDecorator(App delegate) {
    this.delegate = delegate;
}

@Override
public void developApp() {
    this.delegate.developApp();
    System.out.println("Adding TV extension...");
}

}

/**

  • This implementation of the decorator adds a Watch extension to the provided app.
  • @author Fazrin

*/ public class WatchAppDecorator extends AppDecorator {

/**
 * A required constructor to set the delegate for this app.
 *
 * @param delegate
 *            the delegate which should be decorated.
 */
public WatchAppDecorator(App delegate) {
    this.delegate = delegate;
}

@Override
public void developApp() {
    this.delegate.developApp();
    System.out.println("Adding Watch extension...");
}

}eval(ez_write_tag([[336,280],‘javabeginnerstutorial_com-medrectangle-4’,’ezslot_3’,113,‘0’,‘0’]));

public static void main(String... args) {
    final App tvApp = new TVAppDecorator(new IOSApp());
    tvApp.developApp();

    System.out.println("------");

    final App watchApp = new WatchAppDecorator(new IOSApp());
    watchApp.developApp();
}

}

private final App delegate;

/**
 * A required constructor to set the delegate for this app.
 *
 * @param delegate
 *            the delegate which should be decorated.
 */
public SimpleDecorator(App delegate) {
    this.delegate = delegate;
}

@Override
public void developApp() {
    System.out.println("Preparing extra content...");
    this.delegate.developApp();
    System.out.println("Fine-tuning the app to be more perfect...");
}

}

public static void main(String... args) {
    final App perfectApp = new SimpleDecorator(new IOSApp());
    perfectApp.developApp();
}

}

Back to top

/**
 * @return the name of the employee
 */
String getName();

/**
 * @param e
 *            add this employee to the list of employees
 */
void add(Employee e);

/**
 * @param e
 *            remove this employee from the list of employees
 */
void remove(Employee e);

/**
 * @return the list of employees
 */
List<Employee> getEmployees();

/**
 * This method estimates the costs in ManDays for the given project. Managers delegate this request to their
 * employees, developers return an estimate.
 *
 * @param projectDescription
 * @return
 */
int estimateProject(String projectDescription);

}

public Manager(String name) {
    this.name = name;
}

@Override
public List<Employee> getEmployees() {
    return this.employees;
}

@Override
public void add(Employee e) {
    if (e != null) {
        this.employees.add(e);
    }
}

@Override
public void remove(Employee e) {
    if (e != null) {
        this.employees.remove(e);
    }
}

@Override
public int estimateProject(String projectDescription) {
    if (this.employees.isEmpty()) {
        return 0;
    }
    return Math.round(this.employees.stream().mapToInt(e -> {
        System.out.println(e);
        return e.estimateProject(projectDescription);
    }).sum() / this.employees.size());
}

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

}

/**

  • Simple implementation of a VP
  • @author Fazrin

*/ public class VP extends Manager {

public VP(String name) {
    super(name);
}

@Override
public String toString() {
    return "I am " + getName() + ", VP";
}

/**
 * VP doubles the estimated amount.
 */
@Override
public int estimateProject(String projectDescription) {
    System.out.println("I am " + getName() + ", the VP, and calling for an estimate...");
    final int projectEstimate = super.estimateProject(projectDescription);
    System.out.println("Original estimate: " + projectEstimate);
    return Math.toIntExact(Math.round(projectEstimate * 2));
}

}

/**

  • Simple implementation of a Senior Manager
  • @author Fazrin

*/ public class SeniorManager extends Manager {

public SeniorManager(String name) {
    super(name);
}

/**
 * Senior Managers add 10% to the estimate of the team.
 */
@Override
public int estimateProject(String projectDescription) {
    return Math.toIntExact(Math.round(super.estimateProject(projectDescription) * 1.1));
}

@Override
public String toString() {
    return "I am " + getName() + ", Senior Manager";
}

}

/**

  • Simple implementation of a Team Leader
  • @author Fazrin

*/ public class TeamLeader extends Manager {

public TeamLeader(String name) {
    super(name);
}

@Override
public String toString() {
    return "I am " + getName() + ", Team Leader";
}

}eval(ez_write_tag([[728,90],‘javabeginnerstutorial_com-medrectangle-3’,’ezslot_2’,110,‘0’,‘0’]));

String name;

public Developer(String name) {
    this.name = name;
}

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

@Override
public void add(Employee e) {
}

@Override
public void remove(Employee e) {
}

@Override
public List<Employee> getEmployees() {
    return null;
}

@Override
public int estimateProject(String projectDescription) {
    return new Random().nextInt(24);
}

@Override
public String toString() {
    return "I am " + getName() + ", Developer";
}

}

Now let’s implement a structure and see how the pattern works:

/**

  • This is the main entry point of the application.
  • @author Fazrin

*/ public class Composite { public static void main(String… args) { final Developer d1 = new Developer(“Jack”); final Developer d2 = new Developer(“Jill”); final Developer d3 = new Developer(“Brian”); final Developer d4 = new Developer(“Bob”);

    final Manager m1 = new TeamLeader("Marc");
    final Manager m2 = new TeamLeader("Christian");
    final Manager m3 = new TeamLeader("Phil");
    m1.add(d3);
    m1.add(d2);
    m2.add(d1);
    m3.add(d4);

    final Manager m4 = new SeniorManager("Harald");
    final Manager m5 = new SeniorManager("Klaus");

    m4.add(m3);
    m4.add(m2);
    m5.add(m1);

    final VP vp = new VP("Joseph");
    vp.add(m4);
    vp.add(m5);

    System.out.println("Our estimate is: " + vp.estimateProject("New exotic feature"));
}

}

This is the DTO which is returned

/**

  • The DTO to transfer the results of some data gathering to the callers.
  • @author Fazrin

/ public abstract class FirstSystemDTO { /* * @return the first name value – can be null */ public abstract String getFirstName();

/**
 * @return the middle name value -- can be null
 */
public abstract String getMiddleName();

/**
 * @return the last name value -- can be null
 */
public abstract String getLastName();

}

And here is the information consumer:

/**

  • This is the second system’s contract which we want to call after the data is gathered from the first system.
  • @author Fazrin

/ public interface DataConsumer { /* * Triggers some work based on the parameter in the system. * * @param name * the work is based on this parameter * * @throws NullPointerException * if name is null */ void triggerWorker(String name); }

DataProvider dataProviderInstance = new DataProviderInstance();
DataConsumer dataConsumerInstance = new DataConsumerInstance();

/**
 * This method is the adapter which gets the data from the provider and triggers the consumer.
 */
public void doOurWork() {
    final FirstSystemDTO data = this.dataProviderInstance.getData();
    this.dataConsumerInstance.triggerWorker(dataToString(data));
}

/*
 * This method converts the gathered information into a null-safe String
 */
private String dataToString(FirstSystemDTO data) {
    if (data == null) {
        return "";
    }
    return join(" ", data.getFirstName(), data.getMiddleName(), data.getLastName());
}

}

private AppType type;

public abstract void develop();

public void test() {
}

public void debug() {
}

public void deliver() {
}

@Override
protected Object clone() throws CloneNotSupportedException {
    return super.clone();
}

public AppType getType() {
    return this.type;
}

protected void setType(AppType type) {
    this.type = type;
}

}

After the abstract class is ready, let’s create some implementations.

/**

  • @author Fazrin

*/ public class IOSApp extends App {

public IOSApp() {
    System.out.println("Creating an iOS App");
    setType(AppType.IOS);
}

@Override
public void develop() {
    System.out.println("Developing an iOS App");
}

}

/**

  • @author Fazrin

*/ public class WatchApp extends App {

public WatchApp() {
    System.out.println("Creating a watch app");
    setType(AppType.WATCH);
}

@Override
public void develop() {
    System.out.println("Developing a Watch App");
}

}

/**
 * Loads all available types of the application and puts them into the cache.
 */
public void load() {
    System.out.println("Loading App of type " + AppType.IOS.name());
    this.appCache.put(AppType.IOS, new IOSApp());
    System.out.println("Loading App of type " + AppType.WATCH.name());
    this.appCache.put(AppType.WATCH, new WatchApp());
}

/**
 * Gets the app from the cache based on its type
 */
public App get(AppType type) {
    System.out.println("Getting App of type " + type.name());
    final App app = this.appCache.get(type);
    if (app != null) {
        try {
            return (App) app.clone();
        } catch (final CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
    return null;
}

}eval(ez_write_tag([[728,90],‘javabeginnerstutorial_com-medrectangle-3’,’ezslot_4’,110,‘0’,‘0’]));

private App load(AppType type) {
    System.out.println("Loading App of type " + type.name());
    switch (type) {
    case IOS:
        final App app = new IOSApp();
        this.appCache.put(type, app);
        return app;
    case WATCH:
        final App watchApp = new WatchApp();
        this.appCache.put(type, watchApp);
        return watchApp;
    default:
        return null;
    }

}

public App get(AppType type) {
    System.out.println("Getting App of type " + type.name());
    App app = this.appCache.get(type);
    // the type is not in the cache, let's load it
    if (app == null) {
        app = load(type);
    }
    try {
        // we have to cast the result of clone() to App because the default return type is Object
        return (App) app.clone();
    } catch (final CloneNotSupportedException e) {
        // a nasty exception, let's just ignore it
        e.printStackTrace();
    }
    // if everything fails, return simply null
    return null;
}

}

public AppFactory() {
    this.appCache = new BasicAppCache();
    this.appCache.load();
}

public App createApp(AppType type) {
    return this.appCache.get(type);
}

}eval(ez_write_tag([[336,280],‘javabeginnerstutorial_com-medrectangle-4’,’ezslot_3’,113,‘0’,‘0’]));

public void orderApp(AppType type) {
    final App app = new AppFactory().createApp(type);
    app.develop();
    app.test();
    app.debug();
    app.deliver();
}

public static void main(String... args) {
    new AppStore().orderApp(AppType.WATCH);
}

}

System.out.println("------");

final AdvancedAppCache advancedCache = new AdvancedAppCache();
for (final AppType t : AppType.values()) {
    advancedCache.get(t);
}
for (final AppType t : AppType.values()) {
    advancedCache.get(t);
}

}

private final String requirements;

public Design(final String requirements) {
    this.requirements = requirements;
    System.out.println(toString());
}

@Override
public String toString() {

    return "App design for requirements: " + requirements;
}

}

/**

  • Simple app mock which does nothing currently.
  • @author Fazrin

*/ public class App {

private final String title;

public App(final Design design, final String title) {
    System.out.println("Creating an app based on the design: " + design);
    this.title = title;
}

@Override
public String toString() {

    return "Fantastic app with title: " + title;
}

}

Then we create the back-end workers:

/**

  • Simple designer class.
  • @author Fazrin

*/ public class AppDesigner {

public static Design design(final String requirements) {

    return new Design(requirements);
}

}

import java.util.Arrays; import java.util.Collections; import java.util.List;

/**

  • Simple app developer mock.
  • @author Fazrin

*/ public class AppDeveloper {

private static final List<String> POSSIBLE_NAMES = Arrays.asList("Pointless Kangaroo",
        "Moon Forgotten", "Purple Rusty Space", "Blue Vulture", "Skilled Ostrich", "Saturday's Intense Xylophone",
        "Harsh Reborn Gravel", "Liquid Rhinestone", "Wooden Haystack", "Scissors Olive", "Moving Doorstop",
        "Strong Restless Arm", "Dreaded Pottery");

public static App develop(final Design design) {

    Collections.shuffle(POSSIBLE_NAMES);

    return new App(design, POSSIBLE_NAMES.get(0));
}

}

/**

  • A simple app tester.
  • @author Fazrin

*/ public class AppTester {

public static boolean test(final App app, final Design design) {

    System.out.println("Testing app: " + app + ", based on the design " + design);

    return Math.random() < 0.5;
}

}

Without a facade we have to know how every component works until we have our final App. An example of calling the workers is listed here:

/**

  • Entry point for the application and here we can order our apps.
  • @author Fazrin

*/ public class AppStore {

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

    Design d = AppDesigner.design(
            "Create me a cool app which knows everything I want and makes this fast. And of course it has to be cheap.");

    App app = AppDeveloper.develop(d);
    while (!AppTester.test(app, d)) {
        System.out.println("App " + app + " failed the tests :(");
    }
}

}

public static App orderApp(final String requirements) {

    Design d = AppDesigner.design(requirements);
    App app = AppDeveloper.develop(d);
    while (!AppTester.test(app, d)) {
        System.out.println("App " + app + " failed the tests :(");
    }

    System.out.println("Successfully developed " + app);

    return app;
}

}

public static void main(final String... args) {
    AppCreationFacade.orderApp(
            "Create me a cool app which knows everything I want and makes this fast. And of course it has to be cheap.");
}

}eval(ez_write_tag([[336,280],‘javabeginnerstutorial_com-medrectangle-4’,’ezslot_3’,113,‘0’,‘0’]));

String getTitle();

String getAuthor();

String getContent();

}

/**

  • A simple implementation of our real book which loads its contents when constructed.
  • @author Fazrin

*/ public class RealBook implements Book {

private final String title;
private final String author;
private String content;

public RealBook(final String title, final String author) {
    this.title = title;
    this.author = author;
    loadContentFromDatabase(title, author);
}

@Override
public String getTitle() {

    return title;
}

@Override
public String getAuthor() {

    return author;
}

@Override
public String getContent() {

    return content;
}

private void loadContentFromDatabase(final String title, final String author) {

    System.out.println("Loading content from database...");
    try {
        Thread.sleep(2500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    this.content = MessageFormat.format("Interesting and very large content of {0} by {1}", title, author);
}

}eval(ez_write_tag([[728,90],‘javabeginnerstutorial_com-medrectangle-3’,’ezslot_3’,110,‘0’,‘0’]));

private final String title;
private final String author;
private RealBook realBook;

public ProxyBook(final String title, final String author) {
    this.title = title;
    this.author = author;
}

@Override
public String getTitle() {

    return title;
}

@Override
public String getAuthor() {

    return author;
}

@Override
public String getContent() {

    if (realBook == null) {
        realBook = new RealBook(title, author);
    }
    return realBook.getContent();
}

}

import java.util.Map; import java.util.stream.Stream;

/**

  • Simple service to find the right books and load their content.
  • And this is the main entry point of the application too.
  • @author Fazrin

*/ public class LibraryService {

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

    new LibraryService().findContentByAuthorAndTitle("GoF", "Design Patterns").entrySet()
            .forEach(b -> System.out.println(b.getKey() + " --> " + b.getValue()));
}

/**
 * Finds the right book for our test purposes and then loads its content.
 */
public Map<String, String> findContentByAuthorAndTitle(final String author, final String title) {

    return filterByTitle(filterByAuthor(findAllBooks(), author), title)
            .collect(toMap(Book::getTitle, Book::getContent));
}

/*
 * The following methods are really simple and I think straightforward too.
 */
private Stream<Book> findAllBooks() {

    return Stream.of(new ProxyBook("Design Patterns", "Gabor Laszlo Hajba"),
            new ProxyBook("Design Patterns", "GoF"), new ProxyBook("Python 3 in Anger", "Gabor Laszlo Hajba"),
            new ProxyBook("Design Patterns", "Head First"));
}

private Stream<Book> filterByAuthor(final Stream<Book> books, final String author) {

    return books.filter(b -> author.equals(b.getAuthor()));
}

private Stream<Book> filterByTitle(final Stream<Book> books, final String title) {

    return books.filter(b -> title.equals(b.getTitle()));
}

}eval(ez_write_tag([[580,400],‘javabeginnerstutorial_com-medrectangle-4’,’ezslot_0’,113,‘0’,‘0’]));

/**

  • This class is the data container holding information we want to iterate through.
  • @author Fazrin

*/ public class DataContainer {

private final Set<String> data = new HashSet<>();

public void add(String element) {
    this.data.add(element);
}

public Collection<String> getData() {
    return this.data;
}

}

/**

  • This is the main entry point of the application.
  • @author Fazrin

*/ public class Main { public static void main(String… args) { final DataContainer dc = new DataContainer(); dc.add(“Iterator”); dc.add(“Design”); dc.add(“Pattern”); dc.add(“by”); dc.add(“Gabor”); dc.add(“Hajba”);

    Collection<String> data = dc.getData();

    final Iterator<String> it = data.iterator();
    while (it.hasNext()) {
        System.out.print(it.next() + "  ");
    }
    System.out.println();

    data.clear();
    data = dc.getData();
    System.out.println("size of data is " + data.size());
}

}eval(ez_write_tag([[728,90],‘javabeginnerstutorial_com-medrectangle-3’,’ezslot_2’,110,‘0’,‘0’]));

/**

  • This class is the data container holding information we want to iterate through.
  • @author Fazrin

*/ public class IterableDataContainer {

private final Set<String> data = new HashSet<>();

public void add(String element) {
    this.data.add(element);
}

public Iterator<String> getIterator() {
    return new DataIterator(this);
}

private Set<String> getData() {
    return this.data;
}

private class DataIterator implements Iterator<String> {

    private final Iterator<String> iterator;

    public DataIterator(IterableDataContainer cont) {
        this.iterator = cont.getData().iterator();
    }

    @Override
    public boolean hasNext() {
        return this.iterator.hasNext();
    }

    @Override
    public String next() {
        if (this.iterator.hasNext()) {
            return this.iterator.next();
        }
        return null;
    }
}

}eval(ez_write_tag([[336,280],‘javabeginnerstutorial_com-medrectangle-4’,’ezslot_3’,113,‘0’,‘0’]));

/**

  • This is the main entry point of the application.
  • @author Fazrin

*/ public class Main { public static void main(String… args) {

    final IterableDataContainer dc = new IterableDataContainer();
    dc.add("Iterator");
    dc.add("Design");
    dc.add("Pattern");
    dc.add("by");
    dc.add("Gabor");
    dc.add("Hajba");

    final Iterator<String> it = dc.getIterator();
    while (it.hasNext()) {
        System.out.print(it.next() + "  ");
    }
    System.out.println();
}

}