简体   繁体   English

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

[英]Design question - java - what is the best way to doing this?

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. 我有两个数据对象,它们是类A和类B的实例.A和B没有任何行为 - 它们是带有getter和setter的java bean。 I have a Validation interface and 10 implementations of it defining different Validations. 我有一个验证界面和10个实现它定义不同的验证。 I would like to specify in my properties file which Validation applies to which class. 我想在我的属性文件中指定哪个Validation适用于哪个类。 Something like this: 像这样的东西:

class A XYZValidation,ABCValidation A类XYZValidation,ABCValidation

class B: ABCValidation, PPPValidation, etc B类:ABCValidation,PPPValidation等

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? 如何编写我的Validation类,以便它提供作为Class A OR 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. >只是想添加这一行,感谢所有回复此帖的人,并说我喜欢这个神奇的网站上的时间。 Stackoverflow rocks! Stackoverflow岩石!

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. 如果您有10种不同的验证,则可以指定10个注释。 Then mark the fields using annotations: 然后使用注释标记字段:

@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). EDIT2:请不要直接修改字段(如上例所示)。 Instead access their getters and setters using reflection. 而是使用反射访问他们的getter和setter。

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方法。

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

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 除非您想使用某些标记接口来标记适合验证的类

Did you mean: 你的意思是:

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

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, eg 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. 另外,我会使用Validator列表而不是Set,这样您就可以根据复杂性对它们进行排序。 Put the cheap (performance-wise) Validators at the start of the List as an optimization. 将廉价(性能方面)验证器放在List的开头作为优化。

You could then use a general piece of code for validation, eg 然后,您可以使用一般代码进行验证,例如

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM