簡體   English   中英

用Java創建輸入驗證方法的良好實踐是什么?

[英]What's good practice of creating input validation method in Java?

如果我想驗證輸入內容,是否應該將驗證代碼作為私有幫助器方法或創建單獨的靜態幫助器類? 驗證代碼是否會增加對象的大小?

更多信息

假設我有一堂課

import java.util.Vector;


public class Place {
    private final double    longitude;
    private final double    latitude;
    private final String    id;

    private       String           address;
    private       String           name;
    private       String           types;
    private       String           icon;
    private       String           phoneNumber;
    private       String           websiteUrl;
    private       int              rating;
    private       Vector<Integer>  challenges;

    public static class Builder {
        // required parameter
        private final double    longitude;
        private final double    latitude;
        private final String    id;
        // optional parameter
        private       String           address = "n/a";
        private       String           name = "n/a";
        private       String           icon = "n/a";
        private       String           phoneNumber = "n/a";
        private       String           websiteUrl = "n/a";
        private       String           types = "n/a";
        private       Vector<Integer>  challenges = new Vector<Integer>();
        private       int              rating = 0;

        public Builder(double longitude, double latitude, String id) {
            assert(longitude >= -180.0 && longitude <= 180.0);
            assert(latitude >= -90.0 && longitude <= 90.0);
            this.longitude = longitude;
            this.latitude = latitude;
            this.id = id;
        }

        public Builder address(String address) {
            this.address = address;
            return this;
        }

        public Builder types(String types) {
            this.types = types;
            return this;
        }

        public Builder name(String name) {
            this.name = name;
            return this;
        }

        public Builder icon(String icon) {
            this.icon = icon;
            return this;
        }

        public Builder phoneNumber(String phoneNumber) {
            this.phoneNumber = phoneNumber;
            return this;
        }

        public Builder websiteUrl(String websiteUrl) {
            this.websiteUrl = websiteUrl;
            return this;
        }

        public Builder builder(int rating) {
            this.rating = rating;
            return this;
        }

        public Place build() {
            return new Place(this);
        }
    }

    public Place(Builder builder) {
        // required parameters
        longitude = builder.longitude;
        latitude = builder.latitude;
        id = builder.id;

        // optional parameters
        address = builder.address;
        types = builder.types;
        name = builder.name;
        icon = builder.icon;
        phoneNumber = builder.phoneNumber;
        websiteUrl = builder.websiteUrl;
        rating = builder.rating;
        challenges = builder.challenges;
    }

    public double getLongitude() {
        return longitude;
    }

    public double getLatitude() {
        return latitude;
    }

    public String getId() {
        return id;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getAddress() {
        return address;
    }

    public String getTypes() {
        return types;
    }

    public void setTypes(String types) {
        this.types = types;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setIconUrl(String icon) {
        this.icon = icon;
    }

    public String getIcon() {
        return icon;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }


    public void setWebsiteUrl(String websiteUrl) {
        this.websiteUrl = websiteUrl;
    }

    public String getWebsiteUrl() {
        return websiteUrl;
    }

    public void setRating(int rating) {
        this.rating = rating;
    }

    public int getRating() {
        return rating;
    }

    @Override
    public String toString() {
        return "(" + Double.toString(longitude) + ", " +  Double.toString(latitude) + ")";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Place other = (Place) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        }
        else if (!id.equals(other.id))
            return false;
        return true;
    }

    public Vector<Integer> getChallenges() {
        return new Vector<Integer>(challenges);
    }

    public void addChallenges(Integer i) {
        this.challenges.add(i);
    }

    public void showChallenges() {
        for (Integer i : challenges) {
            System.out.print(i + ", ");
        }
    }
}

如果在設置address參數之前必須先驗證address參數,在這種情況下應將用於驗證地址的代碼放在哪里?

如果您只是在看輸入的String的格式正確或長度正確,那么您將使用私有方法。 另一方面,如果您要檢查地址是否正確(在地圖上查找地址)或更高級的內容,則可以創建一個AddressValidator接口並從該私有方法中調用它。

私有方法的原因是您從構造函數,setter或任何其他可以提供地址的方法中調用此方法。 界面的原因是,您可能希望擁有一個在線/離線AddressValidator(MockAddressValidator,或者為每個國家/地區調用不同類的地址)。

由於AddressValidator可以在其他類中重用,並且為了保持代碼的清潔,我將其創建為頂級接口+ OnlineAddressValidator。 這也使您的課程更具可讀性。 對於完全可配置性,您可能需要考慮如何提供AddressValidator實例,例如通過構造函數或定義為靜態最終驗證器的實例。

public interface AddressValidator {
    static class AddressValidatorResult {
        // some results, you might want to return some useful feedback (if not valid)

        boolean isValid() {
            throw new IllegalStateException("Method not implemented yet");
        }
    }

    public static class AddressValidationException extends Exception {
        private AddressValidationException(AddressValidatorResult result) {
            // add some implementation
        }
    }


    // don't throw ValidateException here, invalid addresses are normal for
    // validators, even if they aren't for the application that uses them
    AddressValidatorResult validateAddress(String address);
        // don't throw ValidateException here, invalid addresses are normal for
        // validators, even if they aren't for the application that uses them
}

public class DefaultAddressValidator implements AddressValidator {

    public static class Params {
        // some parameters for this specific validator
    }

    private final Params params;

    public DefaultAddressValidator(Params params) {
        // creates this validator
        this.params = params;
    }

    @Override
    public AddressValidatorResult validateAddress(String address) {
        // perform your code here

        // I don't like "return null" as it may lead to bugs
        throw new IllegalStateException("Method not implemented yet");
    }
}


// and use it like this
private void validateAddress(String address) throws AddressValidationException {
    // e.g. field AddressValidator set in constructor 
    AddressValidatorResult result = addressValidator.validateAddress(address);
    if (!result.isValid()) {
        throw new AddressValidationException(result);
    }
}

我應該將驗證代碼作為私有幫助程序方法還是應該創建單獨的靜態幫助程序類?

這完全取決於您的上下文。 如果不知道您要實現的目標,就不可能說出最好的設計。

編輯后: IMO,仍然很難告訴您。 如果您只需要在應用程序的一個點中驗證地址(id:setter方法),則可以在setter方法中對其進行驗證。 如果輸入無效,我該IllegalArgumentException


驗證代碼是否會增加對象的大小?

但是,第二個問題的答案為 要了解原因,您必須知道什么是面向對象的編程。

一些參考:

我應該將驗證代碼作為私有幫助程序方法還是應該創建單獨的靜態幫助程序類?

這取決於您是否認為出於相同的目的(輸入驗證)還需要在另一個類中重用相同的方法,最好在單獨的靜態幫助器類中編寫該方法,以便您可以重用該方法並輕松維護它。 如果每次需要進行更改時都在多個類中編寫相同的私有幫助器方法,則必須編輯每個類中的每個方法, 而使用靜態幫助器類,則只能在一個位置更改代碼。

我傾向於盡可能在get()和set()方法內進行驗證-調用外部靜態方法來執行常見任務,例如檢查日期或清除輸入(即避免sql注入)

如果僅在一個類中使用(並且將永遠使用)驗證,請將其保留為私有幫助器方法。 如有疑問,我傾向於將功能引入靜態助手類。 它對代碼量的影響很小,無需再努力實現,並且更加靈活。

簡短的答案是:您應該按照框架告訴您的方式實施驗證代碼。 通常,這是一個公共方法或注釋。 接口也可以工作。 如果添加代碼,則類的大小將增加。

數據驗證應由軟件的基礎結構自動調用。 這有助於防止程序員忘記調用適當的代碼。 因此,方法應該是公開的(接口也可以使用)。

像Struts,Spring,Hibernate這樣的框架都有自己的驗證系統。 Java EE利用bean驗證。

我建議使用bean驗證,因為無論輸入源如何,它都會執行驗證。 當大多數人想到輸入驗證時,他們就會想到來自用戶的數據,例如HTTP請求,命令控制台,Swing文本字段。 在這些情況下,Spring和Struts驗證通常很好。 但是在為企業開發的壽命長的程序中,經常會引入其他數據饋送,例如從另一個程序更新SQL數據庫,崩潰后恢復數據庫,企業服務總線,JMS。

這就是為什么我更喜歡bean驗證的原因。 不利之處是不必要地驗證了“安全來源”(您知道的數據沒有污染)。 但是,以今天的處理能力,這應該很少成為一個重大問題。 Java EE教程

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM