0

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!

2
  • Banks wouldn’t be subclasses IRL; a bank is a bank. Each bank might have one or more csv readers. Then you’d either have a simple csv parser factory or a map of classes or whatever from which you’d create an instance as necessary. There are any number of ways to create a plugin system, but basically you’d need to scan the class path to find specific kinds of classes that could then register themselves as csv readers. Commented May 20, 2020 at 23:01
  • Yes, create a Map<String, BankFactory>, where interface BankFactory { Bank create(); }. You would populate the map as map.put("subclass", new SubclassFactory()), where class SubclassFactory implements BankFactory { public Bank create() { return new Subclass(); } }. You should always sanatize your input before processing. Allowing clients to dynamically load any class they want could lead to exploits similar to SQL injection. Commented May 20, 2020 at 23:33

3 Answers 3

1

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

Sign up to request clarification or add additional context in comments.

1 Comment

Depending on how the input string is received, this could lead to injection exploits. I wouldn't recommend giving clients this type of access. Providing factories based on supported subtypes, rejecting any value which isn't mapped to a supported subtypes, would be safer.
0

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());
    }
}

Comments

0

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();
    }

2 Comments

He wrote that he does not want having to add if name.equals("...") for every bank implementation.
I think if name.equals("...") is necessary,we need to create suit object to work,Let's think together and see if there is a more appropriate method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.