Вопрос дизайна - Java - каков наилучший способ сделать это? - PullRequest
7 голосов
/ 05 июня 2009

У меня проблема с дизайном.

У меня есть два объекта данных, которые являются экземплярами, скажем, класса A и класса B. A и B не имеют никакого поведения - они являются Java-бинами с геттерами и сеттерами. У меня есть интерфейс проверки и 10 его реализаций, определяющих различные проверки. Я хотел бы указать в моем файле свойств, какая проверка применяется к какому классу. Что-то вроде этого:

Класс A XYZValidation, ABCValidation

класс B: проверка ABC, проверка PPP и т. Д.

Как мне написать свой класс Validation, чтобы он обслуживал объекты, которые являются экземплярами класса A или ClassB, или почти любой другой класс C, который я мог бы добавить в будущем?

interface Validation {
public boolean check(??);
}

> Просто хотел добавить эту строку, чтобы поблагодарить всех тех, кто откликнулся на этот пост, и сказать, что я люблю свое время здесь, на этом удивительном сайте. Stackoverflow порода!

Ответы [ 7 ]

8 голосов
/ 05 июня 2009

Задумывались ли вы об использовании аннотаций для пометки полей, которые вы хотите проверить в вашем бине?

Если у вас есть 10 разных проверок, вы можете указать 10 аннотаций. Затем отметьте поля, используя аннотации:

@ValideStringIsCapitalCase
private String myString;

@ValidateIsNegative
private int myInt;

С помощью 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);
            }
        }
    }
}

Один из вариантов - пометить весь класс тем валидатором, который вы хотите использовать.

РЕДАКТИРОВАТЬ: не забудьте включить аннотацию:

@Retention(RetentionPolicy.RUNTIME)

для вашего интерфейса аннотации.

EDIT2: пожалуйста, не изменяйте поля напрямую (как в примере выше). Вместо этого получите доступ к их получателям и установщикам, используя отражение.

3 голосов
/ 05 июня 2009

как то так может быть?

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;
   }
}
3 голосов
/ 05 июня 2009

Я, наверное, неправильно понял вопрос, но хватит ли чего-нибудь подобного:

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 (класс cls) и выполнять итерацию по каждому валидатору, создавать экземпляр каждого и вызывать ваш метод проверки.

2 голосов
/ 05 июня 2009

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

public interface Validation<T> {
    boolean check(T object)
}
2 голосов
/ 05 июня 2009

Если вы просто хотите, чтобы он имел дело с каким-либо объектом, тогда это будет Object, ваш интерфейс

публичная логическая проверка (Объект o);

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

0 голосов
/ 05 июня 2009

Шаблон посетителя может решить эту проблему

Позвонив в Валидатор Посетителей, можно получить:


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);
  }
}
 
0 голосов
/ 05 июня 2009

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

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

Таким образом, у вас будет просто список валидаторов, в который вы можете бросить свои объекты.

Влияние производительности несовместимых валидаторов должно быть незначительным, если они реализованы должным образом, например, с ранним экземпляром.

Кстати, я бы использовал Список валидаторов вместо набора, чтобы вы могли упорядочить их в соответствии со сложностью. Поместите дешевые (с точки зрения производительности) валидаторы в начало списка в качестве оптимизации.

Затем вы можете использовать общий кусок кода для проверки, например,

public class Validators {
    public static boolean isValid(Object o, Collection<Validator> validators) {
        for(Validator current : validators) {
            if(!current.isValid()) return false;
        }
        return true;
    }
}

В зависимости от вашего варианта использования может быть хорошей идеей вернуть что-то отличное от логического в вашем интерфейсе. Если вам нужна информация о , что не так, например, чтобы отобразить его, вам нужно будет вернуть эту информацию.

В этом случае может быть хорошей идеей сохранить вышеуказанный цикл работающим, чтобы вы получали все ошибки проверки, а не только первые.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...