I'm designing a text-based adventure game for a school progress. I have each "level" set up as a class, and each explorable area (node) as a method within the appropriate class.
What's messing with me is the code to move from one node to another. Because each node is connected to up to four other nodes, I have to repeat an extremely similar block of code in each method.
What I'd prefer to do is include an array of methods at the beginning of each node, like this:
public static void zero()
{
... adjacentNodes[] = {one(), two(), three(), four()};
}
And then send that array to a generic method, and have it send the player to the right node:
public static void move(...[] adjacentNodes, int index)
{
adjacentNodes[index];
}
I simplified my code, but that's the general idea. Is this possible?
Whenever you think of pointer-to-function, you translate to Java by using the Adapter pattern (or a variation). It would be something like this:
public class Node {
...
public void goNorth() { ... }
public void goSouth() { ... }
public void goEast() { ... }
public void goWest() { ... }
interface MoveAction {
void move();
}
private MoveAction[] moveActions = new MoveAction[] {
new MoveAction() { public void move() { goNorth(); } },
new MoveAction() { public void move() { goSouth(); } },
new MoveAction() { public void move() { goEast(); } },
new MoveAction() { public void move() { goWest(); } },
};
public void move(int index) {
moveActions[index].move();
}
}
Just have your nodes be objects that all adhere to the same interface, then you'll be able to call their methods reliably.
Since Java does not have the concept of methods as first-class entities, this is only possible using reflection, which is painful and error-prone.
The best approximation would probably be to have the levels as enums with a per-instance implementation of a method:
public enum Level1 implements Explorable{
ROOM1 {
public void explore() {
// fight monster
}
}, ROOM2 {
public void explore() {
// solve riddle
}
}, ROOM3 {
public void explore() {
// rescue maiden
}
};
}
public interface Explorable{
public abstract void explore();
}
public static void move(Explorable[] adjacentNodes, int index)
{
adjacentNodes[index].explore();
}
However, this is a bit of an abuse of the enum concept. I wouldn't use it for a serious project.
Your design has fundamental flaws. Normal OO design would have each "level" be an object (of Class 'level' or something like it). each 'explorable area' would also be an object, contained within the level object - maybe of class ExplorableArea. The 'explorable areas' can be different kinds, in which case you make them different subclasses of ExplorableArea.
Try thinking about solutions without reflection. It's can be enums, for example.
I arrive late at the party with one possible approach, now you can use java.util.function (link) for this kind of problem.
To literally answer the question, regardless of its correctness, or applicability, here a possible version:
public static void zero()
{
Function<World, World> one = (World start) -> RoomWithMonster.in(start);
Function<World, World> two = (World start) -> EmptyRoom.in(start);
Function<World, World> three = (World start) -> RoomWithMonster.in(start);
Function<World, World> four = (World start) -> Treasure.in(start);
List<Function<World, World>> adjacentNodes = List.of(one, two, three, four);
return adjacentNodes;
}
public static void move(List<Function<World, World>> possibleNodes, int index)
{
World beginning = World.start();
World end = possibleNodes.get(index).apply(beginning);
}
This approach prefer immutability and add a little World class to abstract away the state of the game but still maintaining the question you wanted.
NB: fortunately now the reflection comments are obsolete!
You can use Reflection class to create an array of methods.
http://java.sun.com/developer/technicalArticles/ALT/Reflection/
Related
public class InventorySetDAO{
public LinkedList<CustomInventory> inventories = new LinkedList<>();
}
I am developing plugin that add/delete data in arraylist. and There's too much reference on the arrayList from other class.
Class InventoryItemModifier:
public class InventoryItemModifier {
InventorySetDAO inventorySetDAO;
public InventoryItemModifier(InventorySetDAO inventorySetDAO){
this.inventorySetDAO = inventorySetDAO;
}
public void addItem(ItemStack itemStack, ClickAction click, RequiredItems requiredItems) {
Bukkit.getPluginManager().callEvent(new ItemAddedEvent());
inventorySetDAO.getLastInventory().addItem(itemStack, click, requiredItems);
}
public void removeItem(ItemStack itemStack){
Bukkit.getPluginManager().callEvent(new ItemRemovedEvent());
inventorySetDAO.getLastInventory().removeItem(itemStack);
}
}
Class InventoryPlayerAccessor:
public class InventoryPlayerAccessor {
InventorySetDAO inventorySetDAO;
public boolean openPage(Player player) {
if (!inventories.isEmpty()) {
inventories.get(0).openInventory(player);
return true;
}
return false;
}
public boolean openPage(Player player, int index) {
if (!inventories.isEmpty()) {
if (index >= 0 && index < inventories.size()) {
inventories.get(index).openInventory(player);
return true;
}
}
return false;
}
}
I think there is risk of manipualte arrayList unproperly, so I think arrayList must be in a class and provide methods(add/insert/remove...) but if then there are too much responsibilities in that class.
I tried to seperate them into multiple classes, but it doesn't seem to solve this problem. is there a way to reduce reliance on arrayList, or efficient way to encapsulate arrayList?
To reduce each classes reliance on the underlying ArrayList (or just List), you could think about using the composite pattern instead of the DAO pattern. This would hide all/most of the logic to the InventorySet class.
class InventorySet {
private final List<CustomInventory> inventories = new ArrayList<>();
public void addItem() { }
public void removeItem() { }
}
Then, you can just keep your InventoryPlayerAccessor (maybe rename) but compose it of a InventorySet for easy access.
class InventorySetView {
void open();
}
I am designing a game with multiple levels. I have a setup class that sets up the board based on the argument it receives, which indicates which level it should set up. Here is the class:
public class BoardState {
public BoardState(InitialState state) {
switch (state) {
case EMPTY:
setupEmptyState();
break;
case INTEGRATIONTEST:
setupIntegrationTestState();
break;
case LEVEL_1:
setupLevelOne();
break;
case LEVEL_2:
setupLevelTwo();
break;
default:
throw new Error("Invalid level selection");
}
}
private void setupEmptyState() { }
private void setupIntegrationTestState() { }
private void setupLevelOne() { }
private void setupLevelTwo() { }
}
This works fine, but every time I add a new level I have to add code in three places: The InitialState enum which defines the list of accepted states, the switch statement in the constructor, and the body of the class, where I have to add a method to set up the level in question.
One nice thing that I want to keep is the fact that my GUI automatically populates with a new button for each level I add based on the enum defining the list of levels.
How can I refactor this code so that there is less overhead associated with adding a new level?
Often when you need to reduce code duplication, an interface arise. This time (based on your comment in OP) it seems you need to add different objects to the board depending on which level you are:
import java.util.List;
public interface LevelSettings {
List<GameObject> startingObjects();
}
Now, BoardState looks like that (no more setupX() methods)
import java.util.List;
public class BoardState {
private final List<GameObject> gameObjects;
public BoardState(LevelSettings settings) {
this.gameObjects = settings.startingObjects();
}
}
Since you also specified it is nice for you to have an enum to dynamically creates buttons on the GUI, one can combine the best of both world (interface and enum) by implementing the interface in an enum...
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public enum InitialState implements LevelSettings {
EMPTY {
#Override
public List<GameObject> startingObjects() {
return Collections.emptyList();
}
},
INTEGRATIONTEST {
#Override
public List<GameObject> startingObjects() {
GameObject g1 = new GameObject("dummy 1");
GameObject g2 = new GameObject("dummy 2");
return Arrays.asList(g1, g2);
}
},
LEVEL_1 {
#Override
public List<GameObject> startingObjects() {
//read a config file to get the starting objects informations
//or also hardcoded (not preferred)
}
},
LEVEL_2 {
#Override
public List<GameObject> startingObjects() {
//read a config file to get the starting objects
//or also hardcoded (not preferred)
}
};
}
And that's it basically. If you need to add LEVEL_3 do it in InitialState and everything will follow.
Going one step further
From here it goes beyond what you requested, feel free to ignore this part if you are not convinced.
As a good practice I would store these configurations only in config files to reduce even more the code duplication and gain in flexibility:
import java.util.List;
public enum InitialState implements LevelSettings {
EMPTY {
#Override
public List<GameObject> startingObjects() {
return readFromFile("empty.level");
}
},
INTEGRATIONTEST {
#Override
public List<GameObject> startingObjects() {
return readFromFile("integration_test.level");
}
},
LEVEL_1 {
#Override
public List<GameObject> startingObjects() {
return readFromFile("1.level");
}
},
LEVEL_2 {
#Override
public List<GameObject> startingObjects() {
return readFromFile("2.level");
}
};
private static List<GameObject> readFromFile(String filename) {
//Open file
//Serialize its content in GameObjects
//return them as a list
}
}
So that when you decide to add a new level you actually only need to know the filename in which the level's configuration is stored.
Going another step further
What you will see there is really tricky and I don't advice you to use it in production code (but it reduces code duplication) !
import java.util.List;
public enum InitialState implements LevelSettings {
EMPTY, INTEGRATIONTEST, LEVEL_1, LEVEL_2;
#Override
public List<GameObject> startingObjects() {
return readFromFile(this.name() + ".level");
}
private static List<GameObject> readFromFile(String filename) {
//Open file
//Serialize its content in GameObjects
//return them as a list
}
}
Here we rely on enum names themselves to find the corresponding correct file. This code works because it is based on the convention that the files are named accordingly to the enum names with the ".level" extension. When you need to add a new level, just add it to the enum and that's it...
You could use inheritance, polymorphism is the keyword here.
Set up your InitialState class as abstract base class (or interface if you have no common fields) and define a method public abstract void setup();.
abstract class InitialState {
public abstract void setup();
}
Then, for each of your original switch cases, derive a class from your base class, for example LevelOne, and implement its specific by overriding setup().
class LevelOne extends InitialState {
#Override
public void setup() {
// The code from "setupLevelOne()" goes here
}
}
Your BoardState class reduces to this:
public class BoardState {
public BoardState(InitialState state) {
// At runtime, the right method of the actual
// state type will be called dynamically
state.setup();
}
}
However, if you need to set interal state of your BoardState class, consider defining the setup method as public abstract void setup(BoardState boardState), so you can access its getter and setter methods.
This appraoch could also foster reuse of code, as you could add several abstract layers for different types of levels.
well you can refactor all of the work into a one method.
say it takes an int as the ID of the level, while it loads a JSON file containing structured information of each level, and creates the given level.
for example :
"levels" : [
"level" : {
"id" : "001",
"size" : "200",
"difficulty" : "2"
},
"level" : {
"id" : "002",
"size" : "300",
"difficulty" : "3"
}
]
then, in your code:
public void setupLevel(int id) throws levelNotFoundException{
//somehow like this
Document doc = parse("levels.json");
for(element elm: doc.get("levels")){
if(Integer.parseInt(elm.get("id")).equals(id)){
//setup your level
}
}
}
and then somewhere you call your method:
int levelId = getNextLevel();
try{
setupLevel(levelId);
} catch (LevelNotFoundException e){e.printStackTrace();}
or you can use XML, or simply hard code it, and store all levels in an array
Thanks for viewing my question, which I have not successfully found an answer for in my searches/books. I'm learning java by writing a roguelike, but I think this question is more java-related than game-related. Feel free to educate me if I'm wrong.
I have similar classes that I want to each have specific abilities. The abilities are enum singletons with a set of standard method names that I would pass the Actor to - I wanted to avoid implementing methods from an interface in every Actor class, and just really liked the envisioned use of this approach as I go forward. I come from a shell/perl background and can't tell if I'm just not thinking OOP, or if I'm on to something useful and don't have the skills yet to pull it off.
addAbility(String) in StdActor is where it finally broke in this experiment.
Question is - am I doing something wrongheaded here? If not, how could I implement this?
interface for manipulating abilities:
public interface ActorAbility {
// doesn't work, but need something to enable
// instance retrieval for addAbility...
public ActorAbility getInstance();
public void act(Actor actor);
public boolean isTickable();
}
sanitized implementation of interface:
public enum ActorMove implements ActorAbility {
INSTANCE;
private ActorMove() {
}
public ActorAbility getInstance() {
return INSTANCE;
}
public void act(Actor actor) {
log.debug("Move");
}
public boolean isTickable() {
return true;
}
}
sanitized use of the ability. trial and error run amock. addAbility(String) broken, copy/paste from SO and elsewhere. it probably needs to be nuked from orbit.
public class StdActor implements Actor {
private HashSet<ActorAbility> abilities = new HashSet<>();
// this whole method is wrecked
public void addAbility(String ability) {
// Class<? extends ActorAbility> action; // in a maze of twisty passages...
ActorAbility actionInstance = null;
try {
// action = Class.forName("game3.Actors.Abilities." + ability);
actionInstance = ActorAbility.valueOf("game3.Actors.Abilities."
+ ability);
} catch (Exception e) {
e.printStackTrace();
}
this.abilities.add(actionInstance);
}
}
use case:
public class StdCharClass extends StdActor {
public StdCharClass() {
// I like this because it's clean and easily
// changeable
addAbility("ActorMove");
}
}
future planned use:
HashSet<ActorAbility> abilities = actor.getAbilities();
for (ActorAbility ability : abilities) {
if (ability.isTickable()) {
ability.act(actor);
}
}
Thanks!
EDIT:
Thanks for such a quick comment, JB. I tried what you suggested and it appears to do what I was hoping. It appears I was just off in the weeds and needed to be pulled back.
new class:
public enum Ability {
MOVE(ActorMove.INSTANCE), FIGHT(ActorFight.INSTANCE);
private ActorAbility ability;
private Ability(ActorAbility abilityClass) {
this.ability = abilityClass;
}
public ActorAbility getAbility() {
return this.ability;
}
}
StdActor:
public class StdActor implements Actor {
private HashSet<Ability> abilities = new HashSet<>();
public void addAbility(Ability ability) {
this.abilities.add(ability);
}
subclass:
public class StdCharClass extends StdActor {
public StdCharClass() {
addAbility(Ability.MOVE);
}
}
and finally, usage:
HashSet<Ability> abilities = bob.getAbilities();
for (Ability ability : abilities) {
ActorAbility abilityClass = ability.getAbility();
if (abilityClass.isTickable()) {
abilityClass.act(bob);
}
}
output!
12:44:15.835 [main] DEBUG ActorMove - Move
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.
How to pass outer anon class ref to a method in an inner anon class in Java?
I have a method that makes async call to a server - sendCall(some_args, callback). The callback is represented by anonymous class (let's name it OuterAnon) and contains a method for failure case. Inside this method a message box is created and sendCall() is called each time OK button is pressed. So I need to pass OuterAnon to the method again.
Here is a code to demonstrate what I mean:
private void sendCall(MyData data, OuterAnon<Boolean> callback){/*...*/}
private void myCall(final MyData data) {
sendCall(data, new OuterAnon<Boolean>() {
public void onFailure(Throwable throwable) {
final OuterAnon<Boolean> callback = this; //how to avoid this?
MessageBox.show(throwable.getMessage(), new MessageListener() {
public void process(MessageBox.OnClick action) {
if (action == MessageBox.OnClick.OK) {
sendCall(new MyData("resend?"), callback);
}
}
});
}
}
});
}
As you noticed, I take a ref for callback here:
final OuterAnon<Boolean> callback = this;
and use it here:
sendCall(new MyData("resend?"), callback);
But I want to avoid ref creation and pass callback like:
sendCall(new MyData("resend?"), this); //at the moment we point to MessageListener instead of OuterAnon.
Is there any way to do it in Java?
It's hard for us to fix since you've only shown incomplete code with classes that aren't supplied, so I don't know if this example is syntactically correct. That being said, a refactoring like this may suit your needs:
private void myCall(final MyData data)
{
sendCall(data, new OuterAnon<Boolean>()
{
public void onFailure(Throwable throwable)
{
showErrorMessage(throwable);
}
});
}
private void showErrorMessage(Throwable throwable)
{
MessageBox.show(throwable.getMessage(), new MessageListener()
{
public void process(MessageBox.OnClick action)
{
if (action == MessageBox.OnClick.OK)
{
sendCall(new MyData("resend?"));
}
}
});
}
private void sendCall(MyData data)
{
sendCall(data, this);
}
In general, I think it's a usually good idea to abstract code out of anon inner classes and into their own method on the enclosing class. It's now testable, reusable, and more readable, IMO.
If you really need to specify the onFailure inside the inner class the way you showed the code, and if you need to use that specific reference for callback, and you need to code this way...
Let's answer the question: no.
In my attempts, I've achieved 3 ways to access the anon-inner-least instance inside the anon-inner-most instance, but I think that none satisfies what you expect.
In that case, the anon-inner-most doesn't have a reference to the anon-inner-least: as you said, the this now points to the anon-inner-least.
Also, I tried to search at the java specification, but couldn't find exactly the answer to the question - if someone find the answer there, please contribute.
My try:
import java.util.ArrayList;
import java.util.LinkedList;
public abstract class AnonTest {
public static void main(String[] args) {
new ArrayList<Object>() {
private static final long serialVersionUID = -5986194903357006553L;
{
// initialize inner anon class
add("1");
}
// Way 1
private Object thisReference1 = this;
// Way 2
private Object getThisReference2() {
return this;
}
#Override
public boolean equals(Object obj) {
// Way 3
final Object thisReference3 = this;
new LinkedList<Object>() {
private static final long serialVersionUID = 900418265794508265L;
{
// initialize inner inner anon class
add("2");
}
#Override
public boolean equals(Object innerObj) {
// achieving the instance
System.out.println(thisReference1);
System.out.println(getThisReference2());
System.out.println(thisReference3);
System.out.println(this);
System.out.println();
// achieving the class
System.out.println(thisReference1.getClass());
System.out.println(getThisReference2().getClass());
System.out.println(thisReference3.getClass());
System.out.println(this.getClass());
System.out.println(this.getClass().getEnclosingClass());
return super.equals(innerObj);
}
}.equals("");
return super.equals(obj);
}
}.equals("");
}
}