在数据处理过程中避免类型转换
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",使用某种处理程序接口。
在类型中保留一些对处理程序的显式引用
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,然后才能开始验证。
或者我可以实现一些具有大型 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;
}
}
工厂是一个单例,有两个重要的方法:
registerValidator
在 HashMap
. 添加一对新的 (modelClass, validatorClass)
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
属性验证用户输入的逻辑。
我似乎想不出解决以下问题的最佳方法。假设有一个抽象基础 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",使用某种处理程序接口。
在类型中保留一些对处理程序的显式引用
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,然后才能开始验证。或者我可以实现一些具有大型 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;
}
}
工厂是一个单例,有两个重要的方法:
registerValidator
在HashMap
. 添加一对新的 (modelClass, validatorClass)
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
属性验证用户输入的逻辑。