JonaH
JonaH

Reputation: 1

Java - Dynamically choosing subclass/object to create

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

Answers (3)

TongChen
TongChen

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

Alex R
Alex R

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

adelphus
adelphus

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

Related Questions