设计问题 - java - 这样做的最佳方法是什么?

时间:2022-09-06 00:25:20

I have a design problem.

我有设计问题。

I have two data objects which are instances of say class A and class B. A and B don't have any behavior - they are java beans with getters and setters. I have a Validation interface and 10 implementations of it defining different Validations. I would like to specify in my properties file which Validation applies to which class. Something like this:

class A XYZValidation,ABCValidation

我有两个数据对象,它们是类A和类B的实例.A和B没有任何行为 - 它们是带有getter和setter的java bean。我有一个验证界面和10个实现它定义不同的验证。我想在我的属性文件中指定哪个Validation适用于哪个类。像这样:类A XYZValidation,ABCValidation

class B: ABCValidation, PPPValidation, etc

How do I write my Validation class so that it serves objects that are instances of Class A OR ClassB, or just about any other Class C that I might want to add in future?

B类:ABCValidation,PPPValidation等我如何编写我的Validation类,以便它提供作为A类或ClassB实例的对象,或者我将来可能要添加的任何其他C类?

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

> Just wanted to add this line to say thank you to all those who have responded to this post and to say that I am loving my time here on this amazing website. * rocks!

>只是想添加这一行,感谢所有回复此帖的人,并说我喜欢这个神奇网站上的时间。 *岩石!

7 个解决方案

#1


Have you thought about using annotations to mark the fields you want to validate in your bean?

您是否考虑过使用注释来标记要在bean中验证的字段?

If you have 10 different validations you could specify 10 annotations. Then mark the fields using annotations:

如果您有10种不同的验证,则可以指定10个注释。然后使用注释标记字段:

@ValideStringIsCapitalCase
private String myString;

@ValidateIsNegative
private int myInt;

With reflection API iterate through all the fields and see if they are marked, something like this:

使用反射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);
            }
        }
    }
}

An option would be to mark the whole class with the validator you want to use.

一个选项是使用您要使用的验证器标记整个类。

EDIT: remember to include annotation:

编辑:记得包含注释:

@Retention(RetentionPolicy.RUNTIME)

for your annotation interface.

为您的注释界面。

EDIT2: please don't modify the fields directly (as in the example above). Instead access their getters and setters using reflection.

EDIT2:请不要直接修改字段(如上例所示)。而是使用反射访问他们的getter和setter。

#2


I've probably misunderstood the question but would something like this suffice:

我可能误解了这个问题,但这样就足够了:

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

When you want to get the list of validators for a particular class, you would then call getValidators(Class cls) and iterate over each validator and create an instance of each and call your check method.

当您想要获取特定类的验证器列表时,您将调用getValidators(Class cls)并遍历每个验证器并创建每个验证器的实例并调用您的check方法。

#3


something like this maybe?

这样的事可能吗?

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

#4


If you just want it to deal with any object then it'll be Object's that your interface

如果你只是想让它处理任何对象那么它就是你的界面的对象

public boolean check(Object o);

public boolean check(Object o);

Unless you want to use some marker interface to tag classes that are suitable for validation

除非您想使用某些标记接口来标记适合验证的类

#5


Did you mean:

你的意思是:

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

#6


First of all, I'd use the following interface

首先,我使用以下界面

interface Validator {
    boolean isValid(Object object);
}

to implicitly document what the return value actually means.

隐含地记录返回值实际意味着什么。

Secondly, I'd suggest to document in the interface what behavior is expected if the Validator doesn't know how to handle the given instance.

其次,如果Validator不知道如何处理给定的实例,我建议在接口中记录预期的行为。

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

That way, you'd simply have a List of Validators that you could throw your objects at.

这样,您只需要一个可以抛出对象的验证器列表。

The performance impact of incompatible Validators should be negligible if they are implemented properly, e.g. with an early instanceof.

如果正确实施,不兼容的验证器对性能的影响可以忽略不计,例如,早期的实例。

On a side note, I'd use a List of Validators instead of a Set so you can order them according to complexity. Put the cheap (performance-wise) Validators at the start of the List as an optimization.

在旁注中,我使用了Validator列表而不是Set,因此您可以根据复杂性对它们进行排序。将廉价(性能方面)验证器放在List的开头作为优化。

You could then use a general piece of code for validation, e.g.

然后,您可以使用一般代码进行验证,例如:

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

Depending on your use-case it might be a good idea to return something different than boolean in your interface. If you need information about what is wrong, e.g. to display it, you'd need to return that info instead.

根据您的用例,在界面中返回不同于布尔值的内容可能是个好主意。如果您需要有关错误的信息,例如要显示它,您需要返回该信息。

In that case it might be a good idea to keep the above loop running so you'll get all validation errors instead of only the first.

在这种情况下,保持上述循环运行可能是个好主意,因此您将获得所有验证错误,而不仅仅是第一个。

#7


A Visitor pattern would solve this

访客模式可以解决这个问题

Calling the Visitor Validator it's possible to have this:

调用访客验证器可以这样:


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

#1


Have you thought about using annotations to mark the fields you want to validate in your bean?

您是否考虑过使用注释来标记要在bean中验证的字段?

If you have 10 different validations you could specify 10 annotations. Then mark the fields using annotations:

如果您有10种不同的验证,则可以指定10个注释。然后使用注释标记字段:

@ValideStringIsCapitalCase
private String myString;

@ValidateIsNegative
private int myInt;

With reflection API iterate through all the fields and see if they are marked, something like this:

使用反射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);
            }
        }
    }
}

An option would be to mark the whole class with the validator you want to use.

一个选项是使用您要使用的验证器标记整个类。

EDIT: remember to include annotation:

编辑:记得包含注释:

@Retention(RetentionPolicy.RUNTIME)

for your annotation interface.

为您的注释界面。

EDIT2: please don't modify the fields directly (as in the example above). Instead access their getters and setters using reflection.

EDIT2:请不要直接修改字段(如上例所示)。而是使用反射访问他们的getter和setter。

#2


I've probably misunderstood the question but would something like this suffice:

我可能误解了这个问题,但这样就足够了:

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

When you want to get the list of validators for a particular class, you would then call getValidators(Class cls) and iterate over each validator and create an instance of each and call your check method.

当您想要获取特定类的验证器列表时,您将调用getValidators(Class cls)并遍历每个验证器并创建每个验证器的实例并调用您的check方法。

#3


something like this maybe?

这样的事可能吗?

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

#4


If you just want it to deal with any object then it'll be Object's that your interface

如果你只是想让它处理任何对象那么它就是你的界面的对象

public boolean check(Object o);

public boolean check(Object o);

Unless you want to use some marker interface to tag classes that are suitable for validation

除非您想使用某些标记接口来标记适合验证的类

#5


Did you mean:

你的意思是:

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

#6


First of all, I'd use the following interface

首先,我使用以下界面

interface Validator {
    boolean isValid(Object object);
}

to implicitly document what the return value actually means.

隐含地记录返回值实际意味着什么。

Secondly, I'd suggest to document in the interface what behavior is expected if the Validator doesn't know how to handle the given instance.

其次,如果Validator不知道如何处理给定的实例,我建议在接口中记录预期的行为。

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

That way, you'd simply have a List of Validators that you could throw your objects at.

这样,您只需要一个可以抛出对象的验证器列表。

The performance impact of incompatible Validators should be negligible if they are implemented properly, e.g. with an early instanceof.

如果正确实施,不兼容的验证器对性能的影响可以忽略不计,例如,早期的实例。

On a side note, I'd use a List of Validators instead of a Set so you can order them according to complexity. Put the cheap (performance-wise) Validators at the start of the List as an optimization.

在旁注中,我使用了Validator列表而不是Set,因此您可以根据复杂性对它们进行排序。将廉价(性能方面)验证器放在List的开头作为优化。

You could then use a general piece of code for validation, e.g.

然后,您可以使用一般代码进行验证,例如:

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

Depending on your use-case it might be a good idea to return something different than boolean in your interface. If you need information about what is wrong, e.g. to display it, you'd need to return that info instead.

根据您的用例,在界面中返回不同于布尔值的内容可能是个好主意。如果您需要有关错误的信息,例如要显示它,您需要返回该信息。

In that case it might be a good idea to keep the above loop running so you'll get all validation errors instead of only the first.

在这种情况下,保持上述循环运行可能是个好主意,因此您将获得所有验证错误,而不仅仅是第一个。

#7


A Visitor pattern would solve this

访客模式可以解决这个问题

Calling the Visitor Validator it's possible to have this:

调用访客验证器可以这样:


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