Вопрос дизайна-java-каков наилучший способ сделать это?
У меня проблема с дизайном.
У меня есть два объекта данных, которые являются экземплярами класса A и класса B.
A и B не имеют никакого поведения - это Java-бобы с геттерами и сеттерами.
У меня есть интерфейс проверки и 10 его реализаций, определяющих различные проверки.
Я хотел бы указать в моем файле свойств, какая проверка применяется к какому классу.
Что-то вроде этого:
класс а XYZValidation, ABCValidation
класс B: ABCValidation, PPPValidation, etc
Как написать класс проверки, чтобы он обслуживал объекты, являющиеся экземплярами класса A или ClassB, или почти любого другого класса C, который я мог бы добавить в будущем?
interface Validation {
public boolean check(??);
}
> просто хотел добавить эту строку, чтобы сказать спасибо всем тем, кто откликнулся на этот пост и сказать, что я люблю свое время здесь, на этом удивительном сайте. Сайте StackOverflow скалы!
7 ответов
вы думали об использовании аннотаций, чтобы отметить поля, которые вы хотите проверить в своем Бобе?
Если у вас есть 10 различных проверок, вы можете указать 10 аннотаций. Затем отметьте поля, используя аннотации:
@ValideStringIsCapitalCase
private String myString;
@ValidateIsNegative
private int myInt;
С reflection API повторите все поля и посмотрите, отмечены ли они, что-то вроде этого:
public static <T> validateBean(T myBean) throws IllegalAccessException {
Field[] fields = myBean.getClass().getDeclaredFields();
// This does not take fields of superclass into account
if (fields != null) {
for (Field field : allFields) {
if (field.isAnnotationPresent(ValideStringIsCapitalCase.class)) {
field.setAccessible(true);
Object value = field.get(existingEntity);
// Validate
field.setAccessible(false);
}
}
}
}
можно было бы пометить весь класс валидатором, который вы хотите использовать.
EDIT: запомнить чтобы включить аннотацию:
@Retention(RetentionPolicy.RUNTIME)
для вашего интерфейса аннотации.
EDIT2: пожалуйста, не изменяйте поля напрямую (как в примере выше). Вместо этого доступ к их геттеров и сеттеров, используя отражение.
Я, вероятно, неправильно понял вопрос, но было бы что-то вроде этого достаточно:
public class ValidationMappings {
private Map<Class, Class<Validation>[]> mappings = new HashMap<Class, Class<Validation>[]>();
public ValidationMappings() {
mappings.put(A.class, new Class[]{XYZValidation.class, ABCValidation.class});
mappings.put(B.class, new Class[]{ABCValidation.class, PPPValidation.class});
}
public Class[] getValidators(Class cls) {
if (!mappings.containsKey(cls)) return new Class[]{};
return mappings.get(cls);
}
}
когда вы хотите получить список валидаторов для определенного класса, вы должны вызвать getValidators (Class cls) и перебрать каждый валидатор и создать экземпляр каждого и вызвать свой метод проверки.
что-то вроде этого возможно?
interface Validation {
public boolean check(Validatable x);
}
interface Validatable {
}
class A implements Validatable {
...
}
class Validator {
public boolean validateObject(Validatable x){
boolean validated = true;
... //read config file, check which validation classes to call
//for each validation class v in the config file:
if(!v.check(x)) validated = false;
return validated;
}
}
Если вы просто хотите иметь дело с любым объектом, то это будет объект, что ваш интерфейс
public boolean check (Object o);
Если вы не хотите использовать некоторый интерфейс маркера для тегов классов, которые подходят для проверки
прежде всего, я бы использовал следующий интерфейс
interface Validator {
boolean isValid(Object object);
}
неявно документировать, что на самом деле означает возвращаемое значение.
во-вторых, я бы предложил документировать в интерфейсе, какое поведение ожидается, если валидатор не знает, как обрабатывать данный экземпляр.
interface Validator {
/**
* @return false if this validator detects that the given instance is invalid, true if the given object is valid or this Validator can't validate it.
*/
boolean isValid(Object object);
}
таким образом, у вас просто будет список валидаторов, в которые вы можете бросить свои объекты.
влияние производительности несовместимых валидаторов должно быть незначительно, если они реализованы должным образом, например, с ранним instanceof.
на боковой заметке я бы использовал список валидаторов вместо набора, чтобы вы могли заказать их в соответствии со сложностью. Поместите дешевые (с точки зрения производительности) валидаторы в начале списка в качестве оптимизации.
затем вы можете использовать общий фрагмент кода для проверки, например
public class Validators {
public static boolean isValid(Object o, Collection<Validator> validators) {
for(Validator current : validators) {
if(!current.isValid()) return false;
}
return true;
}
}
В зависимости от вашего варианта использования может быть хорошей идеей вернуть что-то отличное от boolean в ваш интерфейс. Если вам нужна информация о что неправильно, например, чтобы отобразить его, вам нужно будет вернуть эту информацию.
в этом случае было бы неплохо сохранить вышеуказанный цикл, чтобы вы получили все ошибки проверки вместо только первого.
шаблон посетителя решил бы это
вызов валидатора посетителей возможно иметь следующее:
public interface Validatable { public boolean validate(Validator v); } public interface Validator { public boolean validate(A a); public boolean validate(B b); } public class A implements Validatable { public boolean validate(Validator v){ return v.validate(this); } } public class B implements Validatable { public void validate(Validator v) { return v.validate(this); } } // Default validator just doesn't know how to // validate neither A's, nor B's public class GenericValidator implements Validator { public boolean validate(A a) { throw new UnsupportedOperationException("Cannot validate A"); } public boolean validate(B b) { throw new UnsupportedOperationException("Cannot validate B"); } } // since XYZValidation is supposed to run only on A's // it only overrides A validation public class XYZValidation extends GenericValidator { public boolean validate(A a) { // validate a return isVAlid(a); } } // since ABCValidation is supposed to run on A's and B's // it overrides A and B validation public class ABCValidation extends GenericValidator { public boolean validate(A a) { // validate a return isVAlid(a); } public boolean validate(B b) { // validate b return isVAlid(b); } } // since ABCValidation is supposed to run only on B's // it overrides A only B validation public class PPPValidation extends GenericValidator { public boolean validate(B b) { // validate b return isVAlid(b); } }