I have a class contains 10 methods which are doing almost the same things apart from one key event. Two examples are given below:
Public String ATypeOperation(String pin, String amount){
doSomething();
doMoreStuff();
requestBuilder.buildATypeRequest(pin, amount);
doAfterStuff();
}
Public String BTypeOperation(String name, String sex, String age){
doSomething();
doMoreStuff();
requestBuilder.buildBTypeRequest(name, sex, age);
doAfterStuff();
}
As you can see from the above methods, they are similar apart from calling different methods provided by requestBuilder. The rest 8 are similar too. There is a lot duplicated code here. I feel there is a better way to implement this, but don’t know how. Any ideas and suggestions are appreciated.
Thanks,
Sarah
Use something like RequestBuilder, that accepts all these kinds of parameters:
public RequestBuilder {
// setters and getters for all properties
public Request build() {
doStuff();
Request request = new Request(this);
doAfterStuff();
return request;
}
}
and then
new RequestBuilder().setAge(age).setName(name).build();
What’s the nearest substitute for a function pointer in Java?
Function Pointers in Java
interface RequestBuilder {
void doStuff(params);
}
public RequestBuilder getARequestBuilder() {
return new RequestBuilder() {
void doStuff(params) {
// impl.details
}
}
}
public RequestBuilder getBRequestBuilder() {
return new RequestBuilder() {
void doStuff(params) {
// impl.details
}
}
}
public String buildRequest(yourParams, RequestBuilder builder){
doBefore();
builder.doStuff(yourParams);
doAfter();
}
I think this is called the Strategy pattern. It looks a lot like the Command pattern but because you encapsulate an algorithm it seems to be Strategy :)
What Bozho suggest is the Builder pattern.
I recommend you browse through a list of patterns some time, or buy Head First Patterns. Really fun reading.
You could pass the builder object to a generic buildRequest method. Since not only the algorithm but also the arguments vary, i put them into the builder. I dont think thats a nice solution but i wanted to show a command pattern here :D (Extraneon showed how to decouple params and command)
// call somewhere in the code:
Builder b = new BTypeBuilder();
b.age = "20"; b.sex = "female"; b.name = "eve";
String res = buildRequest(b);
Public String buildRequest(Builder builder)
{
doSomething();
doMoreStuff();
builder.build();
doAfterStuff();
}
// Command pattern
class BTypeBuilder implements Builder
{
String name, age, sex;
// Constructor here
void build()
{
// Do your stuff here
}
}
class ATypeBuilder implements Builder
{
String pin, amount;
// Constructor here
void build()
{
// Do your stuff here
}
}
public interface Builder
{
void build();
}
In addition to other answers, this might also be useful for you (If you want to just plugin your method, not using your parameters for 'before' and 'after' methods)
interface Function0<R> {
R apply();
}
public void performOperation(Function0<Void> operation) {
doSomething();
doBeforeStuff();
operation.apply();
doAfterStuff();
}
then you could use it like this,
final RequestBuilder builder = new RequestBuilder();
performOperation(new Function0<Void>() {
public Void apply() {
builder.buildATypeRequest("1234", "2445");
return null;
}
});
performOperation(new Function0<Void>() {
public Void apply() {
builder.buildBTypeRequest("1234", "2445", "1234");
return null;
}
});
Instead of sending a long parameter list just push all the parameters in a map and send that map as argument.
Related
I'm trying to pass new Object as method parameter and set it's variable at the same time.
This an okay solution but big and not nice...
EventBox evtbox = new EventBox();
evtbox.setFloorColor(floorColor);
scriptUtils.runScript("sc-cfrmd",evtbox);
and I need something like this to shorten it up a bit
scriptUtils.runScript("sc-cfrmd",new EventBox().setFloorColor(floorColor));
of course, logical way of doing this is just creating a constructor in EventBox class but I need to figure out way without use of constructors.
any tips are appreciated
You are looking for fluent interface
Usually, when you need to create a complex object you are implementing fluent interface with builder design pattern
For example:
import java.awt.Color;
public class EventBox {
private Color floorColor;
private EventBox() {
this.floorColor = null;
}
public Color getFloorColor() {
return floorColor;
}
private void setFloorColor(Color floorColor) {
this.floorColor = floorColor;
}
public static EventBoxBuilder builder() {
return new EventBoxBuilder();
}
public static class EventBoxBuilder {
private final EventBox box;
EventBoxBuilder() {
this.box = new EventBox();
}
public EventBoxBuilder setFloorColor(Color color) {
box.setFloorColor(color);
return this;
}
public EventBox build() {
return box;
}
}
}
....
scriptUtils.runScript("sc-cfrmd",EventBox.builder().setFloorColor(floorColor).build());
If you are able to use Lombok Framwork, such builder can be automatically generated on compile time by adding #Builder annotation to the EventBox class
You can use method chaining by adding methods as desired. Conventionally, leave the setters/getters as the standard practice to just do what their name says.
public EventBox withFloorColor(String floorColor) {
setFloorColor(floorColor);
return this;
}
scriptUtils.runScript("sc-cfrmd",new EventBox().withFloorColor(floorColor));
The most basic and simple solution is of course to create a method like
EventBox createFloorEventBox( String floorColor ) {
EventBox eb = new EventBox();
eb.setFloorColor( floorColor );
return eb;
}
and use it like
scriptUtils.runScript("sc-cfrmd", createEventBox( floorColor ) );
Besides the builder pattern/method chaining/fluent approach, you could also consider using lambdas in Java, like
void runScript(String something, Consumer<EventBox> boxInitializer) {
EventBox eb = new EventBox();
initializer.apply(eb);
...
}
and then call this like
runScript("something", eb -> eb.setFloorColor( floorColor ));
I'm new to RxJava, and I am wondering how I can create a configurable Observable? Let's imagine I could write a DB-to-DB transfer like this:
srcDb.getObservable(Bean.class)
.sql(selectSql)
.params(selectParams)
.subscribe(
trgDb.getSubscriber(Bean.class)
.sql(insertSql)
);
I can already do that with the Subscriber, but how can I get some small configuration in the same fashion to the Observable itself?
There's 2 ways you can do that:
Option #1: have your own objects do the configuration, and then have an execute(), query() or toObservable() that switches domains:
srcDb
.find(Bean.class)
.sql(selectSql)
.params(selectParams)
.execute()
.subscribe(
trgDb.getSubscriber(Bean.class)
.sql(insertSql)
);
Option #2: use .compose() to re-use common operations:
srcDb
.getObservable(Bean.class)
.compose(addSQLParameters())
.subscribe(
trgDb.getSubscriber(Bean.class)
.sql(insertSql)
);
<T> Transformer<T,T> addSQLParameters() {
return obs -> obs.sql(selectSql).params(selectParams);
}
I would suggest you use option #1, as it allows much better management of your part of the code.
Maybe I found an acceptable way around this. It seems that what I need to do here is a double-binding outside of the Observable instantiation itself. E.g. I need a DbObservable and DbOnSubscribe pair which is counting on each other, something like this:
DbObservable class:
public class DbObservable<T> extends Observable<T> {
//Some parameter
private String sql;
protected DbObservable(DbOnSubscribe<T> onSub) {
super(onSub);
}
//Getter for DbOnSubscribe
public String getSql() {
return sql;
}
//Chain parameter modifier
public DbObservable<T> sql(String sql) {
this.sql = sql;
return this;
}
}
DbOnSubscribe class:
public class DbOnSubscribe<T> implements Observable.OnSubscribe<T> {
private DbObservable<T> dbObservable;
#Override
public void call(Subscriber<? super T> subscriber) {
String sql = dbObservable.getSql(); //Access SQL param
subscriber.onNext( (T) sql ); //Use subscriber
subscriber.onCompleted();
}
//Set back-reference
public void setDbObservable(DbObservable<T> dbObservable) {
this.dbObservable = dbObservable;
}
}
And finally our assumed DbConnector class:
public class DbConnector {
public DbObservable<String> getObservable() {
DbOnSubscribe<String> onSub = new DbOnSubscribe<String>();
DbObservable<String> obs = new DbObservable<>(onSub);
onSub.setDbObservable(obs);
return obs;
}
}
So when I try it out ...
public class DbObservableTest {
public static void main(String[] args) {
DbConnector srcDb = new DbConnector();
srcDb.getObservable()
.sql("some SQL")
.subscribe(System.out::println);
}
}
... it really works! It prints out the "some SQL".
Conclusion
If you want to be super-clean and don't mind one or 2 extra lines of code, go for a builder as proposed by Joel and Tassos Bassoukos.
If you're not afraid of a little bit more complicated code (which should be always encapsulated somewhere) and you really want those parameters to be inside your own Observable, you can try the double-binding way
Any more options?
I have a if else statement which might grow in the near future.
public void decide(String someCondition){
if(someCondition.equals("conditionOne")){
//
someMethod("someParameter");
}else if(someCondition.equals("conditionTwo")){
//
someMethod("anotherParameter");
}
.
.
else{
someMethod("elseParameter");
}
}
Since, this is already looking messy, I think it would be better if I can apply any design patterns here. I looked into Strategy pattern but I am not sure if that will reduce if else condition here. Any suggestions?
This is a classic Replace Condition dispatcher with Command in the Refactoring to Patterns book.
Basically you make a Command object for each of the blocks of code in your old if/else group and then make a Map of those commands where the keys are your condition Strings
interface Handler{
void handle( myObject o);
}
Map<String, Handler> commandMap = new HashMap<>();
//feel free to factor these out to their own class or
//if using Java 8 use the new Lambda syntax
commandMap.put("conditionOne", new Handler(){
void handle(MyObject o){
//get desired parameters from MyObject and do stuff
}
});
...
Then instead of your if/else code it is instead:
commandMap.get(someCondition).handle(this);
Now if you need to later add new commands, you just add to the hash.
If you want to handle a default case, you can use the Null Object pattern to handle the case where a condition isn't in the Map.
Handler defaultHandler = ...
if(commandMap.containsKey(someCondition)){
commandMap.get(someCondition).handle(this);
}else{
defaultHandler.handle(this);
}
Let's assume that we have such code (which is the same as yours):
public void decide(String someCondition) {
if(someCondition.equals("conditionOne")) {
someMethod("someParameter");
}
else if(someCondition.equals("conditionTwo")) {
someMethod("anotherParameter");
}
else {
someMethod("elseParameter");
}
}
Assuming that you don't want to refactor other parts of the application and you don't want to change method signature there are possible ways in which it could be refactored:
Warning - You should use generic versions of mentioned patterns.
I showed non generic ones because it is easier to read them.
Strategy + Factory Method
We can use Strategy and Factory Method patterns. We also take advantage of polymorphism.
private final StrategyConditionFactory strategyConditionFactory = new StrategyConditionFactory();
public void decide(String someCondition) {
Strategy strategy = strategyConditionFactory.getStrategy(someCondition)
.orElseThrow(() -> new IllegalArgumentException("Wrong condition"));
strategy.apply();
}
It would be better to design it in a way that else condition is included in the factory, and developer calls it on purpose. In such case we throw exception when condition is not meet. Alternatively we could write it exactly as it was in question. If you want so instead of .orElseThrow(() -> new IllegalArgumentException("Wrong condition")); put .orElse(new ElseStrategy());
StrategyConditionFactory (factory method):
public class StrategyConditionFactory {
private Map<String, Strategy> conditions = new HashMap<>();
public StrategyConditionFactory() {
conditions.put("conditionOne", new ConditionOneStrategy());
conditions.put("conditionTwo", new ConditionTwoStrategy());
//It is better to call else condition on purpose than to have it in the conditional method
conditions.put("conditionElse", new ElseStrategy());
//...
}
public Optional<Strategy> getStrategy(String condition) {
return Optional.ofNullable(conditions.get(condition));
}
}
Strategy interface:
public interface Strategy {
void apply();
}
Implementations:
public class ConditionOneStrategy implements Strategy {
#Override
public void apply() {
//someMethod("someParameter");
}
}
public class ConditionTwoStrategy implements Strategy {
#Override
public void apply() {
//someMethod("anotherParameter")
}
}
public class ElseStrategy implements Strategy {
#Override
public void apply() {
//someMethod("elseParameter")
}
}
Usage (simplified):
public void strategyFactoryApp() {
//...
decide("conditionOne");
decide("conditionTwo");
decide("conditionElse");
//...
}
Strategy + Factory Method - this particular case (where only parameter changes)
We can use the fact that in this case we always call the same method, only parameter changes
We change our base strategy interface to abstract class with getParameter() method and we make new implementations of this abstract class. Other code remains the same.
public abstract class Strategy {
public abstract String getParameter();
public void apply() {
someMethod(getParameter());
}
private void someMethod(String parameter) {
//someAction
}
}
Implementations:
public class CondtionOneStrategy extends Strategy {
#Override
public String getParameter() {
return "someParameter";
}
}
public class CondtionTwoStrategy extends Strategy {
#Override
public String getParameter() {
return "anotherParameter";
}
}
public class ElseStrategy extends Strategy {
#Override
public String getParameter() {
return "elseParameter";
}
}
Enum + enum kinda "factory"
We might use Enum to implement strategy and instead of factory method we can use valueOf() from enum.
public void decide(String someCondition) {
ConditionEnum conditionEnum = ConditionEnum.valueOf(someCondition);
conditionEnum.apply();
}
Condition enum:
public enum ConditionEnum {
CONDITION_ONE {
#Override
public void apply() {
//someMethod("someParameter");
}
},
CONDITION_TWO {
#Override
public void apply() {
//someMethod("anotherParameter");
}
},
CONDITION_ELSE {
#Override
public void apply() {
//someMethod("elseParameter");
}
};
//...more conditions
public abstract void apply();
}
Usage (simplified):
public void enumFactoryApp() {
//...
decide("CONDITION_ONE");
decide("CONDITION_TWO");
decide("CONDITION_ELSE");
//...
}
Notice that you will get IllegalArgumentException when enum type has no constant with the specified name.
Command + Factory
The difference between strategy and command is that command holds also state, so if you have for example compute(int a, int b, String someCondition) and you want to refactor it with strategy including it's signature change you can reduce it to compute(int a, int b, ComputeStrategy computeStrategy) with command you can reduce it to one argument compute(ComputeCommand computeCommand). In this case we also take advantage of polymorphism similarly to strategy pattern case.
CommandConditionFactory commandConditionFactory = new CommandConditionFactory();
public void decide(String someCondition) {
Command command = commandConditionFactory.getCommand(someCondition)
.orElseThrow(() -> new IllegalArgumentException("Wrong condition"));
command.apply();
}
It would be better to design it in a way that else condition is included in the factory, and developer calls it on purpose. In such case we throw exception when condition is not meet. Alternatively we could write it exactly as it was in question. If you want so instead of .orElseThrow(() -> new IllegalArgumentException("Wrong condition")); put .orElse(new ElseCommand());
CommandConditionFactory (factory method):
public class CommandConditionFactory {
private Map<String, Command> conditions = new HashMap<>();
public CommandConditionFactory() {
conditions.put("conditionOne", new ConditionOneCommand("someParameter"));
conditions.put("conditionTwo", new ConditionTwoCommand("anotherParameter"));
//It is better to call else condition on purpose than to have it in the conditional method
conditions.put("conditionElse", new ElseCommand("elseParameter"));
//...
}
public Optional<Command> getCommand(String condition) {
return Optional.ofNullable(conditions.get(condition));
}
}
Command interface:
public interface Command {
void apply();
}
Implementations (there is some redundancy, but It is there to show how command should look in more general case where instead of someMethod() we have three different methods):
public class ConditionOneCommand implements Command {
private final String parameter;
public ConditionOneCommand(String parameter) {
this.parameter = parameter;
}
#Override
public void apply() {
//someMethod(parameter);
}
}
public class ConditionTwoCommand implements Command {
private final String parameter;
public ConditionTwoCommand(String parameter) {
this.parameter = parameter;
}
#Override
public void apply() {
//someMethod(parameter);
}
}
public class ElseCommand implements Command {
private final String parameter;
public ElseCommand(String parameter) {
this.parameter = parameter;
}
#Override
public void apply() {
//someMethod(parameter);
}
}
Usage (simplified):
public void commandFactoryApp() {
//...
decide("conditionOne");
decide("conditionTwo");
decide("conditionElse");
//...
}
Command + Factory - This particular case.
This in fact isn't a real command pattern just a derivative. It takes advantage of the fact that in this case we are always calling the same method someMethod(parameter) and only the parameter changes.
Abstract class:
public abstract class Command {
abstract void apply();
protected void someMethod(String parameter) {
//someAction
}
}
Implementation (the same for all 3 conditional cases):
public class CommandImpl extends Command {
private final String parameter;
public CommandImpl (String parameter) {
this.parameter = parameter;
}
#Override
public void apply(){
someMethod(parameter);
}
}
Factory, please notice that there is only one command implementation, only parameter changes:
public class CommandConditionFactory {
Map<String, Command> conditions = new HashMap<>();
public CommandConditionFactory() {
conditions.put("conditionOne", new CommandImpl("someParameter"));
conditions.put("conditionTwo", new CommandImpl("anotherParameter"));
//It is better to call else condition on purpose than to have it in the conditional method
conditions.put("conditionElse", new CommandImpl("elseParameter"));
//...
}
public Optional<Command> getCommand(String condition) {
return Optional.ofNullable(conditions.get(condition));
}
}
Nested if's
Note that even if you have nested ifs sometimes it is possible to refactor them and use one of the mentioned techniques.
Lets say that we have following code:
public void decide2(String someCondition, String nestedCondition) {
if(someCondition.equals("conditionOne")) {
if(nestedCondition.equals("nestedConditionOne")){
someLogic1();
}
else if(nestedCondition.equals("nestedConditionTwo")){
someLogic2();
}
}
else if(someCondition.equals("conditionTwo")) {
if(nestedCondition.equals("nestedConditionThree")){
someLogic3();
}
else if(nestedCondition.equals("nestedConditionFour")){
someLogic4();
}
}
}
You could refactor it using mathematical logic rules:
public void decide2(String someCondition, String nestedCondition) {
if(someCondition.equals("conditionOne")
&& nestedCondition.equals("nestedConditionOne")) {
someLogic1();
}
else if(someCondition.equals("conditionOne")
&& nestedCondition.equals("nestedConditionTwo")) {
someLogic2();
}
else if(someCondition.equals("conditionTwo")
&& nestedCondition.equals("nestedConditionThree")) {
someLogic3();
}
else if(someCondition.equals("conditionTwo")
&& nestedCondition.equals("nestedConditionFour")) {
someLogic4();
}
}
and then you can use strategy, enum or command. You just have a pair of Strings <String, String> instead of single String.
Decision Tables
When you have nested ifs that couldn't be refactored as mentioned you can implement your own decision tables or use some ready to go decision tables solution. I won't give the implementation there.
Rules Engine
When you have nested ifs that couldn't be refactored as mentioned you can also implement your own simple rules engine. You should use it only if you have many nested ifs, otherwise it is triumph of form over content.
For very complicated Business Logic there are professional Rule Engines like Drools.
I won't give the implementation there.
One more thing
In the example that you gave there is a high possibility that someone introduced these ifs, but they are totally redundant. And we can check it by trying to refactor decide method signature to make it take some other argument and to refactor surrounding code that is calling our method. By doing so we are getting rid of our Factory Method. There are examples that present how the code might look when it occurs that these ifs were redundant.
Strategy
Decide method:
public void decide(Strategy strategy) {
strategy.apply();
}
Usage (simplified):
public void strategyApp() {
//...
decide(new ConditionOneStrategy());
decide(new ConditionTwoStrategy());
decide(new ElseStrategy());
//...
}
Enum
Decide method:
public void decide(ConditionEnum conditionEnum) {
conditionEnum.apply();
}
Usage (simplified):
public void enumApp() {
//...
decide(ConditionEnum.CONDITION_ONE);
decide(ConditionEnum.CONDITION_TWO);
decide(ConditionEnum.CONDITION_ELSE);
//...
}
Command
Decide method:
public void decide(Command command) {
command.apply();
}
Usage (simplified):
public void commandApp() {
//...
decide(new ConditionOneCommand("someParameter"));
decide(new ConditionTwoCommand("anotherParameter"));
decide(new ElseCommand("elseParameter"));
//...
}
In fact it is quite specific case, there are cases in which for example we have to use simple type like String, because it comes from the external system or condition is based on integer from input so we can't refactor the code so easily.
The general recommendation by Martin Fowler is to
Replace Conditional with Polymorphism.
In terms of design patterns this would often be the Strategy Pattern
Replace Conditional Logic with Strategy.
If you have a small, finite set of conditions, I recommend to use an enum to implement the Strategy Pattern (provide an abstract method in the enum and override it for each constant).
public enum SomeCondition{
CONDITION_ONE{
public void someMethod(MyClass myClass){
//...
}
},
CONDITION_TWO{
public void someMethod(MyClass myClass){
}
}
public abstract void someMethod(MyClass myClass);
}
public class MyClass{
//...
public void decide(SomeCondition someCondition){
someCondition.someMethod(this);
}
}
If it's really just a parameter you want to pick, then you could define the enum like this instead:
public enum SomeCondition{
CONDITION_ONE("parameterOne"),
CONDITION_TWO("parameterTwo");
private final String parameter;
private SomeCondition(String parameter){
this.parameter = parameter;
}
public String getParameter(){
return parameter;
}
}
public class MyClass{
//...
public void decide(SomeCondition someCondition){
someMethod(someCondition.getParameter());
}
}
Another way to solve the current problem is to use Factory Pattern. This provides functionality to extract a factory method that returns an object of a given type and performs the operation based on the concrete object behavior.
public interface Operation {
String process(String a, String b);
}
The method takes two string as input and returns the result.
public class Concatenation implements Operation {
#Override
public String process(String a, String b) {
return a.concat(b);
}
}
public class Join implements Operation {
#Override
public String process(String a, String b) {
return String.join(", ", a, b);
}
}
And then we should define a factory class which returns instances of Operation based on the given operator:
public class OperatorFactory {
static Map<String, Operation> operationMap = new HashMap<>();
static {
operationMap.put("concatenation", new Concatenation());
operationMap.put("join", new Join());
// more operators
}
public static Optional<Operation> getOperation(String operator) {
return Optional.ofNullable(operationMap.get(operator));
}
}
And now we can use it:
public class SomeServiceClass {
public String processUsingFactory(String a, String b, String operationName) {
Operation operation = OperatorFactory
.getOperation(operationName)
.orElseThrow(() -> new IllegalArgumentException("Invalid Operation"));
return operation.process(a, b);
}
}
I guess you must have already considered it, but if you are using JDK 7 or above, you can switch on strings. That way your code can look cleaner than a bunch of if-else statements.
This is the code of the method that I want to simplify. The method name I call of SerializedExpFamMixture class is exactly the value of "model", my question is how to assign the value of "model" directly as the name of the method instead of using "if" to determine which method I should call. Since by using "if", I need to list all the possible values of "model" and judge which method I should use.
Thank you very much for help. I am new to java.
public static SerializedExpFamMixture RateMtxModel(String model)
{
SerializedExpFamMixture result=new SerializedExpFamMixture();
if(model=="kimura1980()")
result=SerializedExpFamMixture.kimura1980();
if(model=="accordance()")
result=SerializedExpFamMixture.accordance();
if(model=="pair()")
result=SerializedExpFamMixture.pair();
return result;
}
One way you can approach this is to use Reflection:
Method method = myClass.getClass().getMethod("doSomething", null);
method.invoke(myClass, null);
Since you are new to Java, it's time for some general pointers:
In Java, we usually name our methods with camelCase, so the first letter is lower case.
Also, in Java we usually leave the opening curly-bracket on the same line as the code (no newline).
Always use final on your variables. At least your parameters. That way you won't overwrite it, and thus won't have to try to figure out which value it actually has at runtime.
Use curly-brackets! Please!
The result variable is not actually needed.
Use the equals-method to compare Strings.
If you only want one result, use else-if
Fixing these things, your method looks like this:
public static SerializedExpFamMixture rateMtxModel(String model) {
if (model.equals("kimura1980()")) {
return SerializedExpFamMixture.kimura1980();
} else if (model.equals("accordance()")) {
return SerializedExpFamMixture.accordance();
} else if(model.equals("pair()")) {
return SerializedExpFamMixture.pair();
}
return new SerializedExpFamMixture();
}
Next, let's look at what you are actually trying to do here. You want to pass some Strings around, and use them as a basis for creating objects. And now, with the advice given here, you will do this using reflection. This does not sound like a very good idea to me. Say you were to go through with this, and this happened:
rateMtxModel("kinura1980");
Small typo, hard to spot, will give unexpected results. If you were actually calling a method the compiler would let you know that you messed up, now you will get no warning (btw did you see both errors in that method call?). The same if someone were to delete the accordance()-method, the compiler would not alert them that this will break the program.
If it was up to be I would just use the static factory-methods in SerializedExpFamMixture directly, but if you have to do it like this (if the task at hand is using a String input to create an object) I would do something like this:
public enum Something {
KIMURA1980("kimura1980()"),
ACCORDANCE("accordance()"),
PAIR("pair()");
private final String stringValue;
private Something(final String stringValue) {
this.stringValue = stringValue;
}
public static Something fromString(final String string) {
for (final Something something : values()) {
if (something.stringValue.equals(string)) {
return something;
}
}
return null;
}
}
public static SerializedExpFamMixture rateMtxModel(final String model) {
if (model == null) {
throw new IllegalArgumentException("model is null!");
}
final Something something = Something.fromString(model);
if (something == null) {
return new SerializedExpFamMixture();
}
switch(something) {
case KIMURA1980:
return SerializedExpFamMixture.kimura1980();
case ACCORDANCE:
return SerializedExpFamMixture.accordance();
case PAIR:
return SerializedExpFamMixture.pair();
default:
return new SerializedExpFamMixture();
}
}
This way, the one place where you will use the Strings is in the enum, the rest of the code will use the enum constants and thus have the safety of the compiler to rely on.
One could also leave the linking between operation and String to the enum, like this:
interface Operation<T> {
public T run();
}
public enum Something {
KIMURA1980("kimura1980()", new Operation<SerializedExpFamMixture>() {
public SerializedExpFamMixture run() {
return SerializedExpFamMixture.kimura1980();
}
}) ,
ACCORDANCE("accordance()", new Operation<SerializedExpFamMixture>() {
public SerializedExpFamMixture run() {
return SerializedExpFamMixture.accordance();
}
}),
PAIR("pair()", new Operation<SerializedExpFamMixture>() {
public SerializedExpFamMixture run() {
return SerializedExpFamMixture.pair();
}
}),
DEFAULT(null, new Operation<SerializedExpFamMixture>() {
public SerializedExpFamMixture run() {
return new SerializedExpFamMixture();
}
});
private final String stringValue;
private final Operation<SerializedExpFamMixture> operation;
private Something(final String stringValue, final Operation<SerializedExpFamMixture> operation) {
this.stringValue = stringValue;
this.operation = operation;
}
public static Something fromString(final String string) {
if (string != null) {
for (final Something something : values()) {
if (string.equals(something.stringValue)) {
return something;
}
}
}
return DEFAULT;
}
public SerializedExpFamMixture getCorrespondingSerializedExpFamMixture() {
return operation.run();
}
}
With this setup in the enum (I think the Operation-part can be trimmed out with Java8), the method will be as simple as:
public static SerializedExpFamMixture rateMtxModel(String model) {
return Something.fromString(model).getCorrespondingSerializedExpFamMixture();
}
Use reflection, but you need to consider a few things:
Bug alert! Comparing Strings using == doesn't work as expected in java - use .equals() instead. However, the solution below bypasses that problem
For the general case, which includes methods not visible to the invoker, you need to consider accessibility, both in finding the method and invoking it
You don't need the result variable, and even if using your code, don't need to initialize it
Try this:
String methodName = model.replace("(", "").replace(")", "");
try {
// getMethod() returns only public methods, getDeclaredMethod() returns any visibility
Method method = SerializedExpFamMixture.class.getDeclaredMethod(methodName);
// if the method is not guaranteed to be visible (eg public) you need this:
method.setAccessible(true);
return (SerializedExpFamMixture) method.invoke(null); // how to invoke on the class object
} catch (Exception forBrevity) {
return new SerializedExpFamMixture();
}
Suppose you create a class names Person using the builder pattern, and suppose the Builder class contains methods body(), head(), arms() and of course build() and you consider methods head() and build() obligatory for the user of this class.
We would like to somehow mark these methods obligatory, if possible using annotations. If a user of this class tries to build a Person instance but forgot to call either of these methods, we would like to get some kind of warning - either from the java compiler, or maybe from Eclipse or Maven, which we use to build our projects - any of them would do.
Is it possible to do? Which way would you suggest?
Here is an example with using different types to make some parts mandatory (it also makes the order you call the methods mandatory):
package test;
import test.StepOne.StepThree;
import test.StepOne.StepTwo;
import test.StepOne.LastStep;
public class TestBuilder {
public static void main(String[] args) {
String person1 = PersonBuilder.newInstance().head("head").body("body").arm("arm").leg("leg").build();
String person2 = PersonBuilder.newInstance().head("head").body("body").arm("arm").build();
}
}
interface StepOne {
// mandatory
StepTwo head(String head);
interface StepTwo {
// mandatory
StepThree body(String body);
}
interface StepThree {
// mandatory
LastStep arm(String arm);
}
// all methods in this interface are not mandatory
interface LastStep {
LastStep leg(String leg);
String build();
}
}
class PersonBuilder implements StepOne, StepTwo, StepThree, LastStep {
String head;
String body;
String arm;
String leg;
static StepOne newInstance() {
return new PersonBuilder();
}
private PersonBuilder() {
}
public StepTwo head(String head) {
this.head = head;
return this;
}
public LastStep arm(String arm) {
this.arm = arm;
return this;
}
public StepThree body(String body) {
this.body = body;
return this;
}
public LastStep leg(String leg) {
this.leg = leg;
return this;
}
public String build() {
return head + body + arm + leg;
}
}
Edit
The OP was so impressed with this answer that he wrote it up fully in a blog. It's such a clever take on the builder pattern that a full treatment deserves to be referenced here.
I believe the correct use of the builder pattern would solve the issue you're having.
I would create class PersonBuilder which would contain the methods setBody() and setArms() and every other optional parameter setter method. The constructor of the builder would take the required parameters. Then the method build() would return the new instance of Person.
public class PersonBuilder
{
private final Head head;
private Body body;
private Arms arms;
public PersonBuilder(Head head)
{
this.head = head;
}
public void setBody(Body body)
{
this.body = body;
}
public void setArms(Arms arms)
{
this.arms = arms;
}
public Person build()
{
return new Person(head, body, arms);
}
}
Alternatively you could pass the Head parameter to the method build() but I prefer passing it in the constructor instead.
No way with the compiler.
You can do is throw a runtime exception from the build() method that the builder is not properly initialized (and have a test that is invoked in the maven test phase)
But you can also have build(..) accept a HeadDetails object. That way tou can't invoke build without specifying the obligatory parameters.
Why not calling body(), head(), arms() in the build()-Method if it is really mandatory and returning Person in the build() method?
[edit]
Short example:
public class Builder {
private final String bodyProp;
private final String headProp;
private final String armsProp;
private String hearProps;
public Builder(String bodyProp, String headProp, String armsProp) {
super();
this.bodyProp = bodyProp; // check preconditions here (eg not null)
this.headProp = headProp;
this.armsProp = armsProp;
}
public void addOptionalHair(String hearProps) {
this.hearProps = hearProps;
}
public Person build() {
Person person = new Person();
person.setBody(buildBody());
// ...
return person;
}
private Body buildBody() {
// do something with bodyProp
return new Body();
}
public static class Person {
public void setBody(Body buildBody) {
// ...
}
}
public static class Body {
}
}
Maybe inside of build() you could check if all the required methods have been called. Behaps the Person instance has some internal sanity check which is triggered by build().
Of course this checks runtime behaviour and is no static analysis as you describe it.
isn't possible to call these methods in Person's constructor ?