Consider an application that prints student names taken from a data access object.
The application can work with different data stores and the storage should not affect the operations performed. That’s why there is an interface DataAccess that provides the data and several implementations (in this case two) that know how to handle the reading from the actual data storage.
Another part of the application is the DataProcessor that prints students obtained from the DataAccess. The DataProcessor does not care what is the actual storage it needs just the data (the students’ names).
Here is a sample implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public class DataProcessor { public void printStudents(DataAccess dataAccess) { for (String student : dataAccess.getAllStudents()) { System.out.println(student); } } } public interface DataAccess { Collection<String> getAllStudents(); } public class MongoAccess implements DataAccess { @Override public Collection<String> getAllStudents() { return Arrays.asList("Student1 from Mongo", "Student2 from Mongo"); } } public class RdbmsAccess implements DataAccess { @Override public Collection<String> getAllStudents() { return Arrays.asList("Student1 from RDBMS", "Student2 from RDBMS"); } } |
Source: DataProcessor.java
Source: DataAccess.java
Source: MongoAccess.java
Source: RdbmsAccess.java
Here is how a main app would look like written in Java 7. First a Factory class that creates a DataAccess is required:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class Factory { DataAccess create(String type) { if ("mongo".equals(type)) { return new MongoAccess(); } if ("dbms".equals(type)) { return new RdbmsAccess(); } throw new IllegalArgumentException("The specified type is not supported"); } } |
Source: Factory.java
and an Application that actually uses the DataProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class Application { public static void main(String[] args) { if (args.length > 0) { Factory factory = new Factory(); DataAccess dataAccess = factory.create(args[0]); DataProcessor dataProcessor = new DataProcessor(); dataProcessor.printStudents(dataAccess); } else { System.out.println("Specify data access type"); } } } |
Source: Application.java
This code can consist of fewer lines if using Java 8 capabilities for Lambdas and the interfaces defined in java.util.function.Supplier
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class ApplicationWithLambda { public static void main(String[] args) { if (args.length > 0) { DataProcessor dataProcessor = new DataProcessor(); Supplier dataAccessSupplier = () -> { String type = args[0]; if ("mongo".equals(type)) { return new MongoAccess(); } if ("dbms".equals(type)) { return new RdbmsAccess(); } throw new IllegalArgumentException("The specified type is not supported"); }; dataProcessor.printStudents(dataAccessSupplier.get()); } else { System.out.println("Specify data access type"); } } } |
Source: ApplicationWithLambda.java
In this case the Factory class is replaced by java.util.function and the code is inlined using a lambda expression.
This code is more compact that the one using Java 7 but it can become event more compact using method references.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class ApplicationWithMetodReference { public static void main(String[] args) { if (args.length > 0) { Map<String, Supplier> factories = new HashMap<>(); factories.put("mongo", MongoAccess::new); factories.put("rdbms", RdbmsAccess::new); DataProcessor dataProcessor = new DataProcessor(); dataProcessor.printStudents(factories.get(args[0]).get()); } else { System.out.println("Specify data access type"); } } } |
Source: ApplicationWithLambda.java
Here the Supplier is inlined using a method reference to the constructor of each data access class. A nice benefit of this approach is that in case of not supported type it is easy the show a readable error displaying all supported types.
1 2 3 4 |
String type = args[0]; if (!factories.containsKey(args[0])) { factories.keySet().forEach(System.out::println); } |
You can find the full code here