Làm thế nào để phong cách chức năng giúp với phụ thuộc chế giễu?


10

Từ cuộc phỏng vấn với Kent Beck trong số phát hành Tạp chí Java gần đây:

Binstock: Hãy thảo luận về microservice. Đối với tôi, dường như việc thử nghiệm đầu tiên trên microservice sẽ trở nên phức tạp theo nghĩa là một số dịch vụ, để hoạt động, sẽ cần sự hiện diện của một loạt các dịch vụ khác. Bạn có đồng ý không?

Beck: Có vẻ như cùng một tập hợp thương mại về việc có một lớp lớn hoặc rất nhiều lớp nhỏ.

Binstock: Phải, ngoại trừ tôi đoán, ở đây bạn phải sử dụng rất nhiều giả để có thể thiết lập một hệ thống mà bạn có thể kiểm tra một dịch vụ nhất định.

Beck: Tôi không đồng ý. Nếu đó là một phong cách bắt buộc, bạn phải sử dụng rất nhiều giả. Trong một kiểu chức năng nơi các phụ thuộc bên ngoài được tập hợp lại với nhau trong chuỗi cuộc gọi, thì tôi không nghĩ điều đó là cần thiết. Tôi nghĩ rằng bạn có thể nhận được rất nhiều bảo hiểm trong các bài kiểm tra đơn vị.

Ý anh ấy là sao cơ? Làm thế nào phong cách chức năng có thể giải phóng bạn khỏi chế nhạo phụ thuộc bên ngoài?



1
Nếu họ đang thảo luận cụ thể về Java, tôi nghi ngờ rằng phần lớn cuộc thảo luận đó là tranh luận. Java không thực sự có loại hỗ trợ mà nó cần để cho vay theo kiểu lập trình chức năng được mô tả. Ồ, chắc chắn, bạn có thể sử dụng các lớp tiện ích hoặc có lẽ là Java 8 Lambdas để mô phỏng nó, nhưng ... blecch.
Robert Harvey

Câu trả lời:


8

Một hàm thuần túy là một hàm :

  1. Sẽ luôn đưa ra cùng một kết quả với cùng một lý lẽ
  2. Không có bất kỳ tác dụng phụ nào có thể quan sát được (ví dụ: thay đổi trạng thái)

Giả sử chúng tôi đang viết một số mã để xử lý đăng nhập của người dùng, nơi chúng tôi muốn kiểm tra xem tên người dùng và mật khẩu được cung cấp có đúng không và ngăn người dùng đăng nhập nếu có quá nhiều lần thử thất bại. Trong một phong cách bắt buộc, mã của chúng tôi có thể trông như thế này:

bool UserLogin(string username, string password)
{
    var user = _database.FindUser(username);
    if (user == null)
    {
        return false;
    }
    if (user.FailedAttempts > 3)
    {
        return false;
    }
    // Password hashing omitted for brevity
    if (user.Password != password)
    {
        _database.RecordFailedLoginAttempt(username);
    }
    return true;
}

Rõ ràng rằng đây không phải là một chức năng thuần túy:

  1. Hàm này sẽ không luôn đưa ra cùng một kết quả cho một kết hợp usernamevà cho trước passwordvì kết quả cũng phụ thuộc vào hồ sơ người dùng được lưu trữ trong cơ sở dữ liệu.
  2. Hàm có thể thay đổi trạng thái của cơ sở dữ liệu, tức là nó có tác dụng phụ.

Cũng lưu ý rằng để kiểm tra đơn vị chức năng này, chúng ta cần mô phỏng hai cuộc gọi cơ sở dữ liệu FindUserRecordFailedLoginAttempt.

Nếu chúng ta tái cấu trúc mã này thành một kiểu chức năng hơn, chúng ta có thể sẽ có một cái gì đó giống như thế này:

bool UserLogin(string username, string password)
{
    var user = _database.FindUser(username);
    var result = UserLoginPure(user, password);
    if (result == Result.FailedAttempt)
    {
        _database.RecordFailedLoginAttempt(username);
    }
    return result == Result.Success;
}

Result UserLoginPure(User user, string pasword)
{
    if (user == null)
    {
        return Result.UserNotFound;
    }
    if (user.FailedAttempts > 3)
    {
        return Result.LoginAttemptsExceeded;
    }
    if (user.Password != password)
    {
        return Result.FailedAttempt;        
    }
    return Result.Success;
}

Lưu ý rằng mặc dù UserLoginchức năng vẫn không thuần túy, UserLoginPurechức năng hiện là chức năng thuần túy và kết quả là logic xác thực người dùng cốt lõi có thể được kiểm tra đơn vị mà không cần phải giả định bất kỳ phụ thuộc bên ngoài nào. Điều này là do sự tương tác với cơ sở dữ liệu được xử lý cao hơn trong ngăn xếp cuộc gọi.


Là sự giải thích của bạn: mệnh lệnh-style = microsfull microservice và function-style = statless microservies ?
k3b

@ k3b Sắp xếp, ngoại trừ bit về các dịch vụ vi mô. Rất đơn giản Phong cách mệnh lệnh liên quan đến thao túng trạng thái trong khi kiểu chức năng sử dụng các hàm thuần túy mà không cần thao tác trạng thái.
Justin

1
@Justin: Tôi sẽ nói rằng phong cách chức năng tách biệt rõ ràng các chức năng thuần túy với mã với các hiệu ứng phụ, như bạn đã làm trong ví dụ của mình. Nói cách khác, mã chức năng vẫn có thể có tác dụng phụ.
Giorgio

Cách tiếp cận chức năng sẽ trả về một cặp có kết quả và người dùng, vì trong lần thử thất bại, result.FailsAtteem là kết quả với một người dùng mới có cùng dữ liệu như ban đầu, ngoại trừ một lần thử thất bại và chức năng thuần túy thực hiện gây ra tác dụng phụ cho người dùng được đưa ra như một tham số.
tăngDarkness

chỉnh sửa cho phần cuối của nhận xét trước đây của tôi: "và một hàm thuần túy KHÔNG gây ra tác dụng phụ cho người dùng được cung cấp dưới dạng tham số."
tăngDarkness
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.