Tại sao nó là một ý tưởng tồi để tạo ra một setter và getter chung với sự phản chiếu?


49

Một thời gian trước, tôi đã viết câu trả lời này cho một câu hỏi về cách tránh có một getter và setter cho mọi biến có thể thay đổi. Vào thời điểm đó, tôi chỉ có một cảm giác khó nói bằng lời rằng đây là một ý tưởng tồi, nhưng OP đã hỏi rõ ràng làm thế nào để làm điều đó. Tôi đã tìm kiếm ở đây về lý do tại sao điều này có thể là một vấn đề, và tìm thấy câu hỏi này , câu trả lời của họ dường như được coi là đưa ra rằng sử dụng sự phản chiếu trừ khi thực sự cần thiết là thực tiễn xấu.

Vì vậy, ai đó có thể xác minh bằng lời tại sao nên tránh sự phản chiếu, cụ thể là trong trường hợp của getter và setter chung không?


12
Nếu bạn chỉ muốn giảm nồi hơi, cả Lombok và Groovy sẽ tạo ra getters và setters âm thầm nhưng an toàn.
chrylis -on đình công-

2
Làm thế nào bạn thậm chí sẽ tiêu thụ những getters và setters trong một ngôn ngữ tĩnh như java? Nó có vẻ như không bắt đầu với tôi.
Esben Skov Pedersen

@EsbenSkovPedersen Bạn muốn tiêu thụ chúng giống như một Map's getput, mà là một cách khá vụng về làm việc.
HAEM

2
IIRC, Lombok tổng hợp các getters / setters tại thời điểm biên dịch, như được quy định bởi chú thích thích hợp, vì vậy chúng: không phải chịu hình phạt hiệu suất của phản xạ, có thể được phân tích tĩnh / nội tuyến, có thể được cấu trúc lại (IntelliJ có plugin cho Lombok)
Alexander

Câu trả lời:


117

Nhược điểm của sự phản ánh nói chung

Phản xạ khó hiểu hơn mã đường thẳng.

Theo kinh nghiệm của tôi, sự phản chiếu là một tính năng "cấp chuyên gia" trong Java. Tôi sẽ lập luận rằng hầu hết các lập trình viên không bao giờ sử dụng phản xạ một cách chủ động (tức là tiêu thụ các thư viện sử dụng sự phản chiếu không được tính). Điều đó làm cho mã sử dụng nó khó hiểu hơn đối với các lập trình viên này.

Mã phản chiếu không thể truy cập để phân tích tĩnh

Giả sử tôi có một getter getFootrong lớp và tôi muốn đổi tên nó thành getBar. Nếu tôi không sử dụng phản xạ, tôi chỉ có thể tìm kiếm cơ sở mã getFoovà sẽ tìm mọi nơi sử dụng getter để tôi có thể cập nhật nó, và ngay cả khi tôi bỏ lỡ một, trình biên dịch sẽ phàn nàn.

Nhưng nếu nơi sử dụng getter giống như callGetter("Foo")callGetterthực hiện getClass().getMethod("get"+name).invoke(this), thì phương thức trên sẽ không tìm thấy nó và trình biên dịch sẽ không phàn nàn. Chỉ khi mã thực sự được thực thi, bạn sẽ nhận được a NoSuchMethodException. Và hãy tưởng tượng nỗi đau mà bạn gặp phải nếu ngoại lệ đó (được theo dõi) bị nuốt bởi callGettervì "nó chỉ được sử dụng với các chuỗi mã hóa cứng, nó thực sự không thể xảy ra". (Không ai sẽ làm điều đó, ai đó có thể tranh luận? Ngoại trừ việc OP đã làm chính xác điều đó trong câu trả lời SO của anh ta. Người dùng của getter có thể, nếu họ may mắn, hãy chú ý đầu ra giao diện điều khiển của ngoại lệ bị bỏ qua.)

Mã phản chiếu không được kiểm tra kiểu bởi trình biên dịch

Đây về cơ bản là một điểm phụ lớn của ở trên. Mã phản ánh là tất cả về Object. Các loại được kiểm tra tại thời gian chạy. Lỗi được phát hiện bởi các bài kiểm tra đơn vị, nhưng chỉ khi bạn có phạm vi bảo hiểm. ("Nó chỉ là một getter, tôi không cần phải kiểm tra nó.") Về cơ bản, bạn mất lợi thế khi sử dụng Java so với Python đã giúp bạn đạt được vị trí đầu tiên.

Mã phản chiếu không có sẵn để tối ưu hóa

Có thể không phải trên lý thuyết, nhưng trong thực tế, bạn sẽ không tìm thấy một JVM nội tuyến hoặc tạo bộ đệm nội tuyến cho Method.invoke. Các cuộc gọi phương thức bình thường có sẵn để tối ưu hóa như vậy. Điều đó làm cho họ nhanh hơn rất nhiều.

Nói chung mã phản chiếu chỉ chậm

Việc tra cứu phương thức động và kiểm tra kiểu cần thiết cho mã phản chiếu chậm hơn so với các cuộc gọi phương thức thông thường. Nếu bạn biến chiếc getter một dòng giá rẻ đó thành một con thú phản chiếu, bạn có thể (tôi chưa đo được điều này) đang xem xét một số mức độ chậm lại.

Nhược điểm của getter / setter chung

Đó chỉ là một ý tưởng tồi, bởi vì lớp của bạn bây giờ không còn đóng gói nữa. Mỗi lĩnh vực nó có thể truy cập được. Bạn cũng có thể làm cho tất cả chúng công khai.


8
Bạn đạt tất cả các điểm chính. Khá nhiều một câu trả lời hoàn hảo. Tôi có thể ngụy biện rằng getters và setter tốt hơn một chút so với các lĩnh vực công cộng nhưng điểm lớn hơn là chính xác.
JimmyJames

2
Cũng đáng đề cập rằng getters / setters cũng có thể dễ dàng được tạo ra bởi một IDE.
stiemannkj1

26
+1 Logic phản ánh gỡ lỗi trong một dự án quy mô sản xuất nên được Liên Hợp Quốc công nhận là tra tấn và phi pháp hóa.
errantlinguist

4
"khó hiểu hơn cho những lập trình viên này." - đối với những lập trình viên này, điều đó thật khó hiểu, nhưng khó hiểu hơn cho mọi người, bất kể thành thạo đến mức nào.
BartoszKP

4
"Mã phản chiếu không thể truy cập để phân tích tĩnh" - Điều này. Rất quan trọng để cho phép tái cấu trúc. Và khi các công cụ phân tích tĩnh trở nên mạnh mẽ hơn (ví dụ: tìm kiếm mã trong Máy chủ nền tảng nhóm mới nhất), việc viết mã cho phép chúng trở nên có giá trị hơn.
Dan J

11

Bởi vì getter / setters truyền qua là một sự gớm ghiếc không cung cấp giá trị. Họ không cung cấp bất kỳ đóng gói nào vì người dùng có thể nhận được các giá trị thực thông qua các thuộc tính. Chúng là YAGNI vì bạn hiếm khi thay đổi việc thực hiện lưu trữ trường của mình.

bởi vì đây là cách ít hiệu quả hơn. Trong C # ít nhất, một getter / setter sẽ chuyển thẳng đến một lệnh CIL duy nhất. Trong câu trả lời của bạn, bạn đang thay thế bằng 3 lệnh gọi hàm, lần lượt cần tải siêu dữ liệu để phản ánh lại. Nói chung, tôi đã sử dụng 10 lần làm quy tắc ngón tay cái cho chi phí phản ánh, nhưng khi tôi tìm kiếm dữ liệu, một trong những câu trả lời đầu tiên bao gồm câu trả lời này khiến nó gần hơn 1000 lần.

.


2
Lưu ý rằng câu hỏi được gắn thẻ Java, có crap thú vị như khái niệm Beans. Một bean không có các trường công khai, nhưng nó có thể phơi bày các trường thông qua getX()setX()các phương thức có thể được tìm thấy thông qua sự phản chiếu. Đậu không nhất thiết phải gói gọn các giá trị của chúng, chúng có thể chỉ là DTO. Vì vậy, trong Java, getters thường là một nỗi đau cần thiết. Các thuộc tính C # rất nhiều, đẹp hơn nhiều về mọi mặt.
amon

11
Các thuộc tính C # là mặc quần áo / setters mặc dù. Nhưng vâng, khái niệm Đậu là một thảm họa.
Sebastian Redl

6
@amon Phải, C # đã lấy một ý tưởng khủng khiếp và thực hiện dễ dàng hơn.
JimmyJames

5
@JimmyJames Đó thực sự không phải là một ý tưởng tồi tệ; bạn có thể duy trì khả năng tương thích ABI mặc dù thay đổi mã. Một vấn đề nhiều người không có, tôi cho rằng.
Casey

3
@Casey Phạm vi của điều này vượt ra ngoài một nhận xét nhưng việc xây dựng một giao diện từ các chi tiết nội bộ của việc triển khai là ngược. Nó dẫn đến một thiết kế thủ tục. Đôi khi có một phương thức getX là câu trả lời đúng nhưng nó không phổ biến trong mã được thiết kế tốt. Vấn đề với getters và setters trong Java không phải là mã soạn sẵn xung quanh chúng, đó là chúng là một phần của cách tiếp cận thiết kế khủng khiếp. Làm cho điều đó dễ dàng hơn cũng giống như làm cho nó dễ dàng hơn để đấm vào mặt mình.
JimmyJames

8

Ngoài các đối số đã được trình bày.

Nó không cung cấp giao diện mong đợi.

Người dùng sẽ gọi foo.getBar () và foo.setBar (value), chứ không phải foo.get ("bar") và foo.set ("bar", value)

Để cung cấp giao diện mà người dùng mong đợi, bạn cần phải viết một getter / setter riêng biệt cho mỗi thuộc tính. Một khi bạn đã làm rằng không có điểm trong việc sử dụng sự phản chiếu.


Dường như với tôi rằng lý do này quan trọng hơn tất cả những câu trả lời khác.
Esben Skov Pedersen

-4

Tất nhiên đó không phải là một ý tưởng tồi, nó chỉ là trong một thế giới java bình thường là một ý tưởng tồi (khi bạn chỉ muốn một getter hoặc setter). Ví dụ, một số khung (spring mvc) hoặc librares đã sử dụng nó (jstl) theo cách đó, một số trình phân tích cú pháp json o xml đang sử dụng nó, nếu bạn muốn sử dụng DSL bạn sẽ sử dụng nó. Sau đó, nó phụ thuộc vào kịch bản trường hợp sử dụng của bạn.

Không có gì là đúng hay sai như một thực tế.

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.