How to design a Java fluent DSL API without static methods - java

There are several ways how to define a Java fluent API:
Use static classes/methods + an internal ThreadLocal to get the DSL instance
Use setter/getter based methods to chain the API. Personally I don't like the readability of it, e.g. see some Spring Security code examples.
Is there a way to create a Java DSL that is more readable, like fluent methods in Kotlin? For exmaple a Computer class with Attributes like cpu/gpu and nested DSL classes like drives (Note: I want to nest ssd and hdd even further, e.g. define partitions?
public class Main {
public static void main(String[] arguments) {
Computer computer = Computer.build("Gaming PC").cpu("AMD 5950X").gpu("Lol rly?");
computer.drives(() {
ssd("1TB", "m2");
hdd("10TB", "SATA");
});
computer.print();
}
public static class Computer {
private final Map<String, String> attributes;
private Computer() {
this.attributes = new LinkedHashMap<>();
}
public static Computer build(String name) {
Computer computer = new Computer();
computer.attributes.put("name", name);
return computer;
}
public Computer cpu(String modelName) {
attributes.put("cpu", modelName);
return this;
}
public Computer gpu(String modelName) {
attributes.put("gpu", modelName);
return this;
}
// TODO: Add ssd and hdd. How?
public void print() {
for (Map.Entry<String, String> entry : attributes.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
}
}
The example above won't compile. But is there a way to achieve it with regular classes/abstract classes or even interfaces [with default methods?]. Like this:
public class Main {
public static void main(String[] arguments) {
Computer computer = Computer.build("Gaming PC").cpu("AMD 5950X").gpu("Lol rly?");
computer.drives((new DriveManager() {
ssd("1TB", "m2");
hdd("10TB", "SATA");
});
computer.print();
}
public static class DriveManager {
private Computer computer;
public void setComputer(Computer computer) {
this.computer = computer;
}
public void ssd(String size, String interfaceType) {
computer.ssd(size, interfaceType);
}
public void hdd(String size, String interfaceType) {
computer.ssd(size, interfaceType);
}
}
}
But how does one call setComputer?

Using reflection and an anonymous instance can be used.
You only need it for the drives
Computer computer = new Computer("Gaming PC") {
MainboardComponent cpu = new CPU("AMD 5950X");
MainboardComponent gpu = new GPU("Lol rly?");
Drive ssd = new SSD("1TB", "m2");
Drive hdd = new HDD("10TB", "SATA") {
Partition hdd1 = ...;
Partition hdd2 = ...;
};
}
This hides those fields however.
This however is useful for things like grammars where you may use inheritance on several levels (protected fields, predefined rules, I/O ports). It has the advantage that the container base class may provide component classes and protected members.
A pure fluent interface is possible too, by letting the ComputerBuilder have a nested DriveBuilder.
Computer computer = Computer.build("Gaming PC")
.cpu("AMD 5950X")
.gpu("Lol rly?")
.drives()
.ssd("1TB", "m2")
.hdd("10TB", "SATA")
.endDrives()
.end();
The third alternative would be to use a vararg method ComputerBuilder drives(DriveBuilder... dds); (Drive or DriveBuilder).
Computer computer = Computer.build("Gaming PC")
.cpu("AMD 5950X")
.gpu("Lol rly?")
.drives(Drive.ssd("1TB", "m2"), Drive.hdd("10TB", "SATA"));

Related

Is it appropriate to use reflection to create a list of objects of all subclasses?

In my program, different features are divided into different modules, potentially hundreds of modules,
each module is a subclass of an abstract Module class
which look something like this
public abstract class Module {
public final String name;
public Module(String name){
this.name = name;
}
public abstract void execute();
}
with subclasses that look like this
public class Mod1 extends Module{
public Mod1() {
super("Mod1");
}
#Override
public void execute() {
//Do something
}
}
and I need to make a list of instances of all modules but doing it like this is kinda tedious since my program might have hundreds of modules and it might be hard to debug(I might miss a few lol)
private static final List<Module> MODULES = new ArrayList<>();
public void init(){
MODULES.add(new Mod1());
MODULES.add(new Mod2());
MODULES.add(new Mod3());
MODULES.add(new Mod4());
MODULES.add(new Mod5());
}
so I think using reflection might be the way to go but after a quick google search, I see many people don't like reflection in production code, so I come here to ask if this is an appropriate case to use reflection or is there any reason or design change to avoid using reflection in this particular case
edit: the list will be used stuff like rendering modules in gui or calling execute method of the module from other user interface like command(simply find the module with matching name and execute it)
note: this is not how my code actually look like but a highly simplified version that give a similar idea
For a minimal-effort approach, java provides the built-in class java.util.ServiceLoader.
With this class you can obtain all available implementations of a service class (in this case Module) like
ServiceLoader moduleLoader = ServiceLoader.load(Module.class);
for (Module mod : moduleLoader) {
System.out.println(mod.name);
}
Note that I highlighted the term available in the previous sentence. In fact, the ServiceLoader implementation does not perform any advanced class-hierarchy lookup, instead it relies on a specific resource file named equal to the canonical name of the Module class.
META-INF/services/com.example.project.Module
com.example.project.impl1.Module1
com.example.project.impl2.Module2
Where each line of the file references an available implementation of Module.
Now, ServiceLoader is an implementation of the java SPI specification, which does not necessarily have to qualify perfectly for your use case. However, since its idea of service loading and providing is rather simple, you can easily build your own variant.
public class JsonServiceLoader<S> {
private Class<S> service;
private Map<String, String> serviceIdentifiers;
public static <T> JsonServiceLoader load(Class<T> service, ClassLoader cl) {
Map<String, String> serviceIdentifiers = new HashMap<>();
String name = "META-INF/json-services/" + service.getCanonicalName();
// TODO check for null references where necessary
for (Enumeration<URL> resources = cl.getResources(name); resources.hasMoreElements();) {
try (InputStream resource = resources.next().openStream()) {
for (Map.Entry<String, String> identifier : parseJson(resource).entrySet()) {
serviceIdentifers.merge(
identifer.getKey(),
identifer.getValue(),
(value1, value2) -> throw new ServiceConfigurationError(
"duplicate service identifier '" + identifier.getKey() + "'"
);
);
}
}
}
return new JsonServiceLoader<>(service, serviceIdentifiers);
}
private static Map<String, String> parseJson(InputStream resource) {
// TODO parse JSON data from the given stream using your favourite JSON facility
/*
* If you want to use a different style of resources, e.g. XML, this is the only
* location you have to change (you might want to rename 'json-services' though).
*/
return new HashMap<>();
}
private JsonServiceLoader(Class<S> service, Map<String, String> serviceIdentifiers) {
this.service = service;
this.serviceIdentifiers = serviceIdentifiers;
}
public Set<String> getServiceIdentifiers() {
return Collections.unmodifiableSet(serviceIdentifiers.keySet());
}
public S getService(String name) {
String className = serviceIdenfiers.get(name);
if (null == className) {
throw new IllegalArgumentException("invalid service identifier '" + name + "'");
}
// TODO improve error handling
try {
return service.cast(Class.forName(className).newInstance());
} catch(Exception exc) {
throw new ServiceConfigurationError("could not load service '" + name + "'", exc);
}
}
}
Your JSON resources could look like
META-INF/json-services/com.example.project.Module
{
"Mod1" : {
"class" : "com.example.project.impl1.Module1"
},
"Mod2" : {
"class" : "com.example.project.impl2.Module2"
}
}
allowing future extensions.
At this point, the Module class also does not need to know its name (the class member name) anymore, since you can always ask the service loader for an appropriate instance. (If you do so at some place in your code, you will already know for which name you just asked.) If so desired, you can also add more logic to this JsonServiceLoader, such as caching.
Ultimately, it all depends on how much information you want to process around your Module and how much of this information you want the Module implementations to handle and how much of it you want the service framework to handle.
Perhaps you can pass the list into the constructor of the parent class and add subclass itself to the list in the constructor method.
Just like this
public abstract class Module {
public final String name;
public Module(String name, List<Module> list) {
this.name = name;
list.add(this);
}
public abstract void execute();
}
private static final List<Module> MODULES = new ArrayList<>();
public void init(){
new Mod1(MODULES);
}

Design - Global Settings from Consumer Module

I am attempting to make a library. Had some constants in my code which were some kind of different time delays and certain String values across the code. I could extract them to a separate constant class to get something like
Delays.LENGTH_SHORT (seems cleaner)
They are linked to many different classes inside the library code.
Now, the problem in hand is to make it configurable, from the calling consumer (i.e., if the consumer provides the values, then use those, otherwise use the preset values). From the consumer end, I decided to use Builder pattern for creating the config and passing it in the init of the library module (this only happens once in the lifecycle of it).
Is there a way to keep the above syntax and yet accept the config from consumer (the settings are only configured once during init, for all other times, it completely behaves as constant)?
Reading from a file, appeared to be costly.
For constants that can take a value from only a fixed set of values then it is always better to use Java enums instead of integers or strings or other raw data types. They are much better to understand and maintain over the period of time. Default values should ideally be read from a property file to initialize them. But as you have mentioned in your case, you want to avoid the cost of reading from a file for performance reasons. The design question is always open ended and can have multiple approaches. One approach that I recommend can be as below:
public interface Configuration {
public Continent getContinent(); //For fixed set of values use enum
public Integer getPoolSize(); //If the config can take any value then use the corresponding data type directly
public String getDefaultLabel();
}
public enum Continent {
ANTARTICA, AFRICA, ASIA, AUSTRALIA, EUROPE, NORTH_AMERICA, SOUTH_AMERICA;
}
public class ConfigurationBuilder {
private DefaultConfiguration configurationInstance;
private class DefaultConfiguration implements Configuration {
//Ideally the below values should be read from a property file, instead of hard coding it here.
private Integer poolSize = Integer.valueOf(50);
private String defaultLabel = "DEFAULT";
private Continent continent = Continent.ASIA;
#Override
public Continent getContinent() {
return continent;
}
#Override
public Integer getPoolSize() {
return poolSize;
}
#Override
public String getDefaultLabel() {
return defaultLabel;
}
}
public ConfigurationBuilder withContinent(Continent continent) {
this.configurationInstance.continent = continent;
return this;
}
public ConfigurationBuilder withPoolSize(Integer poolSize) {
this.configurationInstance.poolSize = poolSize;
return this;
}
public ConfigurationBuilder withDefaultLabel(String defaultLabel) {
this.configurationInstance.defaultLabel = defaultLabel;
return this;
}
public Configuration build() {
return this.configurationInstance;
}
public ConfigurationBuilder() {
this.configurationInstance = new DefaultConfiguration();
}
public static Configuration buildDefaultConfiguration() {
return new ConfigurationBuilder().build();
}
}
public class Library {
private Configuration configuration;
public void init(Configuration configuration) {
this.configuration = configuration;
}
public void init() {
this.configuration = ConfigurationBuilder.buildDefaultConfiguration();
}
private Library(Configuration config) {
this.init(config);
}
private Library() {
this.init();
}
/**
* Library is not singleton here.
*
*/
public static Library getInstance(Configuration configuration) {
return new Library(configuration);
}
public static Library getInstance() {
return new Library();
}
}
public class Client {
public static void main(String args[]) {
Configuration config = new ConfigurationBuilder()
.withContinent(Continent.AFRICA)
.withPoolSize(20)
.withDefaultLabel("Label")
.build();
Library lib = Library.getInstance();
lib.init(config);
}
}
Please check the Library and Client classes for usage.
- It uses the Builder pattern.
- It has init() and init(Configuration) methods to allow completely relying on Library defaults.
- ConfigurationBuilder supports supplying some or all configuration values to override
- Currently all three config options are overridable - continent, poolSize and defaultLabel. However if some configuration is private to Library then just remove the withXXX method for that property from the Builder.
Hope this fits your need. Good question!

Java Simple Factory with constructors using different parameters

I have two ways of saving data in my application: save to database and save to file. Since I don't want client code dealing with construction of objects I created a class that (to my understanding) is simple factory with a factory method. Code below:
public static DataPersister createDataPersister(Boolean saveToDb, Session session, String filename) {
if (saveToDb) {
return new DatabaseDataPersister(session);
} else {
return new FileDataPersister(filename);
}
}
With this setup client code doesn't have to deal with constructing anything or deciding whether to save to DB or file - it can just call a save() method on an object returned by the factory like so:
DataPersister dataPersister = DataPersisterSimpleFactory.createDataPersister(this.savetoDb, this.session, this.filename);
dataPersister.save(this.data);
My question is - is this solution breaking SOLID principles? In order to create e.g. a DatabaseDataPersister client code needs to pass on a filename parameter, and this implementation of DataPersister won't have any use of it. I feel like it doesn't sit right with something similar to Interface-segregation principle but not quite that.
And if the solution is indeed a code smell - how do I go about cleaning it?
The SOLID principle I think is in violation is DIP.
Your client classes, by having to depend on the static factory directly, have a compile-time dependency on actual implementations, DatabaseDataPersister and FileDataPersister, rather than just the abstraction DataPersister.
To solve, supply to the client the DataPersister you want them to use. The constructor is usually a good place for this:
public class ExampleClient {
private final DataPersister dataPersister;
public ExampleClient(DataPersister dataPersister) {
this.dataPersister = dataPersister;
}
public void methodThatUsesSave(){
dataPersister.save(data);
}
}
This code compiles without the concrete implementations, i.e. it has no dependency on them. The client also doesn't need to know the filename or session so it solves that code smell too.
We can decide which concrete implementation to give it at construction time, here I use your existing method:
DataPersister dataPersister = DataPersisterSimpleFactory.createDataPersister(this.savetoDb, this.session, this.filename);
ExampleClient example = new ExampleClient(dataPersister);
This is a perfect opportunity to use the factory pattern
interface DataPersister {
void persist(String s);
}
private class DatabasePersister implements DataPersister {
final Session session;
public DatabasePersister(Session session) {
this.session = session;
}
#Override
public void persist(String s) {
System.out.println("Persist to database: " + s);
}
}
private class FilePersister implements DataPersister {
final String filename;
public FilePersister(String filename) {
this.filename = filename;
}
#Override
public void persist(String s) {
System.out.println("Persist to file: " + s);
}
}
class PersisterFactory {
public DataPersister createDatabasePersister(Session session) {
return new DatabasePersister(session);
}
public DataPersister createFilePersister(String filename) {
return new FilePersister(filename);
}
}
public void test(String[] args) {
DataPersister databasePersister = new PersisterFactory().createDatabasePersister(new Session());
databasePersister.persist("Hello");
DataPersister filePersister = new PersisterFactory().createFilePersister("Hello");
filePersister.persist("Hello");
}
You already pass a bunch of stuff irrelevant to various persisters.
As it stands you need a method that takes a Session and one that takes a String and you're done. No need for a boolean, no need for useless params. That handles your decision making with no cruft.
Whether or not that's a good idea... I'm ambivalent. You're not saving much; might as well just have a static factory in each type so it's explicit in the code what type you're creating.
Consider what happens when you add a new persister, like a REST endpoint, that would take a URL (could be a string, could be an actual URL). You now need even more useless parameters etc. Or you could pass in a URI from the beginning, e.g., file:// or http:// and get around that problem.
There are any number of ways this could be done–I'm not convinced there's a "clearly correct" answer, and it may boil down to opinion.
Well the right solution here is combining the dependency injection from weston and the factory pattern from OldCurmudgeon.
public class ExampleClient {
private final DataPersister dataPersister;
public ExampleClient(DataPersister dataPersister) {
this.dataPersister = dataPersister;
}
public void methodThatUsesSave(){
dataPersister.save(data);
}
}
class PersisterFactory {
public DataPersister createDatabasePersister(Session session) {
return new DatabasePersister(session);
}
public DataPersister createFilePersister(String filename) {
return new FilePersister(filename);
}
}
The upper level code:
PersisterFactory = new PersisterFactory();
DataPersister dataPersister;
if (saveToDb)
dataPersister = PersisterFactory.createDatabasePersister(new Session());
else
dataPersister = PersisterFactory.createFilePersister("Hello");
ExampleClient example = new ExampleClient(dataPersister);
Usually the dataPersister comes from the DI container and the saveToDb comes from the config, but of course testing can be an exception.

factory object creation using per-subclass method

I have a simple Factory (GenericFudge) that creates objects of different types depending on external circumstances. Currently, my code looks something like this:
abstract class Fudge {
Fudge() {
}
void make() {
System.out.println("made.");
}
}
class VanillaFudge extends Fudge {
#Override
void make() {
System.out.print("Vanilla ");
super.make();
}
}
class ChocolateFudge extends Fudge {
#Override
void make() {
System.out.print("Chocolate ");
super.make();
}
}
class InvalidFlavorException extends Exception {};
// factory / proxy
public class GenericFudge {
Fudge mFudge = null;
GenericFudge(String flavor) throws InvalidFlavorException {
if (flavor.equals("Chocolate")) {
mFudge = new ChocolateFudge();
} else if (flavor.equals("Vanilla")) {
mFudge = new VanillaFudge();
}
}
void make() {
mFudge.make();
}
public static void main(String args[]) {
for (String flavor : new String[] {"Chocolate", "Vanilla"}) {
GenericFudge fudge;
try {
fudge = new GenericFudge(flavor);
fudge.make();
} catch (InvalidFlavorException e) {
System.out.println("Sorry, we don't make that flavor");
}
}
}
}
My goal is to get the details of chocolate and vanilla out of GenericFudge, so that when CaramelFudge is implemented, no changes to GenericFudge are required. For example, GenericFudge would iteratively call a "createIfItsMyFlavor()" method for every xxxFudge class. (In my actual application, I have to try them iteratively, but I'd be interested in other possibilities.)
My instinct was to use a static initializer per subclass (per xxxFudge) that adds "itself" to a list by calling a registerFudge method of GenericFudge, but this hits the chicken-and-egg problem (the class is never used, so its static initializer never gets invoked).
No doubt there's a better way I haven't envisioned. Thanks!
If you are using any kind of dependency injection system like Spring, this is easy to implement using #PostConstruct. If this works, then you can call a register method in GenericFudge from the method you annotate with PostConstruct. In GenericFudge, you have a map, and whenever addType is called you add it to the map. That way your GenericFudge remains unchanged, and new callers will register using PostConstruct. To simplify things further, you can define this in your base class Fudge, and pass the fudge name using the constructor, that way you don't have to declare the register method in each sub-class.
private String fudge;
public Fudge(final String fudge) {
this.fudge = fudge;
}
#Autowired
private GenericFudge fudge;
#PostConstruct
private void register() {
fudge.addType(fudge, this);
}
In GenericFudge
private Map<String, Fudge> fudgeTypes = Maps.newHashMap();
public void register(final String fudgeType, final Fudge fudgeInstance) {
fudgeTypes.put(fudgeType, fudgeInstance);
}
If you do not use any dependency injection system:
Another approach could be to have a static method in the base class Fudge, where you declare all the types of fudge and then return an instance based on the request. That way you don't modify the GenericFudge class, but only the base class of Fudge. This is not ideal, but it gets you away from having to modify the GenericFudge class, and instead of "registering" with something like PostConstruct, you put an entry into the Map.
Example (ImmutableMap from Guava, you can declare the map however you like , this is only for the example):
public abstract class Fudge {
private static final Map<String, Fudge> FUDGE_TYPES = ImmutableMap.of(
"Type1", new Type1Fudge(),
"Type2", new Type2Fudge()
// Add new entry when implemented
);
public static Fudge getFudge(final String fudge) {
if (FUDGE_TYPES.containsKey(fudge)) {
return FUDGE_TYPES.get(fudge);
} else {
// handle missing fudge depending on your preference
}
}
}

Array of function pointers in Java [duplicate]

This question already has answers here:
How to call a method stored in a HashMap? (Java) [duplicate]
(3 answers)
Closed 8 years ago.
I have read this question and I'm still not sure whether it is possible to keep pointers to methods in an array in Java. If anyone knows if this is possible (or not), it would be a real help. I'm trying to find an elegant solution of keeping a list of Strings and associated functions without writing a mess of hundreds of if statements.
Cheers
Java doesn't have a function pointer per se (or "delegate" in C# parlance). This sort of thing tends to be done with anonymous subclasses.
public interface Worker {
void work();
}
class A {
void foo() { System.out.println("A"); }
}
class B {
void bar() { System.out.println("B"); }
}
A a = new A();
B b = new B();
Worker[] workers = new Worker[] {
new Worker() { public void work() { a.foo(); } },
new Worker() { public void work() { b.bar(); } }
};
for (Worker worker : workers) {
worker.work();
}
You can achieve the same result with the functor pattern. For instance, having an abstract class:
abstract class Functor
{
public abstract void execute();
}
Your "functions" would be in fact the execute method in the derived classes. Then you create an array of functors and populate it with the apropriated derived classes:
class DoSomething extends Functor
{
public void execute()
{
System.out.println("blah blah blah");
}
}
Functor [] myArray = new Functor[10];
myArray[5] = new DoSomething();
And then you can invoke:
myArray[5].execute();
It is possible, you can use an array of Method. Grab them using the Reflection API (edit: they're not functions since they're not standalone and have to be associated with a class instance, but they'd do the job -- just don't expect something like closures)
Java does not have pointers (only references), nor does it have functions (only methods), so it's doubly impossible for it to have pointers to functions. What you can do is define an interface with a single method in it, have your classes that offer such a method declare they implement said interface, and make a vector with references to such an interface, to be populated with references to the specific objects on which you want to call that method. The only constraint, of course, is that all the methods must have the same signature (number and type of arguments and returned values).
Otherwise, you can use reflection/introspection (e.g. the Method class), but that's not normally the simplest, most natural approach.
I found the reflection approach the cleanest -- I added a twist to this solution since most production classes have nested classes and I didn't see any examples that demonstrates this (but I didn't look for very long either). My reason for using reflection is that my "updateUser()" method below had a bunch of redundant code and just one line that changed (for every field in the user object) in the middle that updated the user object:
NameDTO.java
public class NameDTO {
String first, last;
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
}
UserDTO.java
public class UserDTO {
private NameDTO name;
private Boolean honest;
public UserDTO() {
name = new NameDTO();
honest = new Boolean(false);
}
public NameDTO getName() {
return name;
}
public void setName(NameDTO name) {
this.name = name;
}
public Boolean getHonest() {
return honest;
}
public void setHonest(Boolean honest) {
this.honest = honest;
}
}
Example.java
import java.lang.reflect.Method;
public class Example {
public Example () {
UserDTO dto = new UserDTO();
try {
Method m1 = dto.getClass().getMethod("getName", null);
NameDTO nameDTO = (NameDTO) m1.invoke(dto, null);
Method m2 = nameDTO.getClass().getMethod("setFirst", String.class);
updateUser(m2, nameDTO, "Abe");
m2 = nameDTO.getClass().getMethod("setLast", String.class);
updateUser(m2, nameDTO, "Lincoln");
m1 = dto.getClass().getMethod("setHonest", Boolean.class);
updateUser(m1, dto, Boolean.TRUE);
System.out.println (dto.getName().getFirst() + " " + dto.getName().getLast() + ": honest=" + dto.getHonest().toString());
} catch (Exception e) {
e.printStackTrace();
}
}
public void updateUser(Method m, Object o, Object v) {
// lots of code here
try {
m.invoke(o, v);
} catch (Exception e) {
e.printStackTrace();
}
// lots of code here -- including a retry loop to make sure the
// record hadn't been written since my last read
}
public static void main(String[] args) {
Example mp = new Example();
}
}
You are right that there are no pointers in java because a reference variables are the same as the & syntax in C/C++ holding the reference to the object but no * because the JVM can reallocate the heap when necessary causing the pointer to be lost from the address which would cause a crash. But a method is just a function inside a class object and no more than that so you are wrong saying there are no functions, because a method is just a function encapsulated inside an object.
As far as function pointers, the java team endorses the use of interfaces and nested classes which all fine and dandy, but being a C++/C# programmer who uses java from time to time, I use my Delegate class I made for java because I find it more convenient when I need to pass a function only having to declare the return type of the method delegate.
It all depends on the programmer.
I read the white pages on why delegates are not support but I disagree and prefer to think outside the box on that topic.

Categories

Resources