2017-04-05 30 views
3

鏈檢驗器將像下面如何用Java

public class MyClass { 

    private Integer myField; 
    private Result result; 
    // more global variables 

    public MyResult check(Integer myParameter) { 
     init(myParameter); 

     if (myField < 0) { 
      result.setErrorMessage("My error message"); 
      return result; 
     } 

     // a lot more 'checks' like above where something may be written 
     // to the error message and the result gets returned. 
    } 

    private void init(Integer myParameter) { 
     result = new Result(); 
     result.setExistsAnnouncement(/*search a certain object via crudService with myParameter*/); 
     // initialize other global variables including myField 
    } 

} 

問題一類是上面的check方法是太長,有許多return語句。我想到了一些重構,但我仍然不確定要做什麼。我在想像一個連鎖模式。然後,我會執行幾個檢查員類,他們或者調用鏈中的下一個檢查器,或者返回result與相應的errorMessage

但是後來我有了一個更好的主意(至少我這麼認爲):爲什麼不像java 8那樣表現?我想過使用像Try - Success - Failure - 模式。但我不知道如何實現這一點。我想到的是這樣的:

entrancePoint.check(firstChecker) 
    .check(secondChecker) 
    .check // and so on 

的想法是:當check失敗時,它會表現得像Optional.map()和返回類似的Optional.EMPTY(或在這方面有點像Failure)。當check成功時,它應該繼續下一步檢查(返回Success)。

你有這樣的經驗嗎?

+0

我以前做過同樣的事情,但沒有鏈接。我做到這一點的方式是1.)創建一個'Checkable'接口,它包含一個方法'check()',它需要拋出一些'CheckFailedException',以及這個接口的一些具體實現。 2.)在'MyClass'中創建'List '變量,並且'MyClass'中的check()遍歷每個checkable元素並在其上調用check()。 3.)使用構造函數或setter方法初始化要執行的檢查列表(我使用的是spring,所以我是這麼做的)。 – Jay

+0

是的,這可能有效。但我認爲那裏有更好的解決方案。目前我仍然試圖通過Try-Failure-Success-Pattern來完成此項工作,如下所示:https://dzone.com/articles/whats-wrong-java-8-part-iv。區別在於:在鏈接他們做例外...我使用此嘗試成功失敗實現btw:https://gist.github.com/mariofusco/8287951 – Chris311

+0

你可以使用Hibernate驗證器,如果你在編譯時知道約束條件? http://hibernate.org/validator/ –

回答

4

當我們想到的驗證,它通常是一個複合模式。它大致描述爲:

如果這是有效的,那麼做一下SOMETHING

而且,由於您正在施加壓力,您希望鏈條上的多個檢查器在其區域內執行驗證,您可以實施責任鏈模式。

考慮一下:

你可以有一個Result對象,它可以包含一個有關故障信息以及簡單的真/假。

您可以有一個Validator對象,該對象不需要驗證,並返回Result的實例。

public interface Result { 
    public boolean isOk(); 
    public String getMessage(); 
} 

// We make it genric so that we can use it to validate 
// any type of Object that we want. 
public interface Validator<T> { 
    public Result validate(T value); 
} 

現在,當你說你要驗證「X」使用多個棋子,你堂堂一個驗證規則這不過是Validator對象的集合,同時的Validator一個實例本身。話雖如此,你不能再使用Result對象來檢查規則的驗證結果。您將需要一個可以保留結果爲{Validator=Result}的複合對象Result。它看起來不像HashMap<Validator, Result>的執行嗎?是的,因爲它是。

現在你可以實現你的RuleCompositeResult爲:

public class Rule extends ArrayList<Validator> implements Validator { 

    public Rule(Validator<?> ... chain) { 
     addAll(Arrays.asList(chain)); 
    } 

    public Object validate(Object target) { 
     CompositeResult result = new CompositeResult(size()); 
     for (Validator rule : this) { 
      Result tempResult = rule.validate(value); 
      if (!tempResult.isOk()) 
       result.put(rule, tempResult); 
     } 
     return result; 
    } 
} 

public class CompositeResult extends HashMap<Validator, Result> implements 
     Result { 

    private Integer appliedCount; 

    public CompositeResult(Integer appliedCount) { 
     this.appliedCount = appliedCount; 
    } 

    @Override 
    public boolean isOk() { 
     boolean isOk = true; 
     for (Result r : values()) { 
      isOk = r.isOk(); 
      if (!isOk) 
       break; 
     } 
     return isOk; 
    } 

    @Override 
    public String getMessage() { 
     return toString(); 
    } 

    public Integer failCount() { 
     return size(); 
    } 

    public Integer passCount() { 
     return appliedCount - size(); 
    } 

} 

就是這樣!現在,爲了實現你的棋子:

public class Checker1 implements Validator<Integer> { 
    /* Implementation */ 
} 

public class CheckerN implements Validator<Integer> { 
    /* Implementation */ 
} 

,它的時間做了驗證:

Validator<Integer> checkingRule = new Rule(new Checker1(), new CheckerN()); 
CompositeResult result = checkingRule.validate(yourParameter); 
if (result.isOk()) 
    System.out.println("All validations passed"); 
else 
    System.out.println(result.getFailedCount() + " validations failed"); 

簡單利落。

我上傳了一個example in my public repo供您玩耍。

+0

非常好的解決方案!只有一個區別:你所有的驗證器都被執行,而我想在第一次檢查失敗後返回。但可以輕鬆調整您的「驗證」以滿足我的要求。 – Chris311

+0

@ Chris311你是對的。如果規則的任何組件失敗,您都可以拋出異常,但我太懶惰,無法實現該規則。 – Jay

+0

那麼我寧願使用'break'。 – Chris311

0

您可以使用Hibernate Validator(或其他實現的JSR 303/349/380 Bean驗證標準)。

示例用戶類在使用無效參數實例化時拋出異常。您可以檢查所有的內置限制in the docs

public final class User { 

     @NotNull 
     private final String username; 

     @NotNull 
     private final String firstName; 

     @NotNull 
     private final String lastName; 

     public User(String username, String firstName, String lastName) { 
     this.username = username; 
     this.firstName = firstName; 
     this.lastName = lastName; 

     MyValidator.validate(this); 
     } 
     // public getters omitted 
    } 

而且驗證器類:

import java.security.InvalidParameterException; 
import java.util.Set; 

import javax.validation.ConstraintViolation; 
import javax.validation.Validation; 
import javax.validation.Validator; 
import javax.validation.ValidatorFactory; 

public final class MyValidator { 

    public static void validate(Object object) { 

     ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 
     Validator validator = factory.getValidator(); 

     Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object); 

     if (!constraintViolations.isEmpty()) { 
      ConstraintViolation<Object> firstViolation = constraintViolations.iterator().next(); 

      throw new InvalidParameterException("not valid " 
      + object.getClass() 
      + " failed property ' " + firstViolation.getPropertyPath() + " ' " 
      + " failure message ' " + firstViolation.getMessage() + " ' "); 
     } 
    } 
} 

和消息:

java.security.InvalidParameterException:無效類 com.foo .bar.User失敗的屬性'firstName'失敗消息'可能不爲空'

不要忘記,包括the dependency在你的pom.xml(如果你使用Maven)

<dependency> 
    <groupId>org.hibernate</groupId> 
    <artifactId>hibernate-validator</artifactId> 
    <version>5.4.1.Final</version> 
</dependency>