Visitor概述
UML概述
玩具代码案例 - 基于访问者模式的数据校验器
校验接口
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements;
import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
import java.lang.annotation.Annotation;
public interface IValidator {
ValidatorResponse isVerify(Annotation annotation, String propertyName, Object propertyValue);
}
被访问者
package online.javabook.design.gof.behavioral10.visitor.validation.app.visitor;
import online.javabook.design.gof.behavioral10.visitor.validation.other.ValidatorResponse;
import java.lang.annotation.Annotation;
public interface IValidatorVisitable {
ValidatorResponse accept(ValidatorVisitor validatorVisitor, Annotation attribute, String propertyName, Object propertyValue);
}
校验实现
校验器既是一个校验器,也是一个被访问者。
IsFalseValidator
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsFalse;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.IValidatorAccept;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.ValidatorVisitor;
import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
import java.lang.annotation.Annotation;
public class IsFalseValidator implements IValidator, IValidatorVisitable {
@Override
public ValidatorResponse isVerify(Annotation annotation, String propertyName, Object propertyValue) {
boolean isValid = !Boolean.parseBoolean(propertyValue.toString());
IsFalse isFalseAnnotation = (IsFalse) annotation;
return isValid?
new ValidatorResponse(true, propertyName, propertyValue) :
new ValidatorResponse(false, propertyName, propertyValue, isFalseAnnotation.error());
}
@Override
public ValidatorResponse accept(ValidatorVisitor validatorVisitor, Annotation attribute, String propertyName, Object propertyValue) {
return validatorVisitor.visit(this, attribute, propertyName, propertyValue);
}
}
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsFalse {
public String error() default "";
}
IsTrueValidator
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsTrue;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.IValidatorAccept;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.ValidatorVisitor;
import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
import java.lang.annotation.Annotation;
public class IsTrueValidator implements IValidator, IValidatorVisitable {
@Override
public ValidatorResponse isVerify(Annotation annotation, String propertyName, Object propertyValue) {
boolean isValid = Boolean.parseBoolean(propertyValue.toString());
IsTrue isTrueAnnotation = (IsTrue) annotation;
return isValid?
new ValidatorResponse(true, propertyName, propertyValue) :
new ValidatorResponse(false, propertyName, propertyValue, isTrueAnnotation.error());
}
@Override
public ValidatorResponse accept(ValidatorVisitor validatorVisitor, Annotation attribute, String propertyName, Object propertyValue) {
return validatorVisitor.visit(this, attribute, propertyName, propertyValue);
}
}
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsTrue {
public String error() default "";
}
IsNotEmptyValidator
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsNotEmpty;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.IValidatorAccept;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.ValidatorVisitor;
import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
import java.lang.annotation.Annotation;
public class IsNotEmptyValidator implements IValidator, IValidatorVisitable {
@Override
public ValidatorResponse isVerify(Annotation annotation, String propertyName, Object propertyValue) {
boolean isValid = propertyValue != null && !propertyValue.toString().equals("");
IsNotEmpty isNotEmptyAnnotation = (IsNotEmpty) annotation;
return isValid?
new ValidatorResponse(true, propertyName, propertyValue) :
new ValidatorResponse(false, propertyName, propertyValue, isNotEmptyAnnotation.error());
}
@Override
public ValidatorResponse accept(ValidatorVisitor validatorVisitor, Annotation attribute, String propertyName, Object propertyValue) {
return validatorVisitor.visit(this, attribute, propertyName, propertyValue);
}
}
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsNotEmpty {
public String error() default "";
}
IsNotNullValidator
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsNotNull;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.IValidatorAccept;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.ValidatorVisitor;
import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
import java.lang.annotation.Annotation;
public class IsNotNullValidator implements IValidator, IValidatorVisitable {
@Override
public ValidatorResponse isVerify(Annotation annotation, String propertyName, Object propertyValue) {
boolean isValid = propertyValue != null;
IsNotNull isNotNullAnnotation = (IsNotNull) annotation;
return isValid?
new ValidatorResponse(true, propertyName, propertyValue) :
new ValidatorResponse(false, propertyName, propertyValue, isNotNullAnnotation.error());
}
@Override
public ValidatorResponse accept(ValidatorVisitor validatorVisitor, Annotation attribute, String propertyName, Object propertyValue) {
return validatorVisitor.visit(this, attribute, propertyName, propertyValue);
}
}
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsNotNull {
public String error() default "";
}
IsNumberValidator
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsNumber;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.IValidatorAccept;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.ValidatorVisitor;
import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
import java.lang.annotation.Annotation;
public class IsNumberValidator implements IValidator, IValidatorAccept {
@Override
public ValidatorResponse isVerify(Annotation annotation, String propertyName, Object propertyValue) {
IsNumber isNumberAnnotation = (IsNumber) annotation;
int number = Integer.parseInt(propertyValue.toString());
boolean isValid = (isNumberAnnotation.min() <= number) && (number <= isNumberAnnotation.max());
return isValid?
new ValidatorResponse(true, propertyName, propertyValue) :
new ValidatorResponse(false, propertyName, propertyValue, isNumberAnnotation.error());
}
@Override
public ValidatorResponse accept(ValidatorVisitor validatorVisitor, Annotation attribute, String propertyName, Object propertyValue) {
return validatorVisitor.visit(this, attribute, propertyName, propertyValue);
}
}
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsNumber {
public int min() default 0;
public int max() default 0;
public String error() default "";
}
访问者实现
当添加一个校验器或者说是被访问者时,只需要横向扩展一个校验器类(被访问者),并在访问者中添加一个新的访问方法即可。访问器基本是就是一个基于方法重载对简单类。
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements.*;
import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
import java.lang.annotation.Annotation;
public class ValidatorVisitor {
public ValidatorResponse visit(IsNotNullValidator validator, Annotation annotation, String propertyName, Object propertyValue) {
return validator.isVerify(annotation, propertyName, propertyValue);
}
public ValidatorResponse visit(IsNotEmptyValidator validator, Annotation annotation, String propertyName, Object propertyValue) {
return validator.isVerify(annotation, propertyName, propertyValue);
}
public ValidatorResponse visit(IsTrueValidator validator, Annotation annotation, String propertyName, Object propertyValue) {
return validator.isVerify(annotation, propertyName, propertyValue);
}
public ValidatorResponse visit(IsFalseValidator validator, Annotation annotation, String propertyName, Object propertyValue) {
return validator.isVerify(annotation, propertyName, propertyValue);
}
public ValidatorResponse visit(IsNumberValidator validator, Annotation annotation, String propertyName, Object propertyValue) {
return validator.isVerify(annotation, propertyName, propertyValue);
}
}
基于访问者模式的实现
注册被访问者
package online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.*;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements.*;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
public class ValidatorRegister {
private static Map<Class<? extends Annotation>, IValidator> validatorMap = new HashMap<>();
static {
registerValidator(IsFalse.class, new IsFalseValidator());
registerValidator(IsTrue.class, new IsTrueValidator());
registerValidator(IsNotEmpty.class, new IsNotEmptyValidator());
registerValidator(IsNotNull.class, new IsNotNullValidator());
registerValidator(IsNumber.class, new IsNumberValidator());
}
public static void registerValidator(Class<? extends Annotation> annotation, IValidator validator) {
validatorMap.put(annotation, validator);
}
public static IValidator getValidator(Class<? extends Annotation> annotation) {
return validatorMap.get(annotation);
}
}
访问者的调用
package online.javabook.gof.behavioral.patterns10.visitor.validation.app;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements.IValidator;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.IValidatorAccept;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.ValidatorRegister;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.ValidatorVisitor;
import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class ValidatorUtils {
private ValidatorVisitor visitor = new ValidatorVisitor();
public List<ValidatorResponse> validate(Object value) throws IllegalAccessException {
List<ValidatorResponse> validatorResponses = new ArrayList();
Field[] fields = value.getClass().getDeclaredFields();
for (Field field : fields){
field.setAccessible(true);
String propertyName = field.getName();
Object propertyValue = field.get(value);
Annotation[] propertyAnnotations = field.getAnnotations();
for (Annotation propertyAnnotation: propertyAnnotations) {
IValidator validator = ValidatorRegister.getValidator(propertyAnnotation.annotationType());
if(validator!=null) {
// validator is accept
IValidatorVisitable visitable = (IValidatorVisitable)validator;
// accept
ValidatorResponse validatorResponse = visitable.Accept(visitor, propertyAnnotation, propertyName, propertyValue);
// response
if(!validatorResponse.isValid()) {
validatorResponses.add(validatorResponse);
}
}
}
}
return validatorResponses;
}
}
Main
package online.javabook.gof.behavioral.patterns10.visitor.validation.app;
import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
import java.util.List;
public class Main {
public static void main(String[] args) throws IllegalAccessException {
ValidatorUtils dataValidator = new ValidatorUtils();
User user = new User();
user.setName("xxx");
user.setAge(44488);
user.setTrue(false);
user.setFalse(true);
List<ValidatorResponse> responses = dataValidator.validate(user);
for (ValidatorResponse response : responses) {
System.out.println(response);
}
}
}
User
package online.javabook.gof.behavioral.patterns10.visitor.validation.app;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsFalse;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsNotEmpty;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsNumber;
import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsTrue;
public class User {
@IsNotEmpty(error = "name is empty")
private String name;
@IsTrue(error = "isTrue is not true")
private boolean isTrue;
@IsFalse(error = "isFalse is not false")
private boolean isFalse;
@IsNumber(min = 1, max = 200, error = "age is not valid")
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isTrue() {
return isTrue;
}
public void setTrue(boolean aTrue) {
isTrue = aTrue;
}
public boolean isFalse() {
return isFalse;
}
public void setFalse(boolean aFalse) {
isFalse = aFalse;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Console
ValidatorResponse{valid=false, error='isTrue is not true', propertyName='isTrue', propertyValue=false}
ValidatorResponse{valid=false, error='isFalse is not false', propertyName='isFalse', propertyValue=true}
ValidatorResponse{valid=false, error='age is not valid', propertyName='age', propertyValue=44488}