Viết hàm trả về nhiều giá trị

Chào các bạn, không biết có cách nào cho 1 hàm trả về nhiều giá trị không ? mình ví dụ cụ thể như này: mình viết hàm kiểm tra đăng nhập trả về boolean, trong hàm này chia ra nhiều trường hợp để check: như mail không tồn tại, đúng mail nhưng sai pass, đúng mail đúng pass nhưng acc bị khóa hoặc mail chưa xác thực ,…

public boolean checkLogin(email, password){
    if(sai mail) return false;
    if(sai pass) return false;
    if(acc bị khóa) return false;
    if(mail chưa xác thực) return false;
    return true; // pass qua các case trên là đăng nhập thành công
}

Mình muốn không những trả về boolean và còn phải kèm theo 1 thông báo lỗi nữa. Mình có 3 cách để giải quyết trường hợp này nhưng không tối ưu lắm.

  1. Tạo một đối tượng kết quả login với 2 thuộc tính là :
    • boolean kết quả.
    • String message.
      ==> cho phương thức login trả về đối tượng này.
  2. login() trả về một hashMap với key là mã lỗi còn value là thông báo kèm theo.
  3. Cho login() trả về int, với 1 là đăng nhập thành công, 2 là sai mail, 3 là sai pass, 4 là bị khóa acc, … rồi hàm nào gọi phương thức login() thì tự xử lý tiếp.

Không biết có cách nào khác không, hay có kểu dữ liệu nào mà gồm nhiều giá trị bên trong giống như tọa độ A(x,y) B(x,y,z) . Cảm ơn mọi người (Mình hỏi chung thôi, không phụ thuộc ngôn ngữ nào cả)

EDIT (@library) Cậu nên tham khảo cách dùng markdown để format cho những post sau nhé!

  • Cơ bản nhất thì người ta sẽ dùng cách thứ 3, nhưng thay vì int, thì sẽ trả về kiểu enum như sau:
enum LoginResult{
    OK,
    ERR_WRONG_EMAIL,
    ERR_WRONG_PASS,
    ERR_LOCKED_ACC,
    ERR_UNREGISTERED_EMAIL,
   ....
}

Rồi có 1 chỗ map cái enum này qua string như bạn đề cập.

  • Cách thuần OOP thì chắc là checkLogin sẽ throw exception với message nếu check invalid.

  • Cách mới hơn chút chính là dùng plus type như std::variant bên C++:

    struct OK{};
    struct ERR{
      std::string message;
    };
    using LoginResult = std::variant<OK, ERR>;

Như bên Rust thì có std::result, hay haskell thì có Data.Either chính là cùng 1 nguyên lý

7 Likes

Có 1 cách khác cho cậu, đó là dùng exception.

    public void checkLogin(email, password) throws Exception {
        if(sai mail) throw WrongEmailFormatException("You shall not pass! Invalid email format");
        if(sai pass)  throw WrongPasswordException("You shall not pass! Boohoo, wrong password!");
        if(acc bị khóa)  throw AccountLockException("You shall not pass! This account got locked!");
        if(mail chưa xác thực)  throw InvalidEmailException("You shall not pass! The email is not verified.");
        // Congrats! You passed!
    }

Ở bên ngoài, cậu sẽ có đoạn code xử lý exception, với từng exception sẽ đưa ra thông điệp tương ứng, và không cần phải boolean gì cả.
Tớ nghĩ cách này elegant nhất :smile:


Các option khác tớ đoán cậu phần nào hiểu được sao nó không tối ưu.

  1. Việc tạo 1 đối tượng có 2 thuộc tính chạy được, nhưng đồng nghĩa với nó là tất cả các validation khác cũng phải có đối tượng tương ứng.
  2. Việc sử dụng hash map chạy được, nhưng rất khó đọc. Ai đó sử dụng method của cậu sẽ không biết key của map là gì, và value của map là gì, và kỳ vọng có bao nhiêu entry trong map được trả về.
    Mặt khác, trả về multiple result cho 1 hàm thường được coi là bad practice, vì hàm của cậu làm nhiều công việc khác nhau 1 lúc (1 hàm chỉ nên làm duy nhất 1 nhiệm vụ thôi).
  3. Trả về mã lỗi cũng chạy được, nhưng 1 lần nữa, cậu cũng không hiểu mã lỗi 1 nghĩa là gì.
    Trả về enum thì dễ đọc hơn, tuy nhiên cậu cũng sẽ có các enum khác nhau cho từng validation.

Về exception, cậu hoàn toàn có thể có 1 exception duy nhất ValidationFailedException, với thông điệp lỗi rõ ràng. Nó sẽ dễ cho cậu khi handle bên ngoài, và nó là 1 luồng khác, cậu không cần quan tâm nó khi đọc luồng chính.
Vậy nên, exception handling thường là cách elegant nhất cho cậu.

6 Likes

Bạn thêm ngôn ngữ mà bạn đang dùng đi. Cách trả về dạng Either<Value, Error> thường sử dụng trong một số ngôn ngữ thôi.

5 Likes

1 hoặc 3, cái nào cũng được
nếu làm bài tập thì không quá quan trọng

cách 3 gọn gàng hơn, tuy nhiên cần define rõ ràng các mã code

4 Likes

Mình thì sẽ chọn làm theo cách 3

1 Like

Tạo từng cái customException riêng là ok nhất.

4 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?