Visitor概述

Visitor - 图1

UML概述

Visitor - 图2

玩具代码案例 - 基于访问者模式的数据校验器

校验接口

  1. package online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements;
  2. import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
  3. import java.lang.annotation.Annotation;
  4. public interface IValidator {
  5. ValidatorResponse isVerify(Annotation annotation, String propertyName, Object propertyValue);
  6. }

被访问者

  1. package online.javabook.design.gof.behavioral10.visitor.validation.app.visitor;
  2. import online.javabook.design.gof.behavioral10.visitor.validation.other.ValidatorResponse;
  3. import java.lang.annotation.Annotation;
  4. public interface IValidatorVisitable {
  5. ValidatorResponse accept(ValidatorVisitor validatorVisitor, Annotation attribute, String propertyName, Object propertyValue);
  6. }

校验实现

校验器既是一个校验器,也是一个被访问者。

IsFalseValidator

  1. package online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements;
  2. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsFalse;
  3. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.IValidatorAccept;
  4. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.ValidatorVisitor;
  5. import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
  6. import java.lang.annotation.Annotation;
  7. public class IsFalseValidator implements IValidator, IValidatorVisitable {
  8. @Override
  9. public ValidatorResponse isVerify(Annotation annotation, String propertyName, Object propertyValue) {
  10. boolean isValid = !Boolean.parseBoolean(propertyValue.toString());
  11. IsFalse isFalseAnnotation = (IsFalse) annotation;
  12. return isValid?
  13. new ValidatorResponse(true, propertyName, propertyValue) :
  14. new ValidatorResponse(false, propertyName, propertyValue, isFalseAnnotation.error());
  15. }
  16. @Override
  17. public ValidatorResponse accept(ValidatorVisitor validatorVisitor, Annotation attribute, String propertyName, Object propertyValue) {
  18. return validatorVisitor.visit(this, attribute, propertyName, propertyValue);
  19. }
  20. }
  1. package online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target({ElementType.FIELD})
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface IsFalse {
  9. public String error() default "";
  10. }

IsTrueValidator

  1. package online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements;
  2. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsTrue;
  3. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.IValidatorAccept;
  4. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.ValidatorVisitor;
  5. import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
  6. import java.lang.annotation.Annotation;
  7. public class IsTrueValidator implements IValidator, IValidatorVisitable {
  8. @Override
  9. public ValidatorResponse isVerify(Annotation annotation, String propertyName, Object propertyValue) {
  10. boolean isValid = Boolean.parseBoolean(propertyValue.toString());
  11. IsTrue isTrueAnnotation = (IsTrue) annotation;
  12. return isValid?
  13. new ValidatorResponse(true, propertyName, propertyValue) :
  14. new ValidatorResponse(false, propertyName, propertyValue, isTrueAnnotation.error());
  15. }
  16. @Override
  17. public ValidatorResponse accept(ValidatorVisitor validatorVisitor, Annotation attribute, String propertyName, Object propertyValue) {
  18. return validatorVisitor.visit(this, attribute, propertyName, propertyValue);
  19. }
  20. }
  1. package online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target({ElementType.FIELD})
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface IsTrue {
  9. public String error() default "";
  10. }

IsNotEmptyValidator

  1. package online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements;
  2. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsNotEmpty;
  3. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.IValidatorAccept;
  4. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.ValidatorVisitor;
  5. import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
  6. import java.lang.annotation.Annotation;
  7. public class IsNotEmptyValidator implements IValidator, IValidatorVisitable {
  8. @Override
  9. public ValidatorResponse isVerify(Annotation annotation, String propertyName, Object propertyValue) {
  10. boolean isValid = propertyValue != null && !propertyValue.toString().equals("");
  11. IsNotEmpty isNotEmptyAnnotation = (IsNotEmpty) annotation;
  12. return isValid?
  13. new ValidatorResponse(true, propertyName, propertyValue) :
  14. new ValidatorResponse(false, propertyName, propertyValue, isNotEmptyAnnotation.error());
  15. }
  16. @Override
  17. public ValidatorResponse accept(ValidatorVisitor validatorVisitor, Annotation attribute, String propertyName, Object propertyValue) {
  18. return validatorVisitor.visit(this, attribute, propertyName, propertyValue);
  19. }
  20. }
  1. package online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target({ElementType.FIELD})
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface IsNotEmpty {
  9. public String error() default "";
  10. }

IsNotNullValidator

  1. package online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements;
  2. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsNotNull;
  3. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.IValidatorAccept;
  4. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.ValidatorVisitor;
  5. import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
  6. import java.lang.annotation.Annotation;
  7. public class IsNotNullValidator implements IValidator, IValidatorVisitable {
  8. @Override
  9. public ValidatorResponse isVerify(Annotation annotation, String propertyName, Object propertyValue) {
  10. boolean isValid = propertyValue != null;
  11. IsNotNull isNotNullAnnotation = (IsNotNull) annotation;
  12. return isValid?
  13. new ValidatorResponse(true, propertyName, propertyValue) :
  14. new ValidatorResponse(false, propertyName, propertyValue, isNotNullAnnotation.error());
  15. }
  16. @Override
  17. public ValidatorResponse accept(ValidatorVisitor validatorVisitor, Annotation attribute, String propertyName, Object propertyValue) {
  18. return validatorVisitor.visit(this, attribute, propertyName, propertyValue);
  19. }
  20. }
  1. package online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target({ElementType.FIELD})
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface IsNotNull {
  9. public String error() default "";
  10. }

IsNumberValidator

  1. package online.javabook.gof.behavioral.patterns10.visitor.validation.app.elements;
  2. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.annotations.IsNumber;
  3. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.IValidatorAccept;
  4. import online.javabook.gof.behavioral.patterns10.visitor.validation.app.visitor.ValidatorVisitor;
  5. import online.javabook.gof.behavioral.patterns10.visitor.validation.other.ValidatorResponse;
  6. import java.lang.annotation.Annotation;
  7. public class IsNumberValidator implements IValidator, IValidatorAccept {
  8. @Override
  9. public ValidatorResponse isVerify(Annotation annotation, String propertyName, Object propertyValue) {
  10. IsNumber isNumberAnnotation = (IsNumber) annotation;
  11. int number = Integer.parseInt(propertyValue.toString());
  12. boolean isValid = (isNumberAnnotation.min() <= number) && (number <= isNumberAnnotation.max());
  13. return isValid?
  14. new ValidatorResponse(true, propertyName, propertyValue) :
  15. new ValidatorResponse(false, propertyName, propertyValue, isNumberAnnotation.error());
  16. }
  17. @Override
  18. public ValidatorResponse accept(ValidatorVisitor validatorVisitor, Annotation attribute, String propertyName, Object propertyValue) {
  19. return validatorVisitor.visit(this, attribute, propertyName, propertyValue);
  20. }
  21. }
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}