Best way to remove multiple if-else [closed] - java

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
So the similar type of questions are answered elsewhere but here I am expecting the best way to omit if else chain for the given situation.
CURRENT CODE
private ViewModel getViewModel(Receipt receipt) {
String receiptType = receipt.type;
if(receiptType.equals("HOTEL")) {
return new HotelReceiptViewModel(receipt));
} else if(receiptType.equals("CAR")) {
return new CarReceiptViewModel(receipt));
}
.
.
.
} else if(receiptType.equals("LUNCH")) {
return new FoodReceiptViewModel(receipt));
}
}
where all the view models extend a class called ReceiptViewModel. e.g.
public class HotelReceiptViewModel extends ReceiptViewModel implements ViewModel {
public HotelReceiptViewModel(Receipt receipt) {
super(receipt);
this.receiptNumber = receipt.getDocumentNumber();
this.receiptHeading = "HOTEL";
}
}
There are currently 5 types of receipts and there will be 3-4 more types of receipts in future.
POSSIBLE SOLUTIONS
Use of HashMap
Use of Enum
Use Strategy Pattern or Command Pattern
Use of Reflection
Let's see pros and cons of each approach
1. Use of HashMap
private ReceiptViewModel getViewModel(Receipt receipt) {
Map<String, ReceiptViewModel> map = getViewModelsMap();
String receiptType = receipt.type;
ReceiptViewModel viewModel = map.get(receiptType);
if(viewModel != null) {
viewModel.setReceipt(receipt);
}
return viewModel;
}
private Map<String, ReceiptViewModel> getViewModelsMap() {
Map<String, ReceiptViewModel> map = new HashMap<String, ReceiptViewModel>();
map.add("HOTEL"), new HotelReceiptViewModel());
map.add("CAR"), new CarReceiptViewModel());
map.add("LUNCH"), new FoodReceiptViewModel());
}
and ReceiptViewModel classes will look like
public class HotelReceiptViewModel extends ReceiptViewModel implements ViewModel {
public HotelReceiptViewModel(Receipt receipt) {
super(receipt);
this.receiptNumber = receipt.getDocumentNumber();
this.receiptHeading = "HOTEL";
}
}
PROS
Faster, Easier, Extensible.
CONS
ReceiptViewModel object does not require an object of Receipt type in the constructor. Receipt is rather set using a setter, where all the logic of initializing the ReceiptViewModel class will move now.
2. Use of Enum
private ReceiptViewModel getViewModel(Receipt receipt) {
String receiptType = receipt.type;
ReceiptViewModel viewModel =
ReceiptViewModels.valueOf(receiptType).getReceiptViewModel(receipt);
return viewModel;
}
And Enum will look like
public enum ReceiptViewModels {
HOTEL(
ReceiptViewModel getReceiptViewModel(Receipt receipt) {
return new HotelReceiptViewModel(receipt);
}
),
CAR(
ReceiptViewModel getReceiptViewModel(Receipt receipt) {
return new CarReceiptViewModel(receipt);
}
),
.
.
.
LUNCH(
ReceiptViewModel getReceiptViewModel(Receipt receipt) {
return new FoodReceiptViewModel(receipt);
}
),
public abstract ReceiptViewModel getReceiptViewModel(Receipt receipt);
}
PROS
Fast, Probably Easy.
CONS
Size of Enum will keep on increasing as the receipt types increase, resulting in non-maintainable code.
ReceiptViewModels.valueOf(receiptType) expects a known receipt type. If a new receipt type comes as a response from server, it will result in an IllegalArgumentException
3. Use of Reflection
Class<? extends ReceiptViewModel> viewModel = Class.
forName(receiptType + name + "ReceiptViewModel").asSubclass(ReceiptViewModel.class);
ReceiptViewModel receiptViewModel = viewModel .newInstance();
CONS
1. Slower
Cannot be used when the class names are different. e.g. For LUNCH type the view model class name is FoodReceiptViewModel
Logic of getting values from receipt is moved to a setter instead of constructor as in case of HashMap
4. Use of Strategy Pattern or Template Pattern
PROS Easy to understand and faster than Reflection
CONS Probably an overkill. A new class will be added for each type of receipt.
Considering all the above points, which would be the best approach for my use case to remove the multiple if-else blocks?

I would use a switch, unless there is a reason to use something more complex.
private ViewModel getViewModel(Receipt receipt) {
switch(receipt.type) {
case "HOTEL": return new HotelReceiptViewModel(receipt);
case "CAR": return new CarReceiptViewModel(receipt);
case "LUNCH": return new FoodReceiptViewModel(receipt);
default:
throw new IllegalArgumentException("Unknown receipt type " + receipt.type);
}
I would claim this is the best solution as it is the simplest which meets you needs.

No behavior needed, no structure needed, no polymorphism needed, so I don't see interest to use design patterns such as strategy or to use a map which brings nothing but complexity in the need you describe.
You want a very simple state machine. So, "if else if" or "switch" is perfect .
Maybe out of the subject but with a private method, the code is not unitary testable.

I would make a new abstract method into your Receipt class. Something like
public <T extends ReceiptViewModel> T createModel()
And then would create subclasses of the Receipt class where each type would correspond to one subclass. Then each subclass will implement his own version of createModel returning the correct subclass of ReceiptViewModel. Your initial code will be as simple as
private ViewModel getViewModel(Receipt receipt) {
return receipt.createModel()
}

Related

Interface to enforce generic static method in Java

I have a Java class Model which models some data from my remote database. I want all data models in my project to be able to supply a builder from a Map<String, Object> instance (in practice, I'm working with SnapshotParser<Model> parsers with Firestore, but I'll just call getData() in every model). This should look something like:
public class Model {
private String name;
public Model(String name) { this.name = name; }
public static SnapshotParser<Model> getDocParser() {
return docSnapshot -> {
Map<String, Object> data = docSnapshot.getData();
return new Model(data.getOrDefault("name", "John Doe"));
};
}
}
Note that I'll have several models (Model2, Model3...) which will also be required to provide such an interface. To enforce this behavior, I created a DocParserSupplier generic class for my model classes to implement:
public interface DocParserSupplier<T> {
static SnapshotParser<T> getDocParser();
}
This doesn't work for two reasons (as Android Studio informs me):
static methods of interfaces must have a default implementation. I can't do that without knowing T.
I get the "T cannot be referenced in static context" error.
If remove the static keyword from the above interface, I can do what I want but it would require I create an actual instance of the Model to get the parser. It would work but it makes more sense if the method is static.
Is there a Java way to do what I want?
EDIT: My specific use case is in matching RecyclerViews to documents in my database. Constructing the FirestoreRecyclerOptions object requires a parser to convert key-value data to a Model:
FirestoreRecyclerOptions<Model1> fro1 = new FirestoreRecyclerOptions.Builder<Model1>()
.setQuery(query1, Model1.getDocParser())
.build();
FirestoreRecyclerOptions<Model2> fro2 = new FirestoreRecyclerOptions.Builder<Model2>()
.setQuery(query2, Model2.getDocParser())
.build();
Interfaces enforce behavior of instances, so that references to any object which has that behavior can be passed around in a type-safe way. Static methods on the other hand, don't belong to any particular instance of an object; the class name is essentially just a namespace. If you want to enforce behavior, you will have to create an instance somewhere (or use reflection, if it is absolutely necessary to ensure a class has a particular static method).
Unless this system is going to be opened up for extension, where others can define their own models, I would say ditch the DocParserSupplier interface altogether and call the static methods exactly as you are now, or factor them out into a factory interface + implementation. The factory option is nice because you can replace the production implementation with a fake implementation that returns dummy parsers for tests.
Edit: Doc Parser Factory
public interface DocParserFactory {
SnapshotParser<Model1> getModel1Parser();
SnapshotParser<Model2> getModel2Parser();
...
SnapshotParser<Model1> getModelNParser();
}
...
// The implementation of each getModelXParser method
class DocParserFactoryImpl {
SnapshotParser<Model1> getModel1Parser() {
return docSnapshot -> {
Map<String, Object> data = docSnapshot.getData();
return new Model(data.getOrDefault("name", "John Doe"))};
}
...
}
...
private DocParserFactory docParserFactory;
// You can inject either the real instance (DocParserFactoryImpl) or a
// test instance which returns dummy parsers with predicable results
// when you construct this object.
public ThisObject(DocParserFactory docParserFactory) {
this.docParserFactory = docParserFactory;
}
...
// Your code
public void someMethod() {
...
FirestoreRecyclerOptions<Model1> fro1 = new
FirestoreRecyclerOptions.Builder<Model1>()
.setQuery(query1, docParserFactory.getModel1Parser())
.build();
FirestoreRecyclerOptions<Model2> fro2 = new
FirestoreRecyclerOptions.Builder<Model2>()
.setQuery(query2, docParserFactory.getModel2Parser())
.build();
...
}
It's not so much to do with static or non-static, as it is with the fact that you cannot create an instance of a generic object without passing the type parameter(s) one way or another. In fact, I answered a similar question a few days ago, when somebody wanted to use enums to get the required builder.
In short, you cannot write a method <T extends AbstractBuilder> T builder(final SomeNonGenericObject object) (or, in this case, <T extends AbstractBuilder> T builder()) without passing T in some form. Even though it will make sense at runtime, the compiler can't figure out what generic type to use if you don't tell it which one it is.
In Java 8, you can solve this elegantly with method references. I don't know much about Android, but I believe you're still on Java 6 there, so this wouldn't work.
Anyway, you can have something like the following:
public <T extends AbstractBuilder> T builder(final Supplier<T> object) {
return supplier.get();
}
final Supplier<AbstractBuilder> model1BuilderSupplier = Model1Builder::new;
builder(model1BuilerSupplier)
.setQuery(query1, Model1.getDocParser())
.build();
It's not exactly what you want, but the way you're trying to go about it will not work.

Is this an actual shortcut for a Java Lazy-Getter [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 years ago.
Improve this question
The following is less a question than a request for evaluation.
So you will most likely be familiar with the following pattern of a lazy getter.
private Object obj;
public Object getObject() {
if(obj==null) {
obj = new Object();
}
return obj;
}
That code
is long
requires a class variable
requires some logic inside the getter
So recently a colleague and I came up with the following interface (simplified):
public interface LazyGetterSupport {
default Object get(Supplier<Object> impl) {
String key = impl.getClass().getName();
Object retVal;
if ((retVal = getInstanceCache().get(key)) == null) {
retVal = impl.get();
getInstanceCache().put(key, retVal);
}
return retVal;
}
Map<String, Object> getInstanceCache();
}
Sidenote: Not using HashMap#computeIfAbsent bc of Bug-8071667
That Interface is then implemented by the class that you want to use lazy getters in. You need to provide an implementation of getInstanceCache() as follows:
private Map<String, Object> instanceCache;
#Override
public Map<String, Object> getInstanceCache() {
if (instanceCache == null) {
instanceCache = new HashMap<>();
}
return instanceCache;
}
But given that you can start rewriting all other lazy getters in that class (and subclasses) like this:
public Object getObject() {
return get(Objetct::new);
}
This works because the key we get by using impl.getClass().getName(); inside our Interface will actually always be unique for every lambda we use inside implementations of its get-method. Yet it will stay the same for each call-site for at least the life duration of our VM.
Benefits are obvious. We no longer have to create a class-variable for every lazy-getter and the getter themselves become shorter. It may not be a world of a difference, but in our use-case we often have classes with 20+ Lazy Getters for UI Elements. That is where this new technique comes to shine.
I would love to know your thoughts on this approach and whether you have any concerns towards using this in production.
Or you could simply use #Getter(lazy=true) from Project Lombok.
Your cache is per class which doesn't feel future proof to me. If you had some generic Supplier that was used multiple times then using the Class as the cache key wouldn't work. I'd just store a reference on a LazyValue which you can use and cache however you like. Here's a thread safe implementation:
public class LazyValue<T> {
private AtomicReference<T> reference = new AtomicReference<>();
private final Supplier<T> supplier;
public LazyValue(Supplier supplier) { this.supplier = supplier; }
public T get() {
T t = reference.get();
if (t == null) {
synchronized (reference) {
t = reference.get();
if (t == null) {
t = supplier.get();
reference.set(t);
}
}
}
return t;
}
}
public class MyBean {
private LazyValue<Object> lazyObject = new LazyValue(Object::new);
public Object getObject() {
return lazyObject.get();
}
}
Looks interesting, but you replaced a bunch of member variables with a Hashmap. While this works, it increases the memory usage of your class, and adds O(1) overhead to member access. Synchronizing the lazy getter method is not enough, you need to synchronize on the HashMap, adding keys concurrently will break something. In the traditional lazy getter, synchronizing on the one attribute is enough. Here, if you concurrently access different attributes they have to wait on each other.
And in your use case, I really wonder why one would need lazy getters for UI elements.
Object o;
public Object getInstance() {
return o == null ? (o = new Object()) : o;
}
No need for a fancy HashMap, just convert "long" 4 lines of code into 1.
You could also allow the client to actually set a property.
interface LazyGetterSupport<T> {
T get();
static <T> LazyGetterSupport<T> of(T value, Supplier<T> create, Consumer<T> store) {
T result = value;
if (result == null) {
result = create.get();
store.accept(result);
}
return result;
}
}
which you could call using
class Client {
Object field;
void getField() {
LazyGetterSupport.of(field, Object::new, o -> field = o).get();
}
}

Design pattern - creating an object from different types of data source [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have been trying to find an appropriate design pattern, if a formal one exists, for the following scenario:
Object A requires an object B. Now, object B can be created using data from different sources, say S_1 or S_2. I don't want A to have to care about creating B, it should just be given it and proceed. So then, is there a nice clean way of creating B? I have thought about factory-type patterns but I do not have polymorphism at play. I was thinking something like:
B obj_b = B.createInstance(type_S_1);
obj_A.doSomething(obj_B);
where I give the type of data soruce to the static method createInstance().
I'm still learning a lot about design patterns, so that's why I ask. I may be over complicating things, so please feel free to say so!
As you realized, the Abstract Factory pattern is overkill for your use case as you do not need polymorphism. Still, the Concrete Factory part of this design pattern make sense. So this could look a bit like:
Datasource ds1 = ...;
Datasource ds2 = ...;
MyObject objectA = ...;
DatasourceBasedFactory factory1 = new DatasourceBasedFactory(ds1);
objectA.doSomething(factory1.create());
Knowing more about what you actually want to do might help to give you a better answer. Your current problem description is extremely abstract ... If you could give us some more details about your domain, that would help to give you a better answer.
I'm not sure, but perhaps the Builder Pattern? You can give it a type to specify what to build.
I would consider 2 differents approaches using generics.
The client will only deal with a common result Object that could be final for example.
No matter what your DataSource is, you can reduce the impact for the client.
Approach 1
Example
public interface DataSourceExtractor<T> {
public DataSourceExtractResult extract(T source);
}
public final ResultSetExtractor implements DataSourceExtractor<ResultSet>{
public DataSourceExtractResult extract(ResulSet source) {
//CODE HERE
return null;
}
}
public final ResultSetExtractor implements DataSourceExtractor<JsonNode>{
public DataSourceExtractResult extract(JsonNode source) {
//CODE HERE
return null;
}
}
But you can also meet the advantage of Abstract Class and Interface.
The advantage is that the client will inherit common methods or you can even implement template methods.
Example
public AbstractDataSourceExtractor<T> implements DataSourceExtractor<T> {
public static final SomeObject commonMethod(DataSourceExtractResult result) {
//CODE HERE
return null;
}
}
public final ResultSetExtractor extends AbstractDataSourceExtractor<ResultSet>{
public DataSourceExtractResult extract(ResulSet source) {
//CODE HERE
return null;
}
}
public final ResultSetExtractor extends AbstractDataSourceExtractor<JsonNode>{
public DataSourceExtractResult extract(JsonNode source) {
//CODE HERE
return null;
}
}
Approach 2
Example
You can also think about a generic Abstract builder, if many elements need to be set for the construction of the Instance.
The advatange of that solution is that you can set a default value, provide internal implementation hiding from the client if necessary.
public abstract class AbstractDataSourceExtractResultBuilder<T>{
private T _source;
public AbstractDataSourceExtractResultBuilder(T source) {
_source = source;
}
public abstract DataSourceExtractResult build();
}
public final class JsonDataSourceExtractBuilder extends AbstractDataSourceExtractResultBuilder<JsonNode> {
private String _name;
private Charset _charset;
public JsonDataSourceExtractBuilder(JsonNode source, String name){
//GUARD CODE
super(source);
_name = name;
_charset = Charset.defaultCharset();
}
public JsonDataSourceExtractBuilder useCharset(Charset charset){
if(charset == null){
throw new IllegalStateException("The charset is null");
}
_charset = charset;
return this;
}
//etc...
public DataSourceExtractResult build(){
//CODE HERE
return null;
}
}

any way to simplify this with a form of dynamic class instantiation?

I have several child classes that extend a parent class, forced to have a uniform constructor. I have a queue which keeps a list of these classes, which must extend MergeHeuristic. The code that I currently have looks like the following:
Class<? extends MergeHeuristic> heuristicRequest = _heuristicQueue.pop();
MergeHeuristic heuristic = null;
if(heuristicRequest == AdjacentMACs.class)
heuristic = new AdjacentMACs(_parent);
if(heuristicRequest == SimilarInterfaceNames.class)
heuristic = new SimilarInterfaceNames(_parent);
if(heuristicRequest == SameMAC.class)
heuristic = new SameMAC(_parent);
Is there any way to simplify that to dynamically instantiate the class, something along the lines of:
heuristic = new heuristicRequest.somethingSpecial();
That would flatten that block of if statements.
It looks like you're using the class on the queue as a sort of flag to indicate what type of request to instantiate. Another approach that doesn't use reflection is to make this flag behavior explicit by introducing an enum to indicate the request type, with a factory method:
public enum HeuristicType {
AdjacentMACsHeuristic(AdjacentMACs.class) {
#Override public MergeHeuristic newHeuristic(ParentClass parent) {
return new AdjacentMACs(parent);
}
},
SimilarInterfaceNamesHeuristic(SimilarInterfaceNames.class) {
#Override public MergeHeuristic newHeuristic(ParentClass parent) {
return new SimilarInterfaceNames(parent);
}
},
... // other types here.
;
private final Class<? extends MergeHeuristic> heuristicClass;
public Class<? extends MergeHeuristic> getHeuristicClass() {
return heuristicClass;
}
abstract public MergeHeuristic newHeuristic(ParentClass parent);
private HeuristicType(Class<? extends MergeHeuristic> klass) {
this.heuristicClass = klass;
}
}
Your client code then becomes:
Queue<HeuristicType> _heuristicQueue = ...
HeuristicType heuristicRequest = _heuristicQueue.pop();
MergeHeuristic heuristic = heuristicRequest.newHeuristic(_parent);
The main advantages of using an enum as opposed to reflection are:
You're explicitly stating the requirements for adding a new heuristic type, i.e. that there must be a heuristic class and that you must be able to instantiate it based on a parent.
You have a single point in the system where you can see all available heuristic types.
By abstracting the instantiation into a factory method, you allow the possibility of alternate constructor signatures.
You could use reflection, but it won't make the code any prettier.
try {
Constructor<? extends MergeHeuristic> heuristicConstructor =
heuristicRequest.getConstructor(_parent.getClass());
heuristic = heuristicConstructor.newInstance(_parent);
} catch (Exception ex) {
// TODO Handle this
}
Only do this if you're planning on having a lot of different classes. Don't bother if it's only going to be 3 of them, your code is fine for that.
Unfortunately you can't enforce a class to have a certain constructor or a static method - both would be very useful in your case.
As all constructors take the same argument there is one other way to simplify your code using dynamic class instantiation:
Constructor c = heuristicRequest.getConstructor(ParentClass.class).
heuristic = c.newInstance(_parent);
Note that your code did not contain the class type of _parent - in the code sample I named it ParentClass.class - you have to adapt that to your code.
Is Class.forName(heuristicRequest.getName()) an option?
Then constructor heuristicRequestClass.getDeclaredConstructor(_parent.getClass());
Last heuristic = constructor.newInstance(_parent);

Elegant alternatives for huge amount of arguments in class constructor [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 years ago.
Improve this question
For example I have a class that builds the GUI, a class that handles all the events for the GUI and the main class that holds all the objects that are affected by the GUI-Objects (mostly sliders) and instances both the GUI-class and event-class.
Now the event-class's constructor has as arguments the GUI class and every object that is being changed by the GUI. These are quite allot of objects so the amount of arguments I have now are about 8 and still growing.
Is there a more elegant solution to my problem, 30 arguments simply doesn't feel right?
ps, I'd rather not combine classes because all three are quite big and would make everything much less readable.
Often a builder object with fluent syntax is used in such a case. You change:
new XYZEvent(a, null, null, b, null, c, d, null, null)
to
new XYZEventBuilder().setA(a).setB(b).setC(c).setD(d).build()
You can create a configuration class which holds default values for all parameters:
public class GUIConfig {
private String name = "default";
// more private declarations
public GUIConfig() {
// constructor, just for setting defaults
}
// getters and setters
}
Now you can simply create your GUI class instance like this:
GUIConfig guiConfig = new GUIConfig();
guiConfig.setName("foo");
// more setters
GUI myGUI = new GUI(guiConfig);
or for using only defaults:
GUI myGUI = new GUI(new GUIConfig());
Use a DTO (Data Transfer Object) to hold all your classes. This can then be passed in a single parameter.
a DTO does not have any behavior
except for storage and retrieval of
its own data
You should consider using Google's AutoValue library: https://github.com/google/auto/blob/master/value/userguide/index.md
#AutoValue
public abstract class Card {
#Nullable
public abstract Integer localId();
public abstract Long utcStart();
public abstract Integer paramA();
public abstract Integer paramB();
#Nullable
public abstract Boolean paramC();
public static Builder builder() {
return new AutoValue_Card.Builder();
}
#AutoValue.Builder
public abstract static class Builder {
public abstract Builder setLocalId(final Integer localId);
public abstract Builder setUtcStart(final Long utcStart);
public abstract Builder setParamA(final Integer paramA);
public abstract Builder setParamB(final Integer paramB);
public abstract Builder setParamC(final Boolean paramC);
public abstract Card build();
}
}
All the no-mandatory fields can be annotated with #Nullable.
To create an immutable Card object you just use this:
Card myCard = Card.builder()
.setLocalId(123456) // this line can be omitted
.setUtcStart(158632478000)
.setParamA(5)
.setParamB(58)
.setParamC(true) // this line can be omitted
.build();
"AutoValue is a great tool for eliminating the drudgery of writing mundane value classes in Java. It encapsulates much of the advice in Effective Java Chapter 2, and frees you to concentrate on the more interesting aspects of your program. The resulting program is likely to be shorter, clearer, and freer of bugs. Two thumbs up."
-- Joshua Bloch, author, Effective Java

Categories

Resources