Вопрос дизайна-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);

Если вы не хотите использовать некоторый интерфейс маркера для тегов классов, которые подходят для проверки


вы имели в виду:

public interface Validation<T> {
    boolean check(T object)
}

прежде всего, я бы использовал следующий интерфейс

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