在数据处理过程中避免类型转换

Avoid Type Casting During Data Processing

我似乎想不出解决以下问题的最佳方法。假设有一个抽象基础 class 有几个具体的子 classes:

public abstract class AbstractType { /* common properties */ }

public class TypeA { /* properties of type A */ }
public class TypeB { /* properties of type A */ }`

这些是域 classes(JPA 实体)。类型的属性(除其他事项外)用于验证用户数据。我假设将逻辑添加到域模型本身被认为是不好的做法。因此,我想避免向具体的 subclasses 添加 validate 方法。像这样:

UserInput userInput = ...;
AbstractType data = ...;
data.validate(userInput);

我没有看到必须转换域模型的选项, 如果我想将逻辑移动到逻辑层。由于我的知识有限,我只能想出以下两个类似的 "solutions",使用某种处理程序接口。

  1. 在类型中保留一些对处理程序的显式引用

    public interface TypeHandler {
        public validate(AbstractType data, UserInput userInput);
    }
    
    /* TypeAHandler & TypeBHandler implementations */
    
    public enum Type {
        TYPE_A(new TypeAHandler()),
        TYPE_B(new TypeBHandler());
    
        private TypeHandler handler;
    
        public Handler(TypeHandler handler){
            this.handler = handler;
        }
    
        public TypeHandler getHandler(){ return handler; }
    }
    
    public class TypeA {
        private Type type = TYPE_A;
        /* ... */
    }
    

    处理程序将按以下方式调用:

    UserInput userInput = ...;
    AbstractType data = ...;
    data.getType.getHandler().validate(data, userInput);
    

    对处理程序的引用也可以立即添加(中间没有 enum)作为 属性 到 AbstractType class,但这意味着是对域模型中逻辑层内部的 class 的引用(哪种违背了将逻辑移动到逻辑层的目的?)

    这里的问题也是 TypeXHandler 中的验证方法需要先将 data 参数转换为它的子class,然后才能开始验证。

  2. 或者我可以实现一些具有大型 if-then 结构的方法来获取正确的子class,转换它并调用实现类似于以下接口的适当处理程序.

    public interface TypeHandler<T extends AbstractType> {
        public validate(T data, UserInput userInput);
    }
    

所以在这两种情况下都有铸造。第一种情况没有巨大的 if-then 结构,但逻辑和领域没有分离。在第二种情况下,有一个非常不灵活的 if-then 结构。

最后,这是我的问题。我真的应该避免直接在域内实现逻辑吗?如果是这样,有没有办法避免强制转换,if-else 结构 and/or 向域模型添加额外的属性(如第一个 "solution" 中的枚举)。

我上周研究了设计模式,我想提出我的解决方案(它有效,但我不确定这是解决您问题的最明智的方法)。

我的解决方案的想法是使用工厂:您将模型(在您的情况下是 JPA 实体)提供给工厂,它会为您提供该模型的正确验证器。

在程序开始时,你必须通过注册方法告诉工厂你程序的每个模型class哪个是验证器class。

让我们从实施开始...

AbstractModel.java

public abstract class AbstractModel
{
    private final int commonProperty;

    protected AbstractModel(int commonProperty)
    {
        this.commonProperty = commonProperty;
    }

    public int getCommonProperty() { return commonProperty; };
}

AbstractModel中我们放置了模型的所有通用属性。

ModelA.java

public class ModelA extends AbstractModel
{
    private final int specificProperty1;
    private final int specificProperty2;

    public ModelA(int commonProperty, int specificProperty1, int specificProperty2)
    {
        super(commonProperty);

        this.specificProperty1 = specificProperty1;
        this.specificProperty2 = specificProperty2;
    }

    public int getSpecificProperty1() { return specificProperty1; }

    public int getSpecificProperty2() { return specificProperty2; }
}

ModelA 有两个特定的属性。

ModelB.java

public class ModelB extends AbstractModel
{
    private final int specificProperty1;
    private final int specificProperty2;

    public ModelB(int commonProperty, int specificProperty1, int specificProperty2)
    {
        super(commonProperty);

        this.specificProperty1 = specificProperty1;
        this.specificProperty2 = specificProperty2;
    }

    public int getSpecificProperty1() { return specificProperty1; }

    public int getSpecificProperty2() { return specificProperty2; }
}

ModelB 也有两个特定的属性。


假设 ModelA 的实例 a 有效当且仅当

a.commonProperties == a.specificProperty1 + a.specificProperty2

并且 ModelB 的实例 b 有效当且仅当

b.commonProperties == b.specificProperty1 * b.specificProperty2

Validator.java

public interface Validator
{
    public boolean validate();
}

验证器的一个非常简单的界面。

摘要Validator.java

public abstract class AbstractValidator implements Validator
{
    private final AbstractModel toBeValidated;

    protected AbstractValidator(AbstractModel toBeValidated)
    {
        this.toBeValidated = toBeValidated;
    }

    protected AbstractModel getModel()
    {
        return toBeValidated;
    }
}

这是包装待验证模型的具体验证器的超级class。

ValidatorA.java

public class ValidatorA extends AbstractValidator
{
    protected ValidatorA(AbstractModel toBeValidated)
    {
        super(toBeValidated);
    }

    public boolean validate()
    {
        ModelA modelA = (ModelA) getModel();

        return modelA.getCommonProperty() == modelA.getSpecificProperty1() + modelA.getSpecificProperty2();
    }
}

ModelA 实例的验证器。

验证器B

public class ValidatorB extends AbstractValidator
{
    protected ValidatorB(AbstractModel toBeValidated)
    {
        super(toBeValidated);
    }

    public boolean validate()
    {
        ModelB modelB = (ModelB) getModel();

        return modelB.getCommonProperty() == modelB.getSpecificProperty1() * modelB.getSpecificProperty2();
    }
}

这是ModelB实例的验证器。


终于到了工厂!


ValidatorFactory.java

public class ValidatorsFactory
{
    private static ValidatorsFactory instance;
    private final HashMap<Class<? extends AbstractModel>, Class<? extends Validator>> registeredValidators;

    private ValidatorsFactory()
    {
        registeredValidators =
                new HashMap<Class<? extends AbstractModel>, Class<? extends Validator>>();
    }

    public static ValidatorsFactory getInstance()
    {
        if (instance == null)
            instance = new ValidatorsFactory();

        return instance;
    }

    public void registerValidator(
            Class<? extends AbstractModel> model,
            Class<? extends Validator> modelValidator)
    {
        registeredValidators.put(model, modelValidator);
    }

    public Validator createValidator(AbstractModel model)
    {
        Class<? extends Validator> validatorClass = registeredValidators.get(model.getClass());
        Constructor<? extends Validator> validatorConstructor = null;
        Validator validator = null;

        try
        {
            validatorConstructor = validatorClass.getDeclaredConstructor(new Class<?>[] { AbstractModel.class });
            validator = (Validator) validatorConstructor.newInstance(new Object[] { model });
        }
        catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
        {
            System.err.println(e.getMessage());
            // handle exception
        }

        return validator; 
    }
}

工厂是一个单例,有两个重要的方法:

  1. registerValidatorHashMap.
  2. 添加一对新的 (modelClass, validatorClass)
  3. createValidator 获取指定模型的正确验证器。

这是如何使用这个模式:

public class Main
{
    public static void main(String args[])
    {
        ValidatorsFactory factory = ValidatorsFactory.getInstance();

        factory.registerValidator(ModelA.class, ValidatorA.class);
        factory.registerValidator(ModelB.class, ValidatorB.class);

        ModelA modelA = new ModelA(10, 4, 6);
        if (factory.createValidator(modelA).validate())
            System.out.println("modelA is valid");
        else
            System.out.println("modelA is not valid");

        ModelB modelB = new ModelB(10, 8, 2);
        if (factory.createValidator(modelB).validate())
            System.out.println("modelB is valid");
        else
            System.out.println("modelB is not valid");
    }
}

输出:

modelA is valid [because 10 = 4 + 6]
modelB is not valid [because 10 != 8 * 2]

请注意,该模型与控制器完全分离,它仅使用从 AbstractModel 到具体模型的一次转换。

希望对您有所帮助!

归根结底,您是根据子类型(具体 类)进行分支,因为验证用户输入的逻辑是基于子 类 中包含的那些具体细节.

泛型在这里并不能真正帮助你,因为泛型主要基于在不同类型之间应用统一的逻辑,在 通用逻辑 上运行 所有适用类型共享的通用接口。此处您的逻辑和界面因每个子类型而异。

因此,您的主要选择是不可扩展的解决方案,您可以在其中修改中央源代码(如一大堆 ifs/elses、地图等)并根据子类型手动分支,或使用 abstraction/dynamic 多态性作为一种可扩展的解决方案,不需要修改任何中央源代码并根据子类型自动分支。

如果您负担得起,反射也可能是一种可能的途径(它在运行时有点贵)并且只要它适合为您提供可以集中实施的通用逻辑。

如果您不想将此 validate 方法添加到 AbstractType 及其所有子类型,那么您始终可以在顶部添加另一个包含 [=11] 的抽象级别=] 方法,如 ValidatorB,它实现 IValidator 接口并将 TypeB 的对象存储为成员,并应用用于使用 TypeB's 属性验证用户输入的逻辑。