Reputation: 1
What I want to achieve:
I'm currently diving deeper into Java by trying to create a program that reads .csv exports from bank accounts and uses that to do cool stuff.
Since different banks have different file exports, I created an abstract class Bank with only universally present data fields, like money transferred in any transaction. Then, I created a subclass for each individual bank, each extending the abstract Bank class. In each subclass I created a file loader method for managing their specific .csv standard.
My Problem: I want the program to dynamically decide which Bank subclass to use at runtime when reading any given file. That subclass then uses its methods to read the file, and what data to transfer to its Superclass. However, I don't want to add a new if(inputString == bankSubclassName) { bankSubclass.loadFile() }
each time a new subclass gets added.
Is it possible to create a system that reads an argument at runtime, eg. a string, and then to uses a method from a subclass "linked" to that argument? Without having to edit the main program each time a new subclass gets added?
Currently, I seem to have a mental block and I'm totally stuck. Maybe there is a better way?
Thanks in advance!
Upvotes: 0
Views: 1510
Reputation: 1430
I think Factory pattern is a suit method to solve your problem.
Define base bank and it has one abstract method need sub class to override
abstract class AbstractBank {
/**
* method the sub class must to override
*/
abstract void process();
}
Define you needed all sub class,and a default sub class who do nothing
public class DefaultBank extends AbstractBank {
@Override
void process() {
// do nothing
}
}
public class AbbeyNationalBank extends AbstractBank {
@Override
void process() {
}
}
public class BarclaysBank extends AbstractBank {
@Override
void process() {
}
}
public class DaiwaBank extends AbstractBank {
@Override
void process() {
}
}
Define a bank factory who can create bank by bank name
public class BankFactory {
public static AbstractBank getBack(String name) {
if (name.equals("AbbeyNational")){
return new AbbeyNationalBank();
}
if (name.equals("Barclays")) {
return new BarclaysBank();
}
return new DefaultBank();
}
}
The may be code you can use to work
public void process() {
String bankName = "";
AbstractBank bank = BankFactory.getBack(bankName);
bank.process();
}
Upvotes: 0
Reputation: 3311
You don't need multiple Bank
subclasses, you just need an ImportStrategy
that the Bank
uses. This way you don't have to use reflection or clutter you class hierarchy with several classes when the actual difference is just the way the data is read.
import java.util.Arrays;
import java.util.Optional;
public final class Bank {
private String bankData;
interface ImportStrategy {
String importData();
}
enum CsvImportStrategy implements ImportStrategy {
FILE_TYPE1("inputString1") {
@Override
public String importData() {
return "csv data";
}
},
FILE_TYPE2("inputString2") {
@Override
public String importData() {
return "csv data";
}
};
private final String inputString;
CsvImportStrategy(String inputString) {
this.inputString = inputString;
}
public static Optional<CsvImportStrategy> selectByInputString(String inputString) {
return Arrays.stream(CsvImportStrategy.values())
.filter(strategy -> strategy.inputString.equals(inputString))
.findFirst();
}
}
public void readData(String inputString) {
CsvImportStrategy.selectByInputString(inputString)
.ifPresent(strategy -> bankData = strategy.importData());
}
}
Upvotes: 0
Reputation: 10326
If you don't mind passing the name of the class to load, you can use the Class
methods to dynamically load a particular subclass and call newInstance()
to create a object of that subclass.
Class c = Class.forName("some.pkg.name." + inputString);
Bank obj = (Bank)c.newInstance();
In this example, inputString
must be the name of your subclass and obj
will be an instance of it.
These methods are all documented: https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html
Upvotes: 1