【问题标题】:Java - Dynamically choosing subclass/object to createJava - 动态选择要创建的子类/对象
【发布时间】:2020-05-20 22:55:33
【问题描述】:

我想要达到的目标:

我目前正在通过尝试创建一个程序来更深入地研究 Java,该程序可以读取银行账户中的 .csv 导出文件并使用它来做一些很酷的事情。

由于不同的银行有不同的文件导出,我创建了一个抽象类 Bank,它只有普遍存在的数据字段,例如在任何交易中转移的资金。然后,我为每个单独的银行创建了一个子类,每个子类都扩展了抽象 Bank 类。在每个子类中,我创建了一个文件加载器方法来管理其特定的 .csv 标准。

我的问题:我希望程序在读取任何给定文件时动态决定在运行时使用哪个 Bank 子类。然后该子类使用它的方法来读取文件,以及将哪些数据传输到它的超类。但是,我不想每次添加新的子类时都添加新的if(inputString == bankSubclassName) { bankSubclass.loadFile() }

是否可以创建一个在运行时读取参数的系统,例如。一个字符串,然后使用子类“链接”到该参数的方法?每次添加新的子类时都不必编辑主程序?

目前,我似乎有一个心理障碍,我完全被困住了。也许有更好的方法?

提前致谢!

【问题讨论】:

  • 银行不会是 IRL 的子类;银行就是银行。每家银行可能有一个或多个 csv 阅读器。然后你要么有一个简单的 csv 解析器工厂,要么有一个类的映射,或者你可以根据需要创建一个实例的任何东西。创建插件系统的方法有很多种,但基本上您需要扫描类路径以找到特定类型的类,然后将它们自己注册为 csv 阅读器。
  • 是的,创建一个Map<String, BankFactory>,其中interface BankFactory { Bank create(); }。您可以将地图填充为map.put("subclass", new SubclassFactory()),其中class SubclassFactory implements BankFactory { public Bank create() { return new Subclass(); } }。在处理之前,您应该始终清理您的输入。允许客户端动态加载他们想要的任何类可能会导致类似于 SQL 注入的漏洞。

标签: java inheritance


【解决方案1】:

如果您不介意传递要加载的类的名称,可以使用Class 方法动态加载特定子类并调用newInstance() 来创建该子类的对象。

Class c = Class.forName("some.pkg.name." + inputString);
Bank obj = (Bank)c.newInstance();

在本例中,inputString 必须是您的子类的名称,obj 将是它的一个实例。

这些方法都有记录:https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html

【讨论】:

  • 根据输入字符串的接收方式,这可能会导致注入漏洞。我不建议给客户这种类型的访问权限。提供基于支持的子类型的工厂,拒绝任何未映射到支持的子类型的值,会更安全。
【解决方案2】:

您不需要多个Bank 子类,您只需要Bank 使用的ImportStrategy。这样一来,当实际的区别只是读取数据的方式时,您就不必使用反射或将类层次结构与多个类混淆。

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

【讨论】:

    【解决方案3】:

    我认为工厂模式是解决您问题的合适方法。

    定义base bank,它有一个抽象方法需要子类覆盖

    abstract class AbstractBank {
    
        /**
         * method the sub class must to override
         */
        abstract void process();
    }
    

    定义你需要的所有子类,以及一个什么都不做的默认子类

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

    定义一个可以通过银行名称创建银行的银行工厂

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

    可能是您可以用来工作的代码

        public void process() {
            String bankName = "";
            AbstractBank bank = BankFactory.getBack(bankName);
            bank.process();
        }
    

    【讨论】:

    • 他写道,他不想为每个银行实施添加if name.equals("...")
    • 我觉得if name.equals("...")很有必要,我们需要创建适合的对象才能工作,大家一起思考一下,看看有没有更合适的方法。
    猜你喜欢
    • 1970-01-01
    • 2012-11-07
    • 1970-01-01
    • 2014-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多