Quản lý phụ thuộc là một vấn đề lớn trong OOP vì hai lý do sau:
- Sự kết hợp chặt chẽ của dữ liệu và mã.
- Sử dụng phổ biến các tác dụng phụ.
Hầu hết các lập trình viên OO coi việc kết hợp chặt chẽ giữa dữ liệu và mã là hoàn toàn có lợi, nhưng nó đi kèm với một chi phí. Quản lý luồng dữ liệu qua các lớp là một phần không thể tránh khỏi của lập trình trong bất kỳ mô hình nào. Kết hợp dữ liệu và mã của bạn thêm một vấn đề nữa là nếu bạn muốn sử dụng một hàm tại một điểm nhất định, bạn phải tìm cách đưa đối tượng của nó đến điểm đó.
Sử dụng các tác dụng phụ tạo ra những khó khăn tương tự. Nếu bạn sử dụng một tác dụng phụ cho một số chức năng, nhưng muốn có thể trao đổi việc thực hiện nó, bạn không có lựa chọn nào khác ngoài việc loại bỏ sự phụ thuộc đó.
Hãy xem xét như một ví dụ về một chương trình spam giúp loại bỏ các trang web cho các địa chỉ email sau đó gửi email cho họ. Nếu bạn có tư duy DI, ngay bây giờ bạn đang nghĩ về các dịch vụ bạn sẽ gói gọn đằng sau các giao diện và dịch vụ nào sẽ được đưa vào đâu. Tôi sẽ để lại thiết kế đó như một bài tập cho người đọc. Nếu bạn có tư duy FP, ngay bây giờ bạn đang nghĩ đến đầu vào và đầu ra cho lớp chức năng thấp nhất, như:
- Nhập địa chỉ trang web, xuất văn bản của trang đó.
- Nhập văn bản của một trang, xuất ra một danh sách các liên kết từ trang đó.
- Nhập văn bản của trang, xuất danh sách địa chỉ email trên trang đó.
- Nhập danh sách địa chỉ email, xuất danh sách địa chỉ email đã bị xóa.
- Nhập địa chỉ email, xuất email spam cho địa chỉ đó.
- Nhập email spam, xuất các lệnh SMTP để gửi email đó.
Khi bạn nghĩ về mặt đầu vào và đầu ra, không có phụ thuộc chức năng, chỉ có phụ thuộc dữ liệu. Đó là những gì làm cho chúng dễ dàng để kiểm tra đơn vị. Lớp tiếp theo của bạn sắp xếp để đầu ra của một chức năng được đưa vào đầu vào của lớp tiếp theo và có thể dễ dàng trao đổi các triển khai khác nhau khi cần.
Theo một nghĩa rất thực, lập trình chức năng tự nhiên thúc đẩy bạn luôn đảo ngược các phụ thuộc chức năng của mình và do đó bạn thường không phải thực hiện bất kỳ biện pháp đặc biệt nào để làm như vậy sau khi thực tế. Khi bạn làm như vậy, các công cụ như các hàm bậc cao hơn, các bao đóng và ứng dụng một phần sẽ giúp thực hiện dễ dàng hơn với ít nồi hơi hơn.
Lưu ý rằng bản thân nó không phụ thuộc mà có vấn đề. Đó là sự phụ thuộc chỉ ra sai cách. Lớp tiếp theo có thể có chức năng như:
processText = spamToSMTP . emailAddressToSpam . removeEmailDups . textToEmailAddresses
Lớp này hoàn toàn ổn khi có các phụ thuộc được mã hóa cứng như thế này, vì mục đích duy nhất của nó là dán các chức năng của lớp thấp hơn lại với nhau. Hoán đổi một triển khai cũng đơn giản như tạo ra một thành phần khác nhau:
processTextFancy = spamToSMTP . emailAddressToFancySpam . removeEmailDups . textToEmailAddresses
Sự tái tạo dễ dàng này được thực hiện bằng cách thiếu tác dụng phụ. Các chức năng lớp dưới hoàn toàn độc lập với nhau. Lớp tiếp theo có thể chọn lớp processText
thực sự được sử dụng dựa trên một số cấu hình người dùng:
actuallyUsedProcessText = if (config == "Fancy") then processTextFancy else processText
Một lần nữa, không phải là một vấn đề bởi vì tất cả các phụ thuộc chỉ ra một cách. Chúng ta không cần phải đảo ngược một số phụ thuộc để có được tất cả chúng theo cùng một cách, bởi vì các hàm thuần túy đã buộc chúng ta phải làm như vậy.
Lưu ý rằng bạn có thể làm cho điều này được kết hợp nhiều hơn bằng cách chuyển config
xuống lớp thấp nhất thay vì kiểm tra nó ở trên cùng. FP không ngăn cản bạn làm điều này, nhưng nó có xu hướng làm cho nó khó chịu hơn rất nhiều nếu bạn cố gắng.