Có một lý do tốt để làm cho các chức năng thuần túy không công khai?


25

Tôi đã có một cuộc tranh luận nhỏ với một đồng nghiệp. Nói một cách đơn giản, có lý do chính đáng để ẩn / đóng gói các hàm thuần túy không?

Theo "thuần" tôi có nghĩa là định nghĩa wikipedia :

  • Luôn trả về cùng kết quả từ cùng một đầu vào. (Vì lợi ích của cuộc thảo luận Foo Create(){ return new Foo(); }này được coi là không trong sạch nếu Fookhông có ngữ nghĩa giá trị.)
  • Không sử dụng trạng thái có thể thay đổi (ngoại trừ các biến cục bộ) hoặc I / O.
  • Không tạo ra tác dụng phụ.

26
Có thể có một đối số được đưa ra để không biến thành một thành viên chức năng như vậy của một lớp.
Pieter B

6
@PieterB - thực sự, mặc dù không phải tất cả các ngôn ngữ đều hỗ trợ điều đó và một số ngôn ngữ cho phép mô-đun bên trong ngay cả đối với các chức năng miễn phí.
Telastyn

3
Ý kiến ​​của bạn là gì? Tôi không nghĩ rằng độ tinh khiết của hàm có liên quan đến việc nó có thuộc API công khai hay không.
Andres F.

@andresF. - cuộc tranh luận thực sự xoay quanh việc liệu quy tắc xác thực không ràng buộc có nên được công khai hay không. Tôi đã đưa ra lập luận rằng vì nó là một hàm thuần túy, nên có rất ít tác hại. Trong trường hợp cụ thể này, nó hỗ trợ khả năng kiểm tra và có khả năng được sử dụng lại. Câu hỏi này là về cách rộng rãi mà đối số có thể / nên áp dụng.
Telastyn

2
@Telastyn Nếu nó có thể tái sử dụng nhưng không thuộc trách nhiệm của lớp hiện tại, thì nó có thể nằm trong một lớp / mô-đun riêng biệt / bất cứ thứ gì-ngôn ngữ của bạn có. Sau đó, là một phần của trách nhiệm của lớp / mô-đun mới, nó nhất thiết phải được công khai. Vì bạn đề cập đến thử nghiệm, tôi sẽ lưu ý rằng bạn không nhất thiết phải chế giễu phương pháp khi bạn làm điều đó. Là một "chi tiết thực hiện", chế nhạo nó trong các thử nghiệm mang lại rất ít lợi ích.
jpmc26

Câu trả lời:


76

Một chức năng thuần túy vẫn có thể là một chi tiết thực hiện. Mặc dù chức năng có thể không gây hại (từ quan điểm không phá vỡ các bất biến / hợp đồng quan trọng), bằng cách làm cho cả tác giả và người dùng của lớp / mô-đun / gói đó bị mất. Tác giả thua vì bây giờ anh ta không thể xóa nó ngay cả khi việc triển khai thay đổi và chức năng không còn hữu ích với anh ta nữa. Người dùng mất vì họ phải chọn lọc và bỏ qua các chức năng bổ sung không liên quan đến việc sử dụng API để hiểu nó.


33
+1. Các thành viên công khai trong lớp của bạn là API của nó. Đây không phải là câu hỏi "Nó có thể làm tổn thương tôi nếu nó công khai không?", Mà là "Nó có nên là một phần của API không?"
Ordous

1
Điều duy nhất tôi muốn nói thêm là nếu bạn bắt đầu thấy các hàm tương tự xuất hiện ở các vị trí khác, hãy cấu trúc lại nó thành một lớp khác để nhiều lớp có thể sử dụng nó. Hoặc nếu đó là một mối quan tâm để bắt đầu, hãy xác định nó trong một lớp riêng để bắt đầu.
jpmc26

1
Tôi sẽ niêm phong các lớp học công cộng không có nghĩa là được mở rộng là tốt.
Den

+1 để chỉ ra rằng việc ẩn hoặc hiển thị một hàm (hoặc lớp) chủ yếu là các câu hỏi về bảo vệ và duy trì API và không liên quan đến loại mã.
Marco

42

Câu hỏi ngược.

Bạn không tìm kiếm một lý do để làm cho một chức năng không công khai. Đó là một suy nghĩ không chính xác để bắt đầu (theo ý kiến ​​của tôi). Lý luận nên đi theo con đường khác.

Nói cách khác - đừng hỏi "tại sao tôi lại đặt nó ở chế độ riêng tư?". Hỏi: "tại sao tôi sẽ công khai nó?"

Khi nghi ngờ, đừng phơi bày nó. Nó giống như dao cạo của Ockham - không nhân lên nhiều quyền lợi cần thiết.

EDIT: Giải quyết các phản biện được đưa ra bởi @Telastyn trong các bình luận (để tránh thảo luận mở rộng ở đó):

Tôi đã nghe điều đó theo thời gian và thậm chí đã tán thành nó khá lâu, nhưng theo kinh nghiệm của tôi, mọi thứ có xu hướng quá riêng tư.

Có, đôi khi thật đau đớn nếu một lớp được mở để thừa kế, nhưng bạn không thể ghi đè một số phương thức riêng tư (hành vi mà bạn muốn thay đổi).

Nhưng protectedsẽ đủ - và nó vẫn không công khai.

Nó dẫn đến rất nhiều sự trùng lặp mã và chi phí để có được "những thứ không nên công khai" dù sao cũng được truy cập gián tiếp.

Nếu nó trở thành vấn đề, sau đó chỉ cần làm cho nó công khai! Có sự cần thiết tôi đã nói về :)

Quan điểm của tôi là bạn không nên làm điều đó trong trường hợp (YAGNI và tất cả).

Lưu ý rằng việc đặt chức năng riêng tư luôn dễ dàng hơn là kéo nó trở lại quyền riêng tư. Cái sau có khả năng phá vỡ mã hiện có.


Tôi đã nghe điều đó theo thời gian và thậm chí đã tán thành nó khá lâu, nhưng theo kinh nghiệm của tôi, mọi thứ có xu hướng quá riêng tư. Nó dẫn đến rất nhiều sự trùng lặp mã và chi phí để có được "những thứ không nên công khai" dù sao cũng được truy cập gián tiếp.
Telastyn

3
@Telastyn IMHO, nghe có vẻ như ứng dụng bị một số lỗi thiết kế; nếu bạn thấy mình có xu hướng phơi bày một phương thức vì bạn cần phải gọi nó qua các đường dẫn mã rời rạc, thì đó có thể là dấu hiệu cho thấy phương thức này nên được chia thành mô-đun riêng có thể được đưa vào hoặc đưa vào khi thích hợp, đặc biệt nếu đó là một hàm thuần túy .
leo

@leo - xin lỗi, tôi không làm theo. Hãy xem xét một hàm như số nguyên nhỏ hơn. Đây là một chức năng thuần túy tốt đẹp được phơi bày cụ thể vì sau đó nó có thể được tiêm và bao gồm (hoặc đơn giản được sử dụng) khi thích hợp.
Telastyn

5
@Telastyn Theo tôi đó là một triệu chứng của triết lý "mọi thứ là một đối tượng / lớp" kết hợp với thực tế là một số ngôn ngữ OOP không có kiểu dữ liệu tổng hợp ngoài các lớp. Ngay khi ai đó cần vượt qua hai giá trị cùng một lúc họ sẽ viết một lớp, và sau đó mọi đồng nghiệp và phần mềm kiểm tra kiểu sẽ bảo bạn đặt các trường đó ở chế độ riêng tư.
Doval

@Telastyn Phải. Ý tôi là, nếu phương thức 'integLessThan' của bạn bắt đầu như một chi tiết triển khai được đóng gói của một phương thức công khai, nhưng bạn thấy rằng bạn cần phải gọi phương thức riêng ở những nơi khác, đó có thể là dấu hiệu cho thấy phương thức riêng tư thuộc về một phương thức khác mô-đun / gói / lớp để nó có thể được nhập độc lập với logic gọi nó. Chỉ đơn giản là công khai phương thức như có nghĩa là phương thức được đặt tùy ý trong mô-đun đầu tiên nơi bạn thấy nó hữu ích.
leo

6

Tôi không nghĩ rằng quyết định ẩn / đóng gói một chức năng nên phụ thuộc vào độ tinh khiết của nó. Chỉ vì một chức năng là thuần túy không có nghĩa là bên ngoài cần biết về nó. Thật thú vị mặc dù nếu chức năng này là thuần túy và có nghĩa là công khai thì có lẽ nó thậm chí không cần phải là một thành viên của giao diện, có lẽ nó phù hợp hơn như là một tĩnh. Nhưng một lần nữa tất cả những điều này phụ thuộc vào mục đích của hợp đồng và trong trường hợp này là nhóm hợp lý của chức năng, chứ không phải độ tinh khiết của chức năng.


5

Các lớp học phải tuân thủ Nguyên tắc Trách nhiệm duy nhất . Mặc dù một lớp có thể cần phải gọi các chức năng khác để đạt được các mục tiêu của mình, nhưng nó chỉ nên phơi bày các chức năng là một phần của trách nhiệm duy nhất của nó.

Đây chỉ là một ví dụ về trường hợp khả năng hiển thị có thể gây ra vấn đề.

Hãy xem xét một lớp mà frobnicates widget. Có thể là một phần của mã frobnication của nó, nó cần một số hàm tiện ích để phân tích một chuỗi: có lẽ nó cần phải chuyển đổi tên widget theo cách mà các hàm chuỗi tiêu chuẩn không hỗ trợ.

Vì đây là một hàm thuần túy (chuỗi đi vào, chuyển đổi nó bằng cách nào đó, trả về một chuỗi mới), nó có thể là công khai hoặc riêng tư mà không có hậu quả. Hay nó có thể?

Nếu bạn đặt nó ở chế độ công khai, bây giờ lớp của bạn có hai trách nhiệm: các tiện ích frobnicating chuyển đổi chuỗi. Điều này vi phạm SRP và có thể gây ra vấn đề nếu các lớp khác dựa vào chức năng. Vì đây là thứ bạn nghĩ chỉ được sử dụng nội bộ cho lớp, nên có lẽ bạn thay đổi giao diện hoặc mức độ hiển thị của nó. Bây giờ các lớp trong các phần khác của hệ thống bị hỏng.

Bằng cách giữ chức năng riêng tư, không ai có cơ hội dựa vào mã không thuộc trách nhiệm duy nhất của lớp.


2
Tôi sẽ lập luận rằng nó chỉ nên chứa các chức năng là một phần của trách nhiệm duy nhất của nó, không chỉ phơi bày.
Telastyn

1
Tôi nghĩ rằng có một khu vực màu xám. Bạn có thực sự cần một StringTransformerlớp riêng để gói hai hoặc ba dòng mã chỉ được sử dụng ở một nơi không? Tôi đồng ý rằng một khi mã được sử dụng ở nhiều nơi, tốt nhất là tách nó thành một lớp mới với một trách nhiệm, nhưng có một sự đánh đổi.

Chắc chắn rồi. Hướng dẫn là hướng dẫn, không phải quy tắc.
Telastyn

@snowman trong java không bạn chỉ cần tạo một thư viện các hàm.
Pieter B

@PieterB không phải là về Java v. Bất cứ điều gì khác. Để làm cho nó thực sự OO, bạn sẽ cần một nhà máy, một số lớp trừu tượng, v.v.
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.