Làm lại một hàm trả về một mã số đại diện cho nhiều trạng thái khác nhau


10

Tôi đã thừa hưởng một số mã khủng khiếp mà tôi đã bao gồm một mẫu ngắn dưới đây.

  • Có một tên cho mô hình chống đặc biệt này?
  • Một số khuyến nghị để tái cấu trúc này là gì?

    // 0=Need to log in / present username and password
    // 2=Already logged in
    // 3=Inactive User found
    // 4=Valid User found-establish their session
    // 5=Valid User found with password change needed-establish their session
    // 6=Invalid User based on app login
    // 7=Invalid User based on network login
    // 8=User is from an non-approved remote address
    // 9=User account is locked
    // 10=Next failed login, the user account will be locked
    
    public int processLogin(HttpServletRequest request, HttpServletResponse response, 
                            int pwChangeDays, ServletContext ServContext) { 
    }
    

2
Thế nào là "phát hiện ra thiết lập lại""cần thiết lập lại" ?
Tulains Córdova

4
Đó được coi là một dấu gạch ngang , đọc như "tìm thấy người dùng hợp lệ: thiết lập phiên của họ".
BJ Myers

2
@A_B Những giá trị trả về nào là thông tin đăng nhập thành công là thông tin đăng nhập thất bại. Không phải tất cả là hiển nhiên.
Tulains Córdova

@A_B "thiết lập phiên của họ" có nghĩa là "phiên được thiết lập" hay "cần thiết lập phiên"?
Tulains Córdova

@ TulainsCórdova: "Thiết lập" có nghĩa nhiều như "tạo" (ít nhất là trong bối cảnh này) - vì vậy "thiết lập phiên của họ" gần bằng với "tạo phiên của họ"
hoffmale

Câu trả lời:


22

Mã này không chỉ xấu vì các số ma thuật , mà bởi vì nó kết hợp một số ý nghĩa trong mã trả về, ẩn bên trong ý nghĩa của nó là một lỗi, một cảnh báo, cho phép tạo một phiên hoặc kết hợp cả ba, làm cho nó trở thành một đầu vào xấu cho việc ra quyết định.

Tôi sẽ đề xuất cách tái cấu trúc sau: trả lại một enum với kết quả có thể (như được đề xuất trong các câu trả lời khác), nhưng thêm vào enum một thuộc tính cho biết đó có phải là từ chối, từ bỏ (tôi sẽ cho phép bạn vượt qua lần cuối này) hoặc nếu nó ổn (PASS):

public LoginResult processLogin(HttpServletRequest request, HttpServletResponse response, 
                            int pwChangeDays, ServletContext ServContext) { 
    }

==> Đăng nhậpResult.java <==

public enum LoginResult {
    NOT_LOGGED_IN(Severity.DENIAL),
    ALREADY_LOGGED_IN(Severity.PASS),
    INACTIVE_USER(Severity.DENIAL),
    VALID_USER(Severity.PASS),
    NEEDS_PASSWORD_CHANGE(Severity.WAIVER),
    INVALID_APP_USER(Severity.DENIAL),
    INVALID_NETWORK_USER(Severity.DENIAL),
    NON_APPROVED_ADDRESS(Severity.DENIAL),
    ACCOUNT_LOCKED(Severity.DENIAL),
    ACCOUNT_WILL_BE_LOCKED(Severity.WAIVER);

    private Severity severity;

    private LoginResult(Severity severity) {
        this.severity = severity;
    }

    public Severity getSeverity() {
        return this.severity;
    }
}

==> Severity.java <==

public enum Severity {
    PASS,
    WAIVER,
    DENIAL;
}

==> Test.java <==

public class Test {

    public static void main(String[] args) {
        for (LoginResult r: LoginResult.values()){
            System.out.println(r + " " +r.getSeverity());           
        }
    }
}

Đầu ra cho Test.java hiển thị mức độ nghiêm trọng cho mỗi LoginResult:

NOT_LOGGED_IN : DENIAL
ALREADY_LOGGED_IN : PASS
INACTIVE_USER : DENIAL
VALID_USER : PASS
NEEDS_PASSWORD_CHANGE : WAIVER
INVALID_APP_USER : DENIAL
INVALID_NETWORK_USER : DENIAL
NON_APPROVED_ADDRESS : DENIAL
ACCOUNT_LOCKED : DENIAL
ACCOUNT_WILL_BE_LOCKED : WAIVER

Dựa trên cả giá trị enum và mức độ nghiêm trọng của nó, bạn có thể quyết định việc tạo ra tiến trình phiên hay không.

BIÊN TẬP:

Để phản hồi bình luận của @ T.Sar, tôi đã thay đổi các giá trị có thể nghiêm trọng thành PASS, WAIVER và DENIAL thay vì (OK, CẢNH BÁO và LRI). Theo cách đó, rõ ràng là một DENIAL (trước đây là LRI) không phải là một lỗi và không nhất thiết phải chuyển thành một ngoại lệ. Người gọi kiểm tra đối tượng và quyết định có ném ngoại lệ hay không, nhưng DENIAL là trạng thái kết quả hợp lệ do gọi processLogin(...).

  • PASS: hãy tiếp tục, tạo một phiên nếu không tồn tại
  • WAIVER: đi trước thời gian này, nhưng người dùng lần sau bạn có thể không được phép vượt qua
  • DENIAL: xin lỗi, người dùng không thể vượt qua, không tạo phiên

bạn cũng có thể xây dựng một enum "phức tạp" (enum với các thuộc tính) để nhúng mức độ lỗi trong Enum. Tuy nhiên, hãy cẩn thận vì nếu bạn sử dụng các công cụ tuần tự hóa somme, điều đó có thể không vượt qua tốt.
Walfrat

Ném ra các ngoại lệ trong các trường hợp lỗi và chỉ lưu enum để thành công cũng là một lựa chọn.
T. Sar

@ T.Sar Vâng, như tôi đã hiểu, họ không phải là lỗi mỗi se mà từ chối để tạo một phiên vì một số lý do. Tôi sẽ chỉnh sửa câu trả lời.
Tulains Córdova

@ T.Sar Tôi đã thay đổi các giá trị thành PASS, WAIVER và DENIAL để làm rõ hơn những gì trước đây tôi gọi là LRI là trạng thái hợp lệ. Có lẽ bây giờ tôi nên nghĩ ra một cái tên hay hơn choSeverity
Tulains Córdova

Tôi đã nghĩ về điều gì khác với đề nghị của tôi, nhưng tôi thực sự thích đề xuất của bạn! Chắc chắn tôi đang tăng +1!
T. Sar

15

Đây là một ví dụ về Nỗi ám ảnh nguyên thủy - sử dụng các kiểu nguyên thủy cho các nhiệm vụ "đơn giản" mà cuối cùng trở nên không đơn giản.

Điều này có thể đã bắt đầu như một mã trả về một booldấu hiệu thành công hay thất bại, sau đó biến thành inttrạng thái khi có trạng thái thứ ba và cuối cùng trở thành một danh sách toàn bộ các điều kiện lỗi không được ghi nhận.

Tái cấu trúc điển hình cho vấn đề này là tạo ra một lớp / struct / enum / object / bất cứ thứ gì có thể biểu thị tốt hơn giá trị trong câu hỏi. Trong trường hợp này, bạn có thể xem xét chuyển sang một enumđiều kiện có chứa các điều kiện kết quả hoặc thậm chí một lớp có thể chứa boolthành công hay thất bại, thông báo lỗi, thông tin bổ sung, v.v.

Để biết thêm các mẫu tái cấu trúc có thể hữu ích, hãy xem Công thức mùi của công nghệ tái cấu trúc .


7

Tôi gọi đó là một trường hợp "số ma thuật" - những con số đặc biệt và không có ý nghĩa rõ ràng.

Việc tái cấu trúc mà tôi sẽ áp dụng ở đây là cơ cấu lại kiểu trả về cho một enum, vì nó gói gọn mối quan tâm miền trong một loại. Xử lý các lỗi biên dịch rơi ra từ đó nên có thể là từng phần, vì các enum java có thể được đặt hàng và đánh số. Ngay cả khi không, cũng không khó để đối phó trực tiếp với họ thay vì quay trở lại với ints.


Đó không phải là những gì thường có nghĩa với 'số ma thuật'.
D Drmmr

2
Nó sẽ hiển thị dưới dạng số ma thuật tại các trang web cuộc gọi, như trongif (processLogin(..) == 3)
Daenyth

@DDrmmr - Đây chính xác là ý nghĩa của mùi mã 'số ma thuật'. Chữ ký hàm này gần như chắc chắn ngụ ý rằng processLogin () chứa các dòng như "return 8;" trong quá trình triển khai và khá nhiều mã buộc sử dụng processLogin () để trông giống như thế này "if (resultFromProcessLogin == 7) {".
Stephen C. Steel

3
@Stephen Giá trị thực của các con số không liên quan ở đây. Họ chỉ là ID. Thuật ngữ số ma thuật thường được sử dụng cho các giá trị trong mã có ý nghĩa, nhưng ý nghĩa của ai là không có giấy tờ (ví dụ: trong một tên biến). Thay thế các giá trị ở đây bằng các biến số nguyên có tên sẽ không khắc phục được vấn đề.
D Drmmr

2

Đây là một đoạn mã đặc biệt khó chịu. Các antipotype được gọi là "mã trả lại ma thuật". Bạn có thể tìm thấy một cuộc thảo luận ở đây .

Nhiều giá trị trả về chỉ trạng thái lỗi. Có một cuộc tranh luận hợp lệ về việc có nên sử dụng xử lý lỗi cho kiểm soát luồng hay không, nhưng trong trường hợp của bạn, tôi nghĩ có 3 trường hợp: thành công (mã 4), thành công nhưng cần thay đổi mật khẩu (mã 5) và "không được phép". Vì vậy, nếu bạn không quan tâm đến việc sử dụng các ngoại lệ để kiểm soát luồng, bạn có thể sử dụng các ngoại lệ để chỉ ra các trạng thái đó.

Một cách tiếp cận khác là cấu trúc lại thiết kế để bạn trả về một đối tượng "người dùng", với thuộc tính "hồ sơ" và "phiên" để đăng nhập thành công, thuộc tính "must_change_password" nếu cần và một loạt các thuộc tính để cho biết lý do tại sao nhật ký -Trong thất bại nếu đó là dòng chảy.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.