I am working on a project in java and am quite new to the language and OOP. My dilema is that I want to carry out a task/function from a specific class based on the value of a variable.
This is kind of what I am trying to achieve.
class mainClass{
String option;
public static void main(String[] args) {
mainClass main = new mainClass();
}
mainClass(){
secondClass sC = new secondClass();
thirdClass tC = new thirdClass();
switch (option){
case "1" :
sC.doSomething();
case "2" :
tC.doSomething();
}
}
}
class secondClass{
void doSomething(){
System.out.println("1");
}
}
class thirdClass{
void doSomething(){
System.out.println("2");
}
}
The reason I don't want to do this, is because if I want to add a fourth, fifth, sixth class etc... I would have to update the switch.
I tried using a hashmap. Where I assigned secondClass the key of "1". But then I would have to cast the object, but this brings me back to the original headache of not knowing what class would need to be called in advance.
So then I tried using a hashmap like this,
HashMap<String, Object> map = new HashMap<String, Object>();
Which I could then do map.get("1") but then now I can't call any of the methods for the class in question.
If I need to use a large switch statement I will, but I am actively seeking a more efficient alternative.
You were right to use a Map but you were also right to balk at casting. However, nowadays with generics you can get around all that:
interface DoesSomething {
// An object implementing this interface does something.
public void doSomething();
}
// Class that does something.
class FirstClass implements DoesSomething {
#Override
public void doSomething() {
// What FirstClass does.
}
}
// Another class that does something.
class SecondClass implements DoesSomething {
#Override
public void doSomething() {
// What SecondClass does.
}
}
// How I know what to do. Map the string to a DoesSomethng.
Map<String, DoesSomething> whatToDo = new HashMap<>();
{
// Populate my map.
whatToDo.put("1", new FirstClass());
whatToDo.put("2", new SecondClass());
}
public void doSomethingDependingOnSomething(String something) {
// Look up the string in the map.
DoesSomething toDo = whatToDo.get(something);
// Was it in there?
if (toDo != null) {
// Yes! Make it do it's thing.
toDo.doSomething();
}
}
If you want to avoid using Reflection (wich is discouraged here), you should consider a simple SAM-Interface:
public interface Doable { public void doSomething(); }
and have all classes implement the interface (no other changes required in these classes) and having a Map<String, Doable> and calling
if (map.containsKey(option)) map.get(option).doSomething();
// Or (may be a little faster)
Doable opt = map.get(option);
if (opt != null) opt.doSomething();
If your implementations have different methods, you'll most likely be bound to use Reflection to get the declared methods and compare by String.
Related
I have method which returns sql query as string. This method consider parameter 'level' , based on this
parameter there are multiple if else statement like
if(level.equals("A1")){
// add some field in query
}
else if (level.equals("A2"))
{
// add some field and logic in query
}
..
so on
In future number of levels are going to increase and I don`t want to write pesky if else statements so I am looking for cleaner and maintainable design approach for this scenario.
I am thinking of strategy design pattern but not sure whether it is best for this scenario.
You have several options:
if/else if as you've indicated.
A switch statement.
A Map with the keys being the level string and the values being references of a functional interface type (Runnable or an appropriate interface from java.util.function, or your own) you can initialize with method references, lambdas, anonymous class instances, or even concrete class instances as required.
Here's an example of that last option, using the most basic functional interface, Runnable, with method references:
import java.util.Map;
import java.util.HashMap;
public class Example {
private Map<String, Runnable> handlers;
public Example() {
this.handlers = new HashMap<String, Runnable>();
this.handlers.put("A1", this::a1action);
this.handlers.put("A2", this::a2action);
}
private void a1action() {
System.out.println("Action A1");
}
private void a2action() {
System.out.println("Action A2");
}
public void handleAction(String action) {
Runnable handler = this.handlers.get(action);
if (handler == null) {
System.out.println("No handler for '" + action + "'");
} else {
handler.run();
}
}
public static void main(String[] args) throws Exception {
Example ex = new Example();
ex.handleAction("A1");
ex.handleAction("A2");
ex.handleAction("A3");
}
}
You can use an enum to list the operations and use valueOf to work out which to use.
enum Levels {
A1 {
#Override
void doSomethingToTheQuery(Query q) {
// add some field in query
}
},
A2{
#Override
void doSomethingToTheQuery(Query q) {
// add some field and logic in query
}
};
abstract void doSomethingToTheQuery(Query q);
}
public void test() {
// Lookup the level and do hack the query.
Levels.valueOf("A1").doSomethingToTheQuery(q);
}
You can then add new levels just by adding to the enum.
I come from a Python background and in Python you can pass in the type of an object as a parameter. But in Java you cannot do this, any tips on how to get something like this working?
private void function(Type TypeGoesHere)
Stock s = new TypeGoesHere();
s.analyze();
}
Java does not support Python’s way of referencing functions and classes. To achieve this behaviour, you have to use two advanced techniques: generics and reflection. Explaining these concepts is beyond the scope of a SO answer. You should read a Java guide to learn about them.
Yet here is an example how this would look like, assuming that the given class has a no-argument constructor:
public <T extends Stock> void analyzeNewStock(Class<T> clazz) throws Exception {
Stock s = clazz.newInstance();
s.analyze();
}
Then call this function with analyzeNewStock(MyStock.class).
As this is a rather complicated and error-prone approach, you’d rather define an interface that creates Stock instances:
public interface StockProvider {
Stock createStock(String value);
}
public class MyStockProvider implements StockProvider {
private final String valueTwo;
public MyStockProvider(String valueTwo) {
this.valueTwo = valueTwo;
}
#Override
public Stock createStock(String valueOne) {
return new MyStock(valueOne, valueTwo);
}
}
public class MyOtherClass {
public void analyzeNewStock(StockProvider provider) {
provider.createStock("Hi!").analyze();
}
public static void main(String[] args) {
analyzeNewStock(new MyStockProvider("Hey!"));
}
}
In Java you can pass a Class. You can do it like this:
private void function(Class c)
This is not very common procatice though. You can probably get wha you need by looking into Strategy pattern, or proper use of Object Oriented Programming (polymorphism).
If you are looking for a way to build some objects, look into Factory pattern.
If you want to create a generic class- look into this detailed answer: https://stackoverflow.com/a/1090488/1611957
You could use generics. For example:
private <T> void function(Class<T> clazz) {
try{
T t = clazz.newInstance();
//more code here
}catch(InstantiationException | IllegalAccessException ex){
ex.printStackTrace();
}
}
The Class<T> clazz shows what type to instantiate. The try/catch is just to prevent errors from stopping your code. The same idea is expanded in this SO post. More info here.
However, I'm not really sure why you would want to do this. There should easily be a workaround using a simple interface. Since you already know that you want an object with type Stock, you could pass an implementation of the interface. For example:
//interface to implement
public interface Stock {
public void analyze();
}
//rewrite of function
private void function(Stock s){
s.analyze();
}
And using two ways to call function:
//first way
public class XYZ implements Stock{
public void analyze(){
//some code here
}
}
//calling the function
function(new XYZ());
//second way
function(new Stock(){
public void analyze(){
//your code here
}
});
There is a given method. How can we refactor it?
public void foo(Factor o){
if(o.matches(constant1)){
method1();
}else if(o.matches(constant2)){
method2();
}
}else if(o.matches(constant3)){
method3();
}
....
}
This is something called a code smell. You would want to use what's called the "command pattern" which is a design pattern to refactor this code. On mobile now, will update with an example when I get to my desk today.
Edit: Here we go.
So the command pattern is a design pattern used for this exact scenario. What you need to first do is create a command Interface.
public interface MyCommand {
public void execute();
}
Great. Next you create Command objects that hold all of your method data.
public class Method1Command implements MyCommand {
public MyVariable var;
public Method1Command(<your arguments to create method>)
{
// instantiate your command
}
public void execute()
{
// what your current method1() is;
}
}
And then you just create some kind of private class in your Main to create a HashMap of all of the commands, keyed with the value of "ConstantX."
private static Map<String, MyCommand> getMyCommands()
{
Map<String, MyCommand> commandList = new HashMap<String, MyCommand>();
MyCommand c;
c = new Method1Command();
commandList.put("constant1", c);
c = new Method2Command();
commandList.put("constant2", c);
c = new Method3Command();
commandList.put("constant3", c);
return commandList();
}
Then, in the refactored method you would simply do the following:
public void foo(Factor o)
{
cl.get(o).execute();
}
This, however, assumes that o has some kind of toString method inside it, or if you have some method inside of o that you use to get the command it would be something like this: cl.get(o.getMyCommand()).execute();
You can make an array containing the constants, and a Map containing constant-string pairs (String is the name of the method), use reflection and do something like:
public void foo(Factor o) {
for(int i = 0; i < constans.length; i++) {
if(o.matches(constant)) {
Method method =
YourClass.class.getMethod(myMap.get(constant), null);
method.invoke(null);
}
}
}
So, in a single parent inheritance model what's the best solution for making code extensible for future changes while keeping the same interface (I'd like to emphasize the fact that these changes cannot be known at the time of the original implementation, the main focus of my question is to explore the best mechanism/pattern for supporting these changes as they come up)? I know that this is a very basic OO question and below I provide example of how I've been going about it, but I was wondering if there a better solution to this common problem.
Here's what I've been doing (the example code is in Java):
In the beginning, the following two classes and interface are created:
public class Foo
{
protected int z;
}
public interface FooHandler
{
void handleFoo(Foo foo);
}
public class DefaultFooHandler implements FooHandler
{
#Override
public void handleFoo(Foo foo)
{
//do something here
}
}
The system uses variables/fields of type FooHandler only and that object (in this case DefaultFooHandler) is created in a few, well-defined places (perhaps there's a FooHandlerFactory) so as to compensate for any changes that might happen in the future.
Then, at some point in the future a need to extend Foo arises to add some functionality. So, two new classes are created:
public class ImprovedFoo extends Foo
{
protected double k;
}
public class ImprovedFooHandler extends DefaultFooHandler
{
#Override
public void handleFoo(Foo foo)
{
if(foo instanceof ImprovedFoo)
{
handleImprovedFoo((ImprovedFoo)foo);
return;
}
if(foo instanceof Foo)
{
super.handleFoo(foo);
return;
}
}
public void handleImprovedFoo(ImprovedFoo foo)
{
//do something involving ImprovedFoo
}
}
The thing that makes me cringe in the example above is the if-statements that appear in ImprovedFooHandler.handleFoo
Is there a way to avoid using the if-statements and the instanceof operator?
First of all the code you wrote won't work.
Each time you see instanceof and if...else together be very careful. The order of these checks is very important. In your case you'll never execute handleImpovedFoo. Guess why :)
It's absolutely normal you have these instanceof statements. Sometimes it's the only way to provide different behavior for a subtype.
But here you can use another trick: use simple Map. Map classes of foo-hierarchy to instances of fooHandler-hierarchy.
Map<Class<? extends Foo>, FooHandler> map ...
map.put( Foo.class, new FooHandler() );
map.put( ImprovedFoo.class, new ImprovedFooHandler() );
Foo foo ...; // here comes an unknown foo
map.get( foo.getClass() ).handleFoo( foo );
The best way of handling this depends too much on the individual case to provide a general solution. So I'm going to provide a number of examples and how I would solve them.
Case 1: Virtual File System
Clients of your code implement virtual file systems which enable them to operate any sort of resource which can be made to look like a file. They do so by implementing the following interface.
interface IFolder
{
IFolder subFolder(String Name);
void delete(String filename);
void removeFolder(); // must be empty
IFile openFile(String Name);
List<String> getFiles();
}
In the next version of your software you want to add the ability to remove a directory and all it contents. Call it removeTree. You cannot simply add removeTree to IFolder because that will break all users of IFolder. Instead:
interface IFolder2 implements IFolder
{
void removeTree();
}
Whenever a client registers an IFolder (rather then IFolder2), register
new IFolder2Adapter(folder)
Instead, and use IFolder2 throughout your application. Most of your code should not be concerned with the difference about what old versions of IFolder supported.
Case 2: Better Strings
You have a string class which supports various functionality.
class String
{
String substring(int start, end);
}
You decide to add string searching, in a new version and thus implement:
class SearchableString extends String
{
int find(String);
}
That's just silly, SearchableString should be merged into String.
Case 3: Shapes
You have a shape simulation, which lets you get the areas of shapes.
class Shape
{
double Area();
static List<Shape> allShapes; // forgive evil staticness
}
Now you introduce a new kind of Shape:
class DrawableShape extends Shape
{
void Draw(Painter paint);
}
We could add a default empty Draw method to Shape. But it seems incorrect to have Shape have a Draw method because shapes in general aren't intended to be drawn. The drawing really needs a list of DrawableShapes not the list of Shapes that is provided. In fact, it may be that DrawableShape shouldn't be a Shape at all.
Case 4: Parts
Suppose that we have a Car:
class Car
{
Motor getMotor();
Wheels getWheels();
}
void maintain(Car car)
{
car.getMotor().changeOil();
car.getWheels().rotate();
}
Of course, you know somewhere down the road, somebody will make a better car.
class BetterCar extends Car
{
Highbeams getHighBeams();
}
Here we can make use of the visitor pattern.
void maintain(Car car)
{
car.visit( new Maintainer() );
}
The car passes all of its component parts to calls into ICarVisitor interface allowing the Maintainer class to maintain each component.
Case 5: Game Objects
We have a game with a variety of objects which can be seen on screen
class GameObject
{
void Draw(Painter painter);
void Destroy();
void Move(Point point);
}
Some of our game objects need the ability to perform logic on a regular interval, so we create:
class LogicGameObject extends GameObject
{
void Logic();
}
How do we call Logic() on all of the LogicGameObjects? In this case, adding an empty Logic() method to GameObject seems like the best option. Its perfectly within the job description of a GameObject to expect it to be able to know what to do for a Logic update even if its nothing.
Conclusion
The best way of handling this situations depends on the individual situation. That's why I posed the question of why you didn't want to add the functionality to Foo. The best way of extending Foo depends on what exactly you are doing. What are you seeing with the instanceof/if showing up is a symptom that you haven't extended the object in the best way.
In situations like this I usually use a factory to get the appropriate FooHandler for the type of Foo that I have. In this case there would still be a set of ifs but they would be in the factory not the implementation of the handler.
Yes, don't violate LSP which is what you appear to be doing here. Have you considered the Strategy pattern?
This looks like a plain simple case for basic polymorphism.Give Foo a method named something like DontWorryI'llHandleThisMyself() (um, except without the apostrophe, and a more sensible name). The FooHandler just calls this method of whatever Foo it's given. Derived classes of Foo override this method as they please. The example in the question seems to have things inside-out.
With the visitor pattern you could do something like this,
abstract class absFoo {}
class Foo extends absFoo
{
protected int z;
}
class ImprovedFoo extends absFoo
{
protected double k;
}
interface FooHandler {
void accept(IFooVisitor visitor, absFoo foo);
}
class DefaultFooHandler implements FooHandler
{
public void accept(IFooVisitor visitor, absFoo foo)
{
visitor.visit(this, foo);
}
public void handleFoo(absFoo foo) {
System.out.println("DefaultFooHandler");
}
}
class ImprovedFooHandler implements FooHandler
{
public void handleFoo(absFoo foo)
{
System.out.println("ImprovedFooHandler");
}
public void accept(IFooVisitor visitor, absFoo foo) {
visitor.visit(this, foo);
}
}
interface IFooVisitor {
public void visit(DefaultFooHandler fooHandler, absFoo foo);
public void visit(ImprovedFooHandler fooHandler, absFoo foo);
}
class FooVisitor implements IFooVisitor{
public void visit(DefaultFooHandler fHandler, absFoo foo) {
fHandler.handleFoo(foo);
}
public void visit(ImprovedFooHandler iFhandler, absFoo foo) {
iFhandler.handleFoo(foo);
}
}
public class Visitor {
public static void main(String args[]) {
absFoo df = new Foo();
absFoo idf = new ImprovedFoo();
FooHandler handler = new ImprovedFooHandler();
IFooVisitor visitor = new FooVisitor();
handler.accept(visitor, idf);
}
}
But this does not guarantee only Foo can be passed to DefaultFooHandler. It allows ImprovedFoo also can be passed to DefaultFooHandler. To overcome, something similar can be done
class Foo
{
protected int z;
}
class ImprovedFoo
{
protected double k;
}
interface FooHandler {
void accept(IFooVisitor visitor);
}
class DefaultFooHandler implements FooHandler
{
private Foo iFoo;
public DefaultFooHandler(Foo foo) {
this.iFoo = foo;
}
public void accept(IFooVisitor visitor)
{
visitor.visit(this);
}
public void handleFoo() {
System.out.println("DefaultFooHandler");
}
}
class ImprovedFooHandler implements FooHandler
{
private ImprovedFoo iFoo;
public ImprovedFooHandler(ImprovedFoo iFoo) {
this.iFoo = iFoo;
}
public void handleFoo()
{
System.out.println("ImprovedFooHandler");
}
public void accept(IFooVisitor visitor) {
visitor.visit(this);
}
}
interface IFooVisitor {
public void visit(DefaultFooHandler fooHandler);
public void visit(ImprovedFooHandler fooHandler);
}
class FooVisitor implements IFooVisitor{
public void visit(DefaultFooHandler fHandler) {
fHandler.handleFoo();
}
public void visit(ImprovedFooHandler iFhandler) {
iFhandler.handleFoo();
}
}
public class Visitor {
public static void main(String args[]) {
FooHandler handler = new DefaultFooHandler(new Foo());
FooHandler handler2 = new ImprovedFooHandler(new ImprovedFoo());
IFooVisitor visitor = new FooVisitor();
handler.accept(visitor);
handler2.accept(visitor);
}
}
If I have a program that does the following:
if(input=='abc'){do x}
if(input=='def'){do y}
In the future, I may want to add another piece of code like so:
if(input=='ghy'){do x}
As you can see, I am adding a new 'if' statement for a different conditional BUT using the SAME function X.
The code in future has potential to have lots of different IF statements (or switches) all of which are comparing a string vs a string and then performing a function. Considering the future expansion, I was wondering if there is a possible 'neater', 'modular' way of achieving the same results.
It's a shame I can't combine the String with a Method call in a hashtable (String, method) in Java. That way I could just store any new procedures inside a hashtable and grab the relevant method for that String.
Any ideas?
Thank you
EDIT: Thank you for everyone's solutions. I was surprised by the quantity and quality of replies I received in such a small amount of time.
Maybe you can use enum. Example:
public enum InputType
{
abc, def
{
#Override
public void x()
{
System.out.println("Another method");
}
},
ghy;
public void x()
{
System.out.println("One method");
}
}
And further:
InputType.valueOf("abc").x();
Cheers!
I guess you could always use a Map<String, Runnable> and map to anonymous Runnable implementations:
myMap.put("abc", new Runnable() { public void run() { do x } });
...
myMap.get(input).run();
You should take a look at the command pattern. There are several ways of implementing it, and frameworks such as Spring can help you do with in a clean way.
But in a simple manner here's what you could do:
1-Create a Command interface with a method that your program will have to call to do the task, say doTask()
2-Create classes for command X and Y, implementing the Command interface.
3-Create a Map<String, Command> that will map your commands (X and Y) to logical names
4-Create a configuration file of your choice, say a .properties file that will map your input to your command names: abc=X, def=Y, ghi=X
5-Your program then does lookups on the config file to know which command to run according to the input.
A lot of ifs always tell us that we could do this better. In your case better option is to use design pattern e.g. Chain of responsibility. You will have good implementation which you can dynamic change and your code will be easier to maintenance than ifs implementation.
Take a look at this adaptation chain of responsibility to your case:
Main:
public static void main(String[] args) {
ClassA classA = new ClassA(Arrays.asList("abc", "ghi"));
ClassB classB = new ClassB(Arrays.asList("def"));
classA.setNextInChain(classB); // you can always write Builder to do this
String input = "def";
classA.execute(input);
}
BaseClass:
public abstract class BaseClass {
private Collection<String> patterns = Collections.EMPTY_LIST;
protected BaseClass nextInChain;
protected abstract void doMethod(); // your doA, doB methods
public void execute(String input) {
// this replace many ifs in your previous implementation
if (patterns.contains(input)) {
doMethod();
} else {
nextInChain.execute(input);
}
}
public void setPatterns(Collection<String> patterns) {
this.patterns = patterns;
}
public void setNextInChain(BaseClass nextInChain) {
this.nextInChain = nextInChain;
}
}
Class in chain:
public class ClassA extends BaseClass {
ClassA(Collection<String> patterns) {
setPatterns(patterns);
}
#Override
protected void doMethod() {
// do A
}
}
public class ClassB extends BaseClass {...}