(This question was closed on code review so I think I should ask here)
Let's say I have a factory like this (it's from an interview):
public class ControllersFactoryImpl implements ControllersFactory {
private final SessionKeeper sessionKeeper;
private final ScoreKeeper scoreKeeper;
public ControllersFactoryImpl(final SessionKeeper sessionKeeper, final ScoreKeeper scoreKeeper) {
this.sessionKeeper = sessionKeeper;
this.scoreKeeper = scoreKeeper;
}
#Override
public Controller makeLoginController(final int userId) {
return new LoginController(userId, sessionKeeper);
}
#Override
public Controller makePostUserScoreController(final int levelId, final String session, final int score) {
return new AddScoreController(levelId, session, score, sessionKeeper, scoreKeeper);
}
#Override
public Controller makeHighScoreController(final int levelId) {
return new HighScoreController(levelId, scoreKeeper);
}
}
since one of the requirements was to handle several call at the time (like millions) they told me that this solution could be improved because in this way we had a huge spawning of new objects (since I'm always calling new) that are doing a single stateless operation and the garbage collector could run into problems trying to clean them.
Controller is an interface that has a single method execute().
Avoiding the usage of constructor is something that is puzzling me because the only way I can think of it, is to give to the execute method a var-args argument and I don't really like that solution because the code is not really readable in that way.
Do you have any alternatives?
This is the code for the controller:
public interface Controller {
String execute();
}
And this is where the controller is used:
Controller controller = null;
try {
if (exchange.isGet()) {
final Matcher mLogin = loginPattern.matcher(path);
if (mLogin.matches()) {
controller = factory.makeLoginController(Integer.parseInt(mLogin.group(1)));
contentType = TEXT_PLAIN;
}
Matcher mHighScore = highScorePattern.matcher(path);
if (mHighScore.matches()) {
controller = factory.makeHighScoreController((Integer.parseInt(mHighScore.group(1))));
contentType = TEXT_CSV;
exchange.setContentDisposition("attachment; fileName=data.csv");
}
} else if (exchange.isPost()) {
final Matcher mScore = userScorePattern.matcher(path);
if (mScore.matches()) {
final Matcher mSession = sessionKeyPattern.matcher(httpExchange.getRequestURI().getQuery());
if (mSession.matches()) {
final Scanner s = new Scanner(httpExchange.getRequestBody());
final int score = Integer.parseInt(s.hasNext() ? s.next() : "0");
controller = factory.makePostUserScoreController(Integer.parseInt(mScore.group(1)), mSession.group(1), score);
contentType = TEXT_PLAIN;
}
}
}
if (controller != null) {
exchange.sendOk();
buildResponse(exchange, controller, contentType);
} else exchange.sendNotFound();
} catch (ExpiredSessionException e) {
exchange.sendUnauthorized();
exchange.setContentType(TEXT_PLAIN);
exchange.setContentType("Session Expired");
} catch (Exception e) {
log(e.getMessage());
httpExchange.sendResponseHeaders(500, 0);
} finally {
httpExchange.getResponseBody().close();
}
Disclaimer: I'm aware of the if-else situation but with that amount of time I didn't have the time to refactor this part.
It's possible to change the code the way you want.
private void buildResponse(Exchange exchange, Controller controller, String contentType) throws IOException {
exchange.setContentType(contentType);
exchange.setContent(controller.execute());
}
since one of the requirements was to handle several call at the time (like millions) they told me that this solution could be improved because in this way we had a huge spawning of new objects (since I'm always calling new)
This sounds like a very very premature optimization. Does the program do any real work, like reading a file or iterating something? If so, then many bigger objects get created and caring about the controller creation is ridiculous.
Anyway, there's a Scanner allocated.
Your controller is not really stateless, it's immutable at best. Its state consists e.g. of levelId, session, score, sessionKeeper, scoreKeeper.
execute method a var-args argument
This means a creation of an array... about the same overhead you wanted to avoid.
Anyway, it looks like the controller currently just complicates the design and you might be better off not using it. However, as the program grows, you may see that using a controller is a good idea as it nicely separates different actions.
I'd just try it out as is. Get millions of requests, determine the bottleneck, and redesign it in case of problems. Till you run into performance problems, keep your design as clean as possible.
Clean design means flexible design and that's the best starting point for optimizations. Code perfectly optimized for imaginary problems is a non-maintainable mess, getting slow in face of real problems and hopeless to improve.
If you really had to eliminate the controller creation, then you can't store any information in them. So you could create an
enum Controller {
LOGIN {
...
}
POST_USERS_SCORE {
...
}
HIGH_SCORE {
...
}
abstract execute(int levelId, String session, int score);
}
where each implementation would ignore the arguments it doesn't need. This is a bit messy, but not as messy as mutable design could get. With mutable controllers you could pool and recycle them, but this is rarely a good idea.
Related
I have a class like below, with hundreds of methods:
public class APIMethods {
public ToView toView;
public APIMethods(ToView toView) {
this.toView = toView;
}
public static final int SUCCESS = 1;
public static final int ERROR = 0;
public void registerAnonymous(String deviceId, String installRef, final int requestCode) {
APIInterface apiService =
RetrofitClientInstance.getRetrofitInstance().create(APIInterface.class);
JsonObject obj = new JsonObject();
obj.addProperty("androidId", deviceId);
obj.addProperty("projectId", 0);
obj.addProperty("ChannelName", installRef);
Call<Response<BasicUser>> call = apiService.registerAnonymous("application/json", Utils.getFlavorId(), obj);
call.enqueue(new Callback<Response<BasicUser>>() {
#Override
public void onResponse(Call<Response<BasicUser>> call, Response<Response<BasicUser>> response) {
Response<BasicUser> mResponse;
try {
mResponse = response.body();
if (mResponse.getErrorCode() == 0)
toView.updateView(requestCode, SUCCESS, mResponse);
else
toView.updateView(requestCode, ERROR, mResponse);
} catch (Exception e) {
mResponse = new Response<>();
mResponse.setErrorCode(-1);
toView.updateView(requestCode, ERROR, mResponse);
e.printStackTrace();
}
}
#Override
public void onFailure(Call<PetMarkResponse<BasicUser>> call, Throwable t) {
Response<BasicUser> numberValidationResponse = new Response<BasicUser>();
numberValidationResponse.setErrorCode(-1);
toView.updateView(requestCode, ERROR, numberValidationResponse);
}
});
}
///And dozens of such method
}
So in my other classes everywhere in my application, I simply instantiate the class and call the method that I want:
APIMethods api = new APIMethods(this);
api.registerAnonymous(Utils.getAndroidId(this), BuildConfig.FLAVOR, STATE_REGISTER_ANONYMOUS);
My question is how expensive this object (api) is? Note that in each class, a few methods of the object are called.
The object is not expensive at all.
An object contains a pointer to the object's class, and the methods are stored with the class. Essentially, the methods are all shared. An object of a class with no methods and an object of a class with 10000 methods are the same size (assuming everything else is equal).
The situation would be different if you had 100 fields instead of 100 methods.
You may want to think about if having hundreds of methods in a single class is a good idea. Is the code easy to understand and maintain? Is this an example of the "God object" anti pattern? https://en.m.wikipedia.org/wiki/God_object
This seems like a classic example of the XY problem. Your actual problem is how to make the code readable, but you're actually asking about whether a class with hundreds of methods is expensive.
It being expensive is the least of your concerns - you should be more worried about maintenance. There's no reason at all that any class should ever be that large, especially if you have a lot of independent methods and each class is only calling a few of them. This will make the class very hard to understand - having them all in one place will not improve the situation.
Some of the comments have already pointed this out, but you should, at a minimum, break this up topically.
Even better, refactor this to the Strategy pattern and use a Factory to pick which one to use. That will meet your goal of ease of use while avoiding the problem of having hundreds of unrelated methods in one place.
Try to define a Cohesive class, untill and unless the methods are written relevant to the class and it defines its purpose.
Below link describe the importance of methods for a class:
https://www.decodejava.com/coupling-cohesion-java.htm
how are you? I'm trying to do some dynamic method calls to get sql strings on various objects in Java (Android), but i'm stuck with some questions about performance and stability.
Context Example: Repository class onCreate method get all entity objects (tables) and call a method (getCreateTable for example) to get a sql string to execute.
Sure i can explicit call class by class caling each method, but i have other calls like "dropTables", "truncateTables" and etc, and i do not want to be repeating the same structure all the time.
public void CreateTables() {
execute(Entity1.getCreateTable());
execute(Entity2.getCreateTable());
execute(Entity3.getCreateTable());
[..]
execute(Entity50.getCreateTable());
}
public void DropTables() {
execute(Entity1.getDropTable());
execute(Entity2.getDropTable());
execute(Entity3.getDropTable());
[..]
execute(Entity50.getDropTable());
}
Until now i know i can do that in 3 diferent ways.
1) Using reflection (currently in use): Basicaly, i store all the objects class in a list, and then use the reflection to call the desired static method.But i know that reflection not always should be the first choice.
private final List<Class> entityList = new ArrayList<Class>() {
{
add(Entity1.class);
add(Entity2.class);
add(Entity3.class);
}
};
public void createTables() {
/* get all query strings */
List<String> queryList = getQueryList("createTable");
try {
for (String query : queryList) {
execute(query);
}
} catch (SQLException e) {
[...]
}
}
private List<String> getQueryList(String methodName) {
List<String> queryList = new ArrayList<>();
for (Class<?> objectClass : entityList) {
try {
Method[] ms = objectClass.getMethods();
for (Method me : ms) {
if (me.getName().equals(methodName)) {
String query = (String) me.invoke(null);
if (query != null && query.length() > 0) {
queryList.add((String) me.invoke(null));
}
break;
}
}
} catch (Exception e) {
[...]
}
}
return queryList;
}
2) Storing object instance in list: I can have a list with the objects instanced and then cast then into abstract parent class (or interface) and call the methods to get the sql string. In this case, i don't know if is a good practice to keep an list of instanced objects in memory, maybe this could be worst than use reflection depending on list size.
private final List<BaseEntity> entityList = new ArrayList<BaseEntity>() {
{
add(new Entity1(context));
add(new Entity2(context));
add(new Entity3(context));
}
};
public void createTables() {
for (BaseEntity entity : entityList) {
try {
execute(entity.getCreateTable());
} catch (Exception e) {
[...]
}
}
}
3) Storing all the strings into JSON object: I don't tested that one yet, but i'm sure with should work. I can call an "init" method to iterate over all objects and create that JSON object/array with all the sql strings (drop, create, truncate and etc).
I really appreciate if you share with me what you think about these approaches (pros and cons) or another better solution.
As pointed out in the comments, it was a clarly a bad design (it's an old project that i'm refactoring). So i decided to get away from reflection and spend some time to redesign the code itself.
I created an base super class to handle all similiar methods and let the entities/models implement only the required individual rules, so DB access is stored in only one class as a Singleton. It's far better to use interface polymorphism.
In this way, the db class handle the dinamic SQL generation to avoid repeating the same code everywhere and re-use/recycle the list of instances to improve performance.
Obs. 1: Reflection throw down performance and usually let debbuging harder. Sure it can save some time as it is fast to implement, but will disable most of the IDE features, which makes it worthless in most cases.
Obs. 2: Keeping a list of DB instances active should never be done either. It's never a good idea to have many instances access database simultaneously, it can cause DB to lock and reproduce unexpectad issues.
Obs. 3: That JSON thing... forget about it. I'm sorry to suggest something so ugly.
I am developing a client-server application in Java using Websocket. Currently, all the client messages are processed using switch-case as shown below.
#OnMessage
public String onMessage(String unscrambledWord, Session session) {
switch (unscrambledWord) {
case "start":
logger.info("Starting the game by sending first word");
String scrambledWord = WordRepository.getInstance().getRandomWord().getScrambledWord();
session.getUserProperties().put("scrambledWord", scrambledWord);
return scrambledWord;
case "quit":
logger.info("Quitting the game");
try {
session.close(new CloseReason(CloseCodes.NORMAL_CLOSURE, "Game finished"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
String scrambledWord = (String) session.getUserProperties().get("scrambledWord");
return checkLastWordAndSendANewWord(scrambledWord, unscrambledWord, session);
}
The server has to process more than 50 different requests from client and that results in more than 50 case statements. And in future, I expect it to grow. Is there any better way to process Websocket messages from client? Or, is this how it is usually done?
I read somewhere about the use of hashtable to avoid long switch-case scenario by mapping to function pointers. Is this possible in Java? Or, is there any better solutions?
Thanks.
After a bit of testing and study, I found two alternatives to avoid long switch case scenario.
Anonymous class method (Strategy pattern)
Reflection with Annotations
Using Anonymous Class
Anonymous class method is the norm and following code shows how to implement it. I used Runnable in this example. If more control is required, create a custom interface.
public class ClientMessageHandler {
private final HashMap<String, Runnable> taskList = new HashMap<>();
ClientMessageHandler() {
this.populateTaskList();
}
private void populateTaskList() {
// Populate the map with client request as key
// and the task performing objects as value
taskList.put("action1", new Runnable() {
#Override
public void run() {
// define the action to perform.
}
});
//Populate map with all the tasks
}
public void onMessageReceived(JSONObject clientRequest) throws JSONException {
Runnable taskToExecute = taskList.get(clientRequest.getString("task"));
if (taskToExecute == null)
return;
taskToExecute.run();
}
}
Major drawback of this method is object creation. Say, we have 100 different tasks to perform. This Anonymous class approach will result in creating 100 objects for a single client. Too much object creation is not affordable for my application, where there will be more than 5,000 active concurrent connections. Have a look at this article http://blogs.microsoft.co.il/gilf/2009/11/22/applying-strategy-pattern-instead-of-using-switch-statements/
Reflection with Annotation
I really like this approach. I created a custom annotation to represent the tasks performed by methods. There is no overhead of object creation, like in Strategy pattern method, as tasks are performed by a single class.
Annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface TaskAnnotation {
public String value();
}
The code given below maps the client request keys to the methods which process the task. Here, map is instantiated and populated only once.
public static final HashMap<String, Method> taskList = new HashMap<>();
public static void main(String[] args) throws Exception {
// Retrieves declared methods from ClientMessageHandler class
Method[] classMethods = ClientMessageHandler.class.getDeclaredMethods();
for (Method method : classMethods) {
// We will iterate through the declared methods and look for
// the methods annotated with our TaskAnnotation
TaskAnnotation annot = method.getAnnotation(TaskAnnotation.class);
if (annot != null) {
// if a method with TaskAnnotation is found, its annotation
// value is mapped to that method.
taskList.put(annot.value(), method);
}
}
// Start server
}
Now finally, our ClientMessageHandler class looks like the following
public class ClientMessageHandler {
public void onMessageReceived(JSONObject clientRequest) throws JSONException {
// Retrieve the Method corresponding to the task from map
Method method = taskList.get(clientRequest.getString("task"));
if (method == null)
return;
try {
// Invoke the Method for this object, if Method corresponding
// to client request is found
method.invoke(this);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
logger.error(e);
}
}
#TaskAnnotation("task1")
public void processTaskOne() {
}
#TaskAnnotation("task2")
public void processTaskTwo() {
}
// Methods for different tasks, annotated with the corresponding
// clientRequest code
}
Major drawback of this approach is the performance hit. This approach is slow compared to Direct Method calling approach. Moreover, many articles are suggesting to stay away from Reflection, unless we are dealing with dynamic programming.
Read these answers to know more about reflection What is reflection and why is it useful?
Reflection performance related articles
Faster alternatives to Java's reflection
https://dzone.com/articles/the-performance-cost-of-reflection
FINAL RESULT
I continue to use switch statements in my application to avoid any performance hit.
As mentioned in the comments, one of websockets drawback is that you'll to specify the communication protocol yourself. AFAIK, the huge switch is the best option. To improve code readability and maintenance, I'll suggest to use encoders and decoders. Then, your problem becomes: how should I design my messages?
Your game looks like Scrabble. I don't know how to play Scrabble so let's take the example of card game with money. Let's assume you have three types of actions:
Global action (join table, leave table ...)
Money action (place bet, split bet, ...)
Card action (draw card, etc)
Then your messages can look like
public class AbstractAction{
// not relevant for global action but let's put that aside for the example
public abstract void endTurn();
}
public class GlobalAction{
// ...
}
public class MoneyAction{
enum Action{
PLACE_BET, PLACE_MAX_BET, SPLIT_BET, ...;
}
private MoneyAction.Action action;
// ...
}
public class CardAction{
// ...
}
Once your decoder and encoders are properly defined, your switch would be easier to read and easier to maintain. In my project, the code would look like this:
#ServerEndPoint(value = ..., encoders = {...}, decoders = {...})
public class ServerEndPoint{
#OnOpen
public void onOpen(Session session){
// ...
}
#OnClose
public void onClose(Session session){
// ...
}
#OnMessage
public void onMessage(Session session, AbstractAction action){
// I'm checking the class here but you
// can use different check such as a
// specific attribute
if(action instanceof GlobalAction){
// do some stuff
}
else if (action instanceof CardAction){
// do some stuff
}
else if (action instance of MoneyAction){
MoneyAction moneyAction = (MoneyAction) action;
switch(moneyAction.getAction()){
case PLACE_BET:
double betValue = moneyAction.getValue();
// do some stuff here
break;
case SPLIT_BET:
doSomeVeryComplexStuff(moneyAction);
break;
}
}
}
private void doSomeVeryComplexStuff(MoneyAction moneyAction){
// ... do something very complex ...
}
}
I prefer this approach because:
The messages design can leverage your entities design (if you are using JPA behind)
As messages are not plain text anymore but objects, enumerations can be used and enumerations are very powerful in this kind of switch-case situation. With the same logic but in a lesser extend, class abstraction can be useful as well
The ServerEndPoint class only handles communication. The business logic is handled out of this class, either directly in Messages classes or in some EJB. Because of this split, code maintenance is much easier
Bonus: #OnMessage method can be read as a summary of the protocol but details should not be displayed here. Each case must contain few lines only.
I prefer avoid using Reflection: it'll ruin your code readability, in the specific scenario of websocket
To go further beyond code readability, maintenance and efficiency, you can use a SessionHandler to intercept some CDI event if this can improve your code. I gave an example in this answer. If you need a more advanced example, Oracle provides a great tutorial about it. It might help you to improve your code.
I am writing endpoint unit tests and for most of those there is an external web service that should be mocked, or a couple of them.
At first, i was creating mocks within tests which was okay when an endpoint test used only one external service, the mock creation was basically one liner.
As use cases became more complex, i needed to mock couple of services and exceptions for a single endpoint test.
I have put these mocks creation behind factories that all extend single factory and used builder pattern.
Within that base factory there is an inner class which i used as a builder for MockWebServiceServer.
protected class MultiStepMockBuilder {
private List<Object> mockActions = new ArrayList<Object>();
private WebServiceGatewaySupport gatewaySupport;
protected MultiStepMockBuilder(WebServiceGatewaySupport gatewaySupport) {
this.gatewaySupport = gatewaySupport;
}
protected MultiStepMockBuilder exception(RuntimeException exception) {
mockActions.add(exception);
return this;
}
protected MultiStepMockBuilder resource(Resource resource) {
mockActions.add(resource);
return this;
}
protected MockWebServiceServer build() {
MockWebServiceServer server = MockWebServiceServer.createServer(gatewaySupport);
for(Object mock: mockActions) {
if (mock instanceof RuntimeException) {
server.expect(anything()).andRespond(withException((RuntimeException)mock));
}
else if (mock instanceof Resource)
{
try
{
server.expect(anything()).andRespond(withSoapEnvelope((Resource) mock));
} catch (IOException e) {e.printStackTrace();}
}
else
throw new RuntimeException("unusuported mock action");
}
return server;
}
}
}
So i can now do something like this to create mock:
return new MultiStepMockBuilder(gatewaySupport).resource(success).exception(new WebServiceIOException("reserve timeout"))
.resource(invalidMsisdn)
.build();
The issue i have with this implementation is dependence on instanceof operator which i never use outside of equals.
Is there an alternative way to instanceof operator in this scenario ? From the questions on topic of instanceof everybody argues it should only be used within equals and therefore i have feeling that this is 'dirty' solution.
Is there an alternative to instanceof operator, within Spring or as a different design, while keeping fluent interface for mocks creation ?
I don't know Spring well enough to comment specifically on this particular area, but to me, this just seems like a design thing. Generally, when you are faced with using instanceof, it means that you need to know the type, but you don't have the type. It is generally the case that we might need to refactor in order to achieve a more cohesive design that avoids this kind of problem.
The root of where the type information is being lost, is in the List of mock actions, which are currently just being stored as a List of Objects. One way to help with this then, is to look at the type of the List and consider if there is a better type that could be stored in the List that might help us later. So we might end up with a refactoring something like this.
private List<MockAction> mockActions = new ArrayList<MockAction>();
Of course, then we have to decide what a MockAction actually is, as we've just made it up. Maybe something like this:
interface MockAction {
void performAction(MockWebServiceServer server);
}
So, we've just created this MockAction interface, and we've decided that instead of the caller performing the action - we're going to pass the server into it and ask the MockAction to perform itself. If we do this, then there will be no need for instanceof - because particular types of MockActions will know what they contain.
So, what types of MockActions do we need?
class ExceptionAction implements MockAction {
private final Exception exception;
private ExceptionAction(final Exception exception) {
this.exception = exception;
}
public void performAction(final MockWebServiceServer server) {
server.expect(anything()).andRespond(withException(exception);
}
}
class ResourceAction implements MockAction {
private final Resource resource;
private ResourceAction(final Resource resource) {
this.resource = resource;
}
public void performAction(final MockWebServiceServer server) {
/* I've left out the exception handling */
server.expect(anything()).andRespond(withSoapEnvelope(resource));
}
}
Ok, so now we've gotten to this point, there are a couple of loose ends.
We're still adding exceptions to the list of MockActions - but we need to change the add methods to make sure we put the right thing in the list. The new versions of these methods might look something like this:
protected MultiStepMockBuilder exception(RuntimeException exception) {
mockActions.add(new ExceptionAction(exception));
return this;
}
protected MultiStepMockBuilder resource(Resource resource) {
mockActions.add(new ResourceAction(resource));
return this;
}
So, now we've left our interface the same, but we're wrapping the resource or exception as they're added to the list so that we have the type specificity we need later on.
And then finally, we need to refactor our method that actually makes the calls, which now looks something like this - which is much simpler and cleaner.
protected MockWebServiceServer build() {
MockWebServiceServer server = MockWebServiceServer.createServer(gatewaySupport);
for(MockAction action: mockActions) {
action.performAction(server);
}
return server;
}
I am building a HTTP server for my android device.
I am using a lot of IF-ELSE statements to handle differnt requests.
As I will be sharing my code with other people for later use, I will have to make it as legible as possible. Right now, I can't even read my code with ease.
I think the problem comes from using a lot of IF-ELSE statements in one class.
For example.
if(purpose.equals("readProfile"){
.....
}
else if(purpose.equals("writeProfile"){
.....
}
....
I tried classifying them in category and ordered the conditions according to their category. But not a lot of legebility was improved.
Then I tried writing short comments infront of each conditions. But this made even more mess.
What can be done to increase legibility for conditional statements?
As Luiggi Mendoza stated, this is a follow up of a previous question...
If you are using Java 7, you can use a switch-case statement for strings
//month is a String
switch (month.toLowerCase()) {
case "january":
monthNumber = 1;
break;
//partsleft out for sake of brevity ..
default:
monthNumber = 0;
break;
}
(excerpt from the Oracle Java Tutorials, referenced above.)
Refactoring
However, this huge if-else is just part of the problem. As this seems to be a structure growing over time, I'd recommend a thorough refactoring, and using what seems to me is a Strategy pattern. You should:
Formulate an interface which covers the boundaries for all the use cases:
interface MyStrategy {
void execute(MyInputContext input, MyOutputContext output);
}
(using a void method with MyInputContext and MyOutputContext are just one approach, this is just an example, but to handle requests that have responses, this makes sense, just like how Servlets work)
Refactor the content of the big IF-ELSE statement into instances of this interface (these will be the strategies):
//VERY simplified...
class ReadProfileStrategy implements MyStrategy {
void execute(MyInputContext input, MyOutputContext output) {
//do the stuff that was in the if-else block in the "readProfile" part
}
}
//... at the branching part:
MyInputContext input; //build this here
MyOutputContext output; //build this here
switch (purpose) {
case "readProfile":
// no need to always instantiate this, it should be stateless...
new ReadProfileStrategy().execute();
break;
//... left out for sake of brevity
}
Refactoring step 2
If this is done, you can add the string IDs to the interface, and the instances themselves, and get rid of the if-else or switch statement altogether, you could create a Map populated even through an IOC container (like), to be up to date, and completely flexible.
class ReadProfileStrategy implements MyStrategy {
String getID() {
return "readProfile";
}
void execute(MyInputContext input, MyOutputContext output) {
//do the stuff that was in the if-else block in the "readProfile" part
}
}
In the class when requests are processed
private final Map<String, MyStrategy> strategyMap; //fill the map using your favorite approach, like using Spring application context, using the getCode() to provide the key of the map
In the processing logic:
MyStrategy strategy = strategyMap.get(purpose);
if(strategy!=null) {
strategy.execute();
}
else {
//handle error here
}
This may be out of scope, but just an observation
try using
if("readProfile".equals(purpose){} instead of
if(purpose.equals("readProfile"){}.
It will help to avoid null pinter exception
Enums can help - you can also add functionality to them.
public void test(String purpose) {
if (purpose.equals("readProfile")) {
// Read.
} else if (purpose.equals("writeProfile")) {
// Write.
}
}
enum Purpose {
readProfile {
#Override
void doIt() {
// Read.
}
},
writeProfile {
#Override
void doIt() {
// Write.
}
};
abstract void doIt();
}
public void test2(String purpose) {
Purpose.valueOf(purpose).doIt();
}
You can try using some kind of Action-Interface with implementations for each block and preload a map with concrete Implementations of this action.
interface Action {
void execute();
}
Map<String, Action> actions = new HashMap<>();
actions.put("readProfile", new Action() { ... });
actions.put("writeProfile", new Action() { ... });
actionMap.get(purpose).execute();
That will lower your cyclomatic complexity as well. Of course you should preload the map only once.
Well, If it makes sense to separate code inside if-else condition to another class, perhaps use Factory pattern. Also make all separated classes implement common interface (eg: MyActivity.class) with a method such as execute().
Factory decides what object (ReadProfile.class, WriteProfile.class etc.) has to be created based on the string you pass and then call execute() method.
MyActivity obj = MyFactory.createMyActivity(String)
obj.execute(...);