After a few years of coding in python, I recently moved to Java for a project.
While working with Python, I had a pretty implementation for a factory.
# file abstract_product.py
from abc import ABC, abstractmethod
class AbstractProduct(ABC):
#abstractmethod
def do_something():
pass
# file product_factory.py
from abstract_product import AbstractProduct
class ProductFactory:
def __init__(self):
self._creators = {}
def get(self, product_name) -> Product:
if product_name not in self._creators:
raise ValueError('No valid implementation !')
return self._creators[product_name]()
def register(self, product_name, product):
self._creators[product_name] = product
product_factory = ProductFactory()
# file product1.py
from abstract_product import AbstractProduct
from product_factory import product_factory
class Product1(AbstractProduct):
def do_something():
# does something
pass
product_factory.register('product1', Product1)
Now the advantage would be, that if I had a new Implementation for
Product, all I had to do was
# file product2.py
from abstract_product import AbstractProduct
from product_factory import product_factory
class Product2(AbstractProduct):
def do_something():
# does something
pass
product_factory.register('product2', Product2)
The advantages of the above approach were:
I had my factory as a singleton. Defining the variable in module ensured that.
Registering a new Product, included no changes to the existing code.
No dirty if else ladder has to be set up any where!
The new implementation registered to the factory in their own module. SO CLEAN :D :D
All the client code needed to know was the product_factory from above and the string parameter based on which the client would get some implementation of Product.
However, now with Java, I am thinking what can I do, to get close to the simplicity and extensibility that the above approach had !
Note:
Please also suggest some other approach that you might have come across for an extensible factory that might even be better than the above !
Your python code can be quite easily translated to Java, and it doesn't look too "foreign".
// could even be an interface
abstract class Product {
// ...
abstract void doSomething();
}
final class ProductFactory {
// not strictly a singleton, to allow you to create multiple factories
// your python code allows this too
private static ProductFactory instance = new ProductFactory();
public static ProductFactory getInstance() {
return instance;
}
private HashMap<String, Supplier<? extends Product>> creators = new HashMap<>();
public void register(String productName, Supplier<? extends Product> creator) {
creators.put(productName, creator);
}
public Product get(String productName) {
Supplier<? extends Product> creator = creators.get(productName);
if (creator == null) {
throw new IllegalArgumentException("No valid implementation !");
}
return creator.get();
}
}
class Product1 extends Product {
#Override
void doSomething() {
}
}
Example of registering and getting a product:
ProductFactory.getInstance().register("product1", Product1::new);
System.out.println(ProductFactory.getInstance().get("product1"));
This is how I like to do it (taken another class example),
public class MyFactory {
private Map<String, MyInterface> factoryMap = new HashMap<>();
#Autowired
public MyFactory(List<MyInterface> listOfObjectsImplementingMyInterface) {
for (MyInterface myInterface : listOfObjectsImplementingMyInterface) {
//Get the class annotation value, use it as map's key
String strategyKey = myInterface.getClass().getAnnotationsByType(Component.class)[0].value();
factoryMap.put(strategy, myInterface);
}
}
// To get an instantiation from factory
public MyInterface getFromFactory(String strategyKey) {
return factoryMap.get(strategyKey);
}
}
The above example is a snippet from a spring f/w project, and with this method you can utilise the spring annotations to populate the factory rather than using messy if/else/switch blocks. The above method can be extended to other cases as well with custom annotations.
In my opinion, a strict conversion from your Python code into Java would be the snippet from below. Show this should feel more familiar to you.
For a simple application, you could make your Factory use static or use the Singleton design pattern to ensure a single instance.
If you are using some frameworks, is quite likely that it offers an alternative that requires less coding though and better testability.
import java.util.HashMap;
import java.util.Map;
// AbstractProduct may better be an 'interface' even
abstract class AbstractProduct {
abstract void doSomething();
}
class Product1 extends AbstractProduct {
#Override
void doSomething() {
System.out.println("I'm Product ONE (1)");
}
}
class Product2 extends AbstractProduct {
#Override
void doSomething() {
System.out.println("I'm Product TWO (2)");
}
}
class ProductFactory {
private final Map<String, Class<? extends AbstractProduct>> creators;
ProductFactory() {
this.creators = new HashMap<>();
}
AbstractProduct get(String productName) {
if (!creators.containsKey(productName)) {
throw new RuntimeException("No valid implementation !");// <-- better define or use a specific exception
}
try {
return creators.get(productName).newInstance(); // <-- Class.newInstance is deprecated since Java9, check docs for replacement
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e); // <-- deal with it properly
}
}
void register(String productName, Class<? extends AbstractProduct> productClass) {
creators.put(productName, productClass);
}
}
public class PythonFactoryThing {
public static void main(String[] args) {
ProductFactory productFactory = new ProductFactory();
productFactory.register("product1", Product1.class);
productFactory.register("product2", Product2.class);
productFactory.get("product1").doSomething();
productFactory.get("product2").doSomething();
productFactory.get("product3").doSomething(); // <-- throws exception
}
}
EDIT: if you don't want a separate place/file to register, you could use a static initialisation block (some Database drivers do that).
You'd have to adapt the above code like this:
Make a singleton out of your Factory:
private ProductFactory() {}// private constructor so nobody else cana instantiate it
public static final ProductFactory INSTANCE = new ProductFactory();
And make each class do something like this:
class Product2 extends AbstractProduct {
static {
ProductFactory.INSTANCE.register("product2", Product2.class);
}
#Override
void doSomething() {
System.out.println("I'm Product TWO (2)");
}
}
But for that to work you would also need to explicitly load the classes you want to have registered. Either by using them some place before using the Factory, or by calling:
Class.forName("Product1");
Since the class loader only loads the classes that are explicitly needed. Which I suppose would be equivalent in Python to importing the file in your script.
Related
Let's say I have an application that is responsible for taking a vendor message and converting into a canonical message. For example:
public class MessageA extends VendorMessage { ... }
public class MessageB extends VendorMessage { ... }
public class MessageX extends CanonicalMessage { ... }
public class MessageY extends CanonicalMessage { ... }
Where MessageA maps to MessageX and MessageB maps to MessageY.
My approach is that I have one transformer class per message type to handle this conversion. In this example, I would have the following transformers:
public class MessageXTransfomer()
{
public MessageX transform(MessageA message) {...}
}
public class MessageYTransfomer()
{
public MessageY transform(MessageB message) {...}
}
My questions is really with the way I would ultimately invoke the transformers.
Since my process takes some VendorMessage as an input, I need to interrogate the type so I know which specific transformer to direct it to. For example, one approach might look like this:
public class TransfomerService
{
MessageXTransformer messageXTransformer = new MessageXTransformer();
MessageYTransformer messageYTransformer = new MessageYTransformer();
public CanonicalMessage transform(VendorMessage message)
{
if (message instanceOf MessageA)
{
return messageXTransformer.transform((MessageA) message);
}
else if (message instanceOf MessageB)
{
return messageYTransformer.transform((MessageB) message);
}
}
}
I'm not sure why, but I this approach just feels strange - as if I'm doing something wrong. Is there a best practice for this kind of problem that I should be using?
Note: I'm looking for the best approach without using any transformation frameworks, etc. Ideally, the pattern would be achievable using just basic Java.
I like the answer of #javaguy however it is not complete. Of course it will be nice if you could use the specific transformer like in his later example, but if you can't you have to stick with TransformerFacade and kind of a StrategyPattern:
public class TransformerFacade {
private Map<Class, VendorMessageToCanonicalMessageTransformer> transformers = new HashMap<>();
{
// this is like strategies, the key may be class, class name, enum value, whatever
transformers.put(MessageA.class, new MessageXTransformer());
transformers.put(MessageB.class, new MessageYTransformer());
}
public CanonicalMessage transform(VendorMessage message) {
return transformers.get(message.getClass()).transform(message);
}
}
I would simply let every concrete VendorMessage return its corresponding CanonicalMessage by implementing an interface:
public interface Mapper<T> {
T map();
}
Then, MessageA should implement this interface:
public MessageA implements Mapper<MessageX> {
#Override
public MessageX map() {
MessageX message = ...;
// fill message
return message;
}
}
If you don't want to do the mapping in the VendorMessage class, then a strategy as suggested by Vadim Kirilchuk in his answer would do the trick.
Not sure if the title does this justice. I am kind of new to Java and trying to figure out how to have a single class use different "services". Let say I have an APIRequest class, this class needs to be able to use different APIs depending on what is needed. Example. I need to ship a package, if the package is below 32OZ I need to use Endicia, else I need to use FedEx. I have 2 "service" classes FedexRequest and EndiciaRequest. I am trying to allow the APIRequest class use either one depending on what the weight of the package. I created a class called APIService that has a static method called getService. it just creates a map of string name -> request class like so...
public class APIService {
private static Map<String, Object> services = new HashMap<>();
private static final Map<String, String> availableServices = new HashMap() {{
put("fedex", "FedexRequest");
put("endicia", "EndiciaRequest");
}};
public static Object getService(String type) {
if(services.containsKey(type)) {
return services.get(type);
}
return null;
}
static {
for(Map.Entry<String, String> serv : availableServices.entrySet()) {
try {
Class<?> cls = Class.forName(serv.getValue());
services.put(serv.getKey(), cls.newInstance());
} catch(Exception e) {
services.put(serv.getKey(), new Class[1]);
}
}
}
}
So now I can call APIService.getService("fedex"); however I am having a really hard time trying to figure out how to use that in my APIRequest class, because I would need to do something like...
this.service = (FedexRequest) APIService.getService("fedex");
//or
this.service = (EndiciaRequest) APIService.getService("endicia);
but that breaks the whole dynamic part of the equation, what if I need to add another service later?
I tried having both FedexRequest and EndiciaRequest implement a Request interface, then use
this.service = (Request) APIService.getService("fedex");
but that gives me a Java.lang.Class error saying it cannot be cast to Request. I am assuming it is because Request is an interface so you cannot use cls.newInstance() on an implementing class then cast to the interface.
I am really lost on how to allow my APIRequest class to use either FedexRequest or EndiciaRequest, without specifically using the type casting, so that it can be dynamic and we could add a service later without recoding the whole thing. I come from PHP where this would be extremely simple, since you do not have to explicitly define a type. Any help would be greatly appreciated. Thank you.
If I were you I would do the following:
This is the implementation of Service interface:
public interface Service {
public void performAction();
//other common functions...
}
A small modification to your APIService class:
public class APIService {
private static Map<String, Service> services = new HashMap<>();
private static final Map<String, String> availableServices = new HashMap() {{
put("fedex", "com.finity.shipping.api.fedexapi.FedexRequest");
put("endicia", "com.finity.shipping.api.endiciaapi.EndiciaRequest");
}};
public static Service getService(String type) {
return services.get(type);
}
static {
for(Map.Entry<String, String> serv : availableServices.entrySet()) {
try {
Class<?> cls = Class.forName(serv.getValue());
services.put(serv.getKey(), cls.newInstance());
} catch(Exception e) {
services.put(serv.getKey(), new Class[1]);
}
}
}
}
Every time you need a service to be added to your application just implement the Service interface:
public class FedexRequest implements Service {
public void performAction() {
//do something
}
}
And finally in your class where you use this.service:
Service service;
...
this.service = APIService.getService("fedex");
this.service.performAction();
Pooya solution is good.
I will add something. You use some strings to represent things which are typable : constants and classes. Using reflection to initialize a factory where you handle only classes written in hard in some strings(for example com.finity.shipping.api.fedexapi.FedexRequest) and belonging to your own project seems to be a overhead.
In case where your factory don't know which classes it will instantiate, using reflection is meaningful. But it seems not be the case.
Besides, FEDEX and ENDICIA could be constant by using enum. It allows to type them and to avoid bad suprpises.
We would expect that your factory be more simple. Here an example :
public class APIService {
public static enum TypeRequest{
FEDEX, ENDICIA;
}
private static Map<String, Service> services = new HashMap<>();
static {
services.put(FEDEX, new FedexRequest());
services.put(ENDICIA, new EndiciaRequest());
}
public static Service getService(TypeRequest typeRequest) {
return services.get(typeRequest);
}
}
I've currently got the following classes/interfaces laid out. The type T represents the format of data returned from DataProvider implementations. I'm using a factory so I don't need to attach type information to MyStreamingOutput. I'm using HK2 to inject the DataProviderFactory into MyStreamingOutput.
public interface DataProvider<T> {
public T next() { ... }
...
}
public final class SQLDataProvider<T> {
public SQLDataProvider(final String query, final RowMapper<T> rowMapper) { ... }
}
public interface DataProviderFactory {
public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper);
...
}
public final class SQLDataProviderFactory {
public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper) {
return new SQLDataProvider<T>(query, rowMapper);
}
}
public final class MyStreamingOutput implements StreamingOutput {
public MyStreamingOutput(final DataProviderFactory dpFactory) { ... }
#Override public void write(final OutputStream outputStream) throws IOException { ... }
}
This all works fine. Now I'm trying to set up a unit test for MyStreamingOutput, but I'm running into a couple of roadblocks. I wrote the following additional class for testing purposes:
public final class DataProviderFactoryStub implements DataProviderFactory {
private final DataProvider dataProvider;
public DataProviderFactoryStub() {
this.dataProvider = new DataProviderStub();
}
public DataProviderFactoryStub(final DataProvider dataProvider) {
this.dataProvider = dataProvider;
}
#Override
public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper) {
return this.dataProvider;
}
}
The binding occurs in
final class QueryTestResourceConfig extends ResourceConfig {
public QueryTestResourceConfig() {
...
this.register(new AbstractBinder() {
#Override
protected void configure() {
bind(DataProviderFactoryStub.class).to(DataProviderFactory.class);
}
});
}
}
I can successfully inject this class into MyStreamingOutput, but it has a compiler warning because the typing information used by getDataProvider() isn't shared by the instance passed into the factory. I can't add type information to the DataProviderFactoryStub class because then it no longer implements the DataProviderFactory interface. I don't want type information on the interface because it's wrong - outside of the Stub case, the factories shouldn't care about the type returned by DataProvider instances. I'd very much like to avoid using setters for the query and rowMapper parameters because I consider it bad design in this case.
I can't shake the feeling that I'm either missing something subtle in my application of generics or something obvious in my application of dependency injection. What is the right way to address this use case? It seems like this is the kind of problem DI is meant to address, but I can't see how to fix it.
When using DI, we usually end up with factory classes that are very basic (i.e., their creation methods are typically simple enough to fit on a single line). Your SQLDataProviderFactory class is a perfect example of this.
The reason for this is because a factory object is just a placeholder for the creation of an object. We want to avoid littering our code with new keywords, because doing so tightly couples code to a specific type. So we end up with factories whose methods are essentially just glorified new keywords.
I bring this up to point out that it's the type of the product that is important here; the factory is just a conduit. When you replace a factory with a test double, what you're really doing is replacing a product with a test double. This means that whenever I define a test double factory, I always have to define a test double product as well.
For example, your stub factory is just trying to return a stub product. The problem is that the type of the stub product it's returning does not match the type expected by calling code. If you define your own stub product, the code falls into place:
public final class DataProviderStub<T> implements DataProvider<T> {
private final T dummy;
public DataProviderStub() { }
public T next() { return this.dummy; } // Just for example
}
public final class DataProviderFactoryStub implements DataProviderFactory {
public DataProviderFactoryStub() { }
#Override
public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper) {
return new DataProviderStub<T>();
}
}
The stub factory only exists so you can inject the stub DataProvider into your SUT.
Unfortunately, due to type erasure, it isn't possible to do what I want. I will have to look at refactoring the existing code.
I am trying to build a factory object, but having trouble working out a good way of doing it in Java.
The application I am writing is used for processing files in various formats, so there is a CodecInterface which applies to all classes which are used for reading and writing files. Let's assume it defines the following methods. Each of these files has a unique human-designated ID string which is used for id'ing the encoder\decoder.
String read();
void write(String data);
String getID();
The factory class would have a create method which is intended to create instances of these codec classes. I imagine the method signature would look something like this.
static CodecInterface CodecFactory.create(String filename, String codecid, String args);
The filename is the name of the file to read/write, and the codecid is the unique ID indicating what codec to use. The args parameter is a string of arguments passed to the decoder/encoder object being generated. The return of this should be an instance of the requested codec object.
All of the Factory examples I have seen typically have a switch statement inside of the create method which creates an object instance dependent on the ID. I want to avoid doing it this way as it doesn't seem like the 'right' way, and it also means that the list is more or less fixed unless you modify the create method. Ideally I would like to use something like a dictionary (indexed by the codec ID) which contains something which can be used to create an instance of the codec classes I want (I will call this mystery class ClassReference). Again to use some quasi-java code, here is what I was thinking as the body for the create method.
static Dictionary<String, ClassReference>;
static CodecInterface CodecFactory.create(String filename, String codecid, String args);
{
ClassReference classreference;
classreference = codeclibrary(codecid);
return classreference.instanceOf(args);
}
The dictionary of ID's is easy enough, but I can't work out what ClassReference should be. Class Reference should allow me to create an instance of the desired class, as in the example above.
From looking around online, the class method, and instanceOf seem to be heading in the right direction, but I haven't found anything which puts the two together. As an added complication, the constructors for the objects being created will have arguments.
Any tips on what I should be looking at would be greatly appreciated.
Thanks in advance.
SOLUTION
Thanks everyone for your advice. I've ended up taking bits and pieces from all of your suggestions and came up with the following which seems to work as I wanted.
Note that I have omitted much of the sanity\error checking code to show off the important bits.
import java.lang.reflect.Constructor;
import java.util.HashMap;
public class CodecFactory
{
private static HashMap<String, Class<? extends CodecInterface>> codecs;
static
{
codecs = new HashMap<String, Class<? extends CodecInterface>>();
//Register built-in codecs here
register("codecA", CodecA.class);
register("codecB", CodecB.class);
register("codecC", CodecC.class);
}
public static void register(String id, Class<? extends CodecInterface> codec)
{
Class<? extends CodecInterface> existing;
existing = codecs.get(id);
if(existing == null)
{
codecs.put(id, codec);
}
else
{
//Duplicate ID error handling
}
}
public static CodecInterface create(String codecid, String filename, String mode, String arguments)
{
Class<? extends CodecInterface> codecclass;
CodecInterface codec;
Constructor constructor;
codec = null;
codecclass = codecs.get(codecid);
if(codecclass != null)
{
try
{
constructor = codecclass.getDeclaredConstructor(String.class, String.class, String.class, String.class);
codec = (CodecInterface)(constructor.newInstance(codecid, filename, mode, arguments));
}
catch(Exception e)
{
//Error handling for constructor/instantiation
}
}
return codec;
}
}
There's a zillion options. For example, you might create a base factory class that also has static methods to manage registered factories (untested code typed here, sorry for errors):
public abstract class CodecFactory {
private final String name;
public CodecFactory (String name) {
this.name = name;
}
public final String getName () {
return name;
}
// Subclasses must implement this.
public abstract Codec newInstance (String filename, String args);
// --- Static factory stuff ---
private static final Map<String,CodecFactory> factories = new HashMap<String,CodecFactory>();
public static void registerFactory (CodecFactory f) {
factories.put(f.getName(), f);
}
public static Codec newInstance (String filename, String codec, String args) {
CodecFactory factory = factories.get(codec);
if (factory != null)
return factory.newInstance(filename, args);
else
throw new IllegalArgumentException("No such codec.");
}
}
Then:
public class QuantumCodecFactory extends CodecFactory {
public QuantumCodecFactory {
super("quantum");
}
#Override public Codec newInstance (String filename, String args) {
return new QuantumCodec(filename, args);
}
}
Of course this means at some point you must:
CodecFactory.registerFactory(new QuantumCodecFactory());
Then usage is:
Codec codec = CodecFactory.newInstance(filename, "quantum", args);
Another option is to use reflection and maintain a Map<String,Class<? extends CodecInterface>>, using Class.newInstance() to instantiate. This is convenient to implement because it works on top of Java's Class, which already supports a factory-style model for instantiating objects. The caveats are, like above the classes must be explicitly registered, and also (unlike above) you can't implicitly enforce constructor parameter types at compile-time (although you could at least abstract it behind some method instead of calling Class.newInstance() directly from client code).
For example:
public final class CodecFactory {
private static final Map<String,Class<? extends Codec>> classes = new HashMap<String,Class<? extends Codec>>();
public static void registerClass (String name, Class<? extends Codec> clz) {
classes.put(name, clz);
}
public static Codec newInstance (String filename, String codec, String args) {
Class<? extends Codec> clz = classes.get(codec);
if (clz != null)
return clz.getDeclaredConstructor(String.class, String.class).newInstance(filename, args);
else
throw new IllegalArgumentException("No such codec.");
}
}
Where every Codec is expected to have a constructor that takes (String filename, String args). Registration, then, is:
CodecFactory.registerClass("quantum", QuantumCodec.class);
Usage is the same as above:
Codec codec = CodecFactory.newInstance(filename, "quantum", args);
You could even leave out the map and just use Class.forName() -- this doesn't give you as much flexibility with codec names, but it essentially lets the class loader do all of the work for you and you don't need to explicitly register types ahead of time.
Edit: Re: Question in comments below. You could come up with a system that combined the two above examples to create a reusable, reflection based generic factory derived from CodecFactory, which still leaves you with the ability to create other more specialized factories, e.g.:
public class GenericCodecFactory extends CodecFactory {
private final String name;
private final Class<? extends Codec> clz;
public GenericCodecFactory (String name, String clzname) {
this.name = name;
this.clz = Class.forName(clzname);
}
public GenericCodecFactory (String name, Class<? extends Codec> clz) {
this.name = name;
this.clz = clz;
}
// parameter type checking provided via calls to this method, reflection
// is abstracted behind it.
#Override public Codec newInstance (String filename, String args) {
return clz.getDeclaredConstructor(String.class, String.class).newInstance(filename, args);
}
}
Then you could use that for whatever:
// you can use specialized factories
ClassFactory.registerFactory(new QuantumCodecFactory());
// you can use the generic factory that requires a class at compile-time
ClassFactory.registerFactory(new GenericCodecFactory("awesome", AwesomeCodec.class));
// you can use the generic factory that doesn't need to have class present at compile-time
ClassFactory.registerFactory(new GenericCodecFactory("ninja", "com.mystuff.codecs.NinjaCodec"));
As you can see, there's a ton of possibilities. Using Class.forName() in reflection-based factories is nice because the class doesn't need to be present at compile-time; so you can drop in codec classes on the class path and, say, specify a list of class names in a run-time configuration file (then you could have static ClassFactory.registerFactoriesListedInFile(String confgFilename) or something), or scan a "plugin" directory. You can even construct class names from simpler strings, if you're comfortable with that, e.g.:
public class GenericPackageCodecFactory extends GenericCodecFactory {
public GenericPackageCodecFactory (String name) {
super(name, "com.mystuff." + name + ".Codec");
}
}
You could even use something like that as a fallback in ClassFactory if the codec name is not found, to get around having to explicitly register types.
The reason reflection keeps popping up, by the way, is that it's very flexible and the Class interface is essentially an all-encompassing class factory, so it frequently parallels what specific factory architectures are trying to accomplish.
Another option is to use the second example I mentioned above (with the Map<String,Class>) but make a version of registerFactory that takes a String class name instead of a Class, similar to the generic implementation I just mentioned. That's probably the least amount of code required to avoid having to create instances of CodecFactorys.
I can't possibly give examples for every combination of things you can do here, so here is a partial list of the tools you have available, which you should use as you see fit. Remember: Factories are a concept; it's up to you to use the tools you have to implement that concept in a clean way that meets your requirements.
Reflection (Class<?> and Class.forName)
Static initializer blocks (sometimes a good place to register a factory; requires class to be loaded, but Class.forName can trigger this).
External configuration files
Plugin frameworks like http://jpf.sourceforge.net/ or https://code.google.com/p/jspf/ or https://code.google.com/p/jin-plugin/ (good comparison of OSGi, JPF, JSPF can be found here; I've never heard of jin-plugin prior to looking through the answers in the link).
Maps of registered factories and/or ability to use reflection to generate class names on the fly.
Don't forget concurrent maps and/or synchronization primitives for multi-threaded support if necessary.
Lots of other stuff.
Also: Don't go crazy implementing all of these possibilities if you don't have to; think about your requirements and decide on the minimum amount of work you need to do here to meet them. For example, if you need extensible plugins, JSPF alone might be enough to satisfy all of your requirements without you having to do any of this work (I haven't actually checked it out, so I'm not sure). If you don't need that kind of plugin "scanning" behavior, simple implementations like the examples above will do the trick.
Try something like that:
public class CodecFactory {
final private static Map<String, Class<? extends CodecInterface>> codecLibrary;
static {
codecLibrary = new HashMap<String, Class<? extends CodecInterface>>();
codecLibrary.put("codec1", Codec1.class);
//...
}
static CodecInterface create(String filename, String codecid, String args) throws InstantiationException, IllegalAccessException {
Class<? extends CodecInterface> clazz;
clazz = codecLibrary.get(codecid);
CodecInterface codec = clazz.newInstance();
codec.setArgs(args);
codec.setFilename(filename);
return codec;
}
}
You can use enum as well, just like below :
interface CodecInterface {
}
class CodecA implements CodecInterface {
}
class CodecB implements CodecInterface {
}
class CodecC implements CodecInterface {
}
enum CodecType {
codecA {
public CodecInterface create() {
return new CodecA();
}
},
codecB {
public CodecInterface create() {
return new CodecB();
}
},
codecC {
public CodecInterface create() {
return new CodecC();
}
};
public CodecInterface create() {
return null;
}
}
class CodecFactory {
public CodecInterface newInstance(CodecType codecType) {
return codecType.create();
}
}
I just wonder: with Java 8, and the possibility to add implementation in interfaces (a bit like Scala traits), will it be possible to implement the cake pattern, like we can do in Scala?
If it is, can someone provide a code snippet?
With inspiration from other answers I came up with the following (rough) class hierarchy that is similar to the cake pattern in Scala:
interface UserRepository {
String authenticate(String username, String password);
}
interface UserRepositoryComponent {
UserRepository getUserRepository();
}
interface UserServiceComponent extends UserRepositoryComponent {
default UserService getUserService() {
return new UserService(getUserRepository());
}
}
class UserService {
private final UserRepository repository;
UserService(UserRepository repository) {
this.repository = repository;
}
String authenticate(String username, String password) {
return repository.authenticate(username, password);
}
}
interface LocalUserRepositoryComponent extends UserRepositoryComponent {
default UserRepository getUserRepository() {
return new UserRepository() {
public String authenticate(String username, String password) {
return "LocalAuthed";
}
};
}
}
interface MongoUserRepositoryComponent extends UserRepositoryComponent {
default UserRepository getUserRepository() {
return new UserRepository() {
public String authenticate(String username, String password) {
return "MongoAuthed";
}
};
}
}
class LocalApp implements UserServiceComponent, LocalUserRepositoryComponent {}
class MongoApp implements UserServiceComponent, MongoUserRepositoryComponent {}
The above compiles on Java 8 as of Jan.9 2013.
So, can Java 8 do a cake-like pattern? Yes.
Is it as terse as Scala, or as effective as other patterns in Java (i.e. dependency injection)?
Probably not, the above sketch required a whole lot of files and is not as terse as Scala.
In summary:
Self-types (as needed for the cake pattern) can be emulated by extending the base interface we expect.
Interfaces cannot have inner classes (as noted by #Owen), so instead we can use anonymous classes.
val and var can be emulated by using a static hashmap (and lazy initialization), or by the client of the class simply storing the value on their side (like UserService does).
We can discover our type by using this.getClass() in a default interface method.
As #Owen notes, path dependent types are impossible using interfaces, so a full cake pattern is inherently impossible. The above shows, however, that one could use it for dependency injection.
Maybe you can do something like this in Java 8
interface DataSource
{
String lookup(long id);
}
interface RealDataSource extends DataSource
{
default String lookup(long id){ return "real#"+id; }
}
interface TestDataSource extends DataSource
{
default String lookup(long id){ return "test#"+id; }
}
abstract class App implements DataSource
{
void run(){ print( "data is " + lookup(42) ); }
}
class RealApp extends App implements RealDataSource {}
new RealApp().run(); // prints "data is real#42"
class TestApp extends App implements TestDataSource {}
new TestApp().run(); // prints "data is test#42"
But it is in no way better than the plain/old approach
interface DataSource
{
String lookup(long id);
}
class RealDataSource implements DataSource
{
String lookup(long id){ return "real#"+id; }
}
class TestDataSource implements DataSource
{
String lookup(long id){ return "test#"+id; }
}
class App
{
final DataSource ds;
App(DataSource ds){ this.ds=ds; }
void run(){ print( "data is " + ds.lookup(42) ); }
}
new App(new RealDataSource()).run(); // prints "data is real#42"
new App(new TestDataSource()).run(); // prints "data is test#42"
I did a small proof-on-concept on this recently. You can see the blog post here: http://thoredge.blogspot.no/2013/01/cake-pattern-in-jdk8-evolve-beyond.html and the github repo here: https://github.com/thoraage/cake-db-jdk8
Basically you can do it, but you face at least two obstacles that makes it less slick than Scala. Firstly the Scala traits can have state and Java's interface can't. Many modules need state. This can be fixed by creating a general state component to hold this information, but this will need to be in a class. At least in part. Second issue is that a nested class in an interface is more akin to a static nested class in class. So you can't access the interfaces methods directly from the module class. The default interface method have access to this scope and can add this to the constructor of the module class.
A few experiments suggest no:
Nested classes are automatically static. This is inherently uncakelike:
interface Car {
class Engine { }
}
// ...
Car car = new Car() { };
Car.Engine e = car.new Engine();
error: qualified new of static class
Car.Engine e = car.new Engine();
So, apparently, are nested interfaces, although it's harder to coax out the error messages:
interface Car {
interface Engine { }
}
// ...
Car car = new Car() { };
class Yo implements car.Engine {
}
error: package car does not exist
class Yo implements car.Engine {
// ...
class Yo implements Car.Engine {
}
// compiles ok.
So, without instance member classes, you do not have path dependent types, which is
basically necessary for the cake pattern. So at least, no, not in the straightforward way,
it is not possible.
Ignoring the new functionality in Java 8 you can in theory do the Cake Pattern in Java 5 and above using compile time AspectJ ITDs.
AspectJ DTO's allow you to make Mixins. The only annoying thing is that you will have to make two artifacts: the aspect (ITD) and the interface. However ITDs allow you to do some crazy stuff like add annotations to classes that implement an interface.