Là đảm bảo tính bất biến là một biện minh cho việc phơi bày một lĩnh vực thay vì một tài sản?


10

Hướng dẫn chung cho C # là luôn sử dụng một tài sản trên một lĩnh vực công cộng. Điều này có ý nghĩa - bằng cách phơi bày một trường, bạn đang phơi bày rất nhiều chi tiết triển khai. Với một thuộc tính, bạn gói gọn chi tiết đó để nó ẩn khỏi việc tiêu thụ mã và các thay đổi triển khai được tách rời khỏi các thay đổi giao diện.

Tuy nhiên, tôi tự hỏi liệu đôi khi có một ngoại lệ hợp lệ cho quy tắc này khi xử lý readonlytừ khóa. Bằng cách áp dụng từ khóa này cho một lĩnh vực công cộng, bạn đảm bảo thêm: tính bất biến. Đây không chỉ là một chi tiết triển khai, tính bất biến là điều mà người tiêu dùng có thể quan tâm. Sử dụng một readonlylĩnh vực làm cho nó trở thành một phần của hợp đồng công cộng, và một cái gì đó không thể bị phá vỡ bởi những thay đổi hoặc kế thừa trong tương lai mà không phải sửa đổi giao diện công cộng. Đó là điều mà một tài sản không thể cung cấp.

Vì vậy, đảm bảo tính bất biến là một lý do chính đáng để chọn một readonlylĩnh vực trên một tài sản trong một số trường hợp?

(Để làm rõ, tôi chắc chắn không nói rằng bạn nên luôn luôn làm cho sự lựa chọn này chỉ vì lĩnh vực này sẽ xảy ra là bất di bất dịch vào lúc này, chỉ khi nó có ý nghĩa như là một phần của thiết kế của lớp và nó có ý định sử dụng để bao gồm bất biến trong hợp đồng của mình. Tôi chủ yếu quan tâm đến các câu trả lời tập trung vào việc liệu điều này có thể hợp lý, thay vì các trường hợp cụ thể không, chẳng hạn như khi bạn cần thành viên tham gia interfacehoặc muốn lười tải.)



1
@gnat Chỉ cần làm rõ, bằng "Tài sản ReadOnly" họ đang sử dụng thuật ngữ VB.NET có nghĩa là một thuộc tính không có setter, không phải là trường chỉ đọc. Đối với mục đích câu hỏi của tôi, hai tùy chọn mà câu hỏi so sánh là tương đương - cả hai đều sử dụng thuộc tính.
Ben Aaronson

Câu trả lời:


6

Các trường chỉ đọc tĩnh công khai chắc chắn là ổn. Chúng được khuyến nghị cho các tình huống nhất định, như khi bạn muốn một đối tượng không đổi có tên nhưng bạn không thể sử dụng consttừ khóa.

Các lĩnh vực thành viên chỉ đọc là một chút phức tạp hơn. Không có nhiều lợi thế đạt được đối với một tài sản mà không có trình thiết lập công khai và có một nhược điểm lớn: các trường không thể là thành viên của giao diện. Vì vậy, nếu bạn quyết định cấu trúc lại mã của mình để hoạt động dựa trên giao diện thay vì loại cụ thể, bạn sẽ phải thay đổi các trường chỉ đọc thành các thuộc tính bằng các biểu đồ công khai.

Một thuộc tính không ảo công khai không có bộ cài đặt được bảo vệ sẽ bảo vệ chống lại sự kế thừa, trừ khi các lớp được kế thừa có quyền truy cập vào trường sao lưu. Bạn hoàn toàn có thể thực thi hợp đồng đó trong lớp cơ sở.

Không thể thay đổi "tính bất biến" của thành viên mà không thay đổi giao diện công khai của lớp không phải là một rào cản trong trường hợp này. Thông thường nếu bạn muốn thay đổi một lĩnh vực công cộng thành một tài sản, nó sẽ diễn ra suôn sẻ, ngoại trừ có hai trường hợp bạn phải giải quyết:

  1. Bất cứ điều gì viết cho một thành viên của đối tượng đó, như: object.Location.X = 4chỉ có thể nếu Locationlà một trường. Điều này không liên quan đến trường chỉ đọc, bởi vì bạn không thể sửa đổi cấu trúc chỉ đọc. (Đây là giả sử Locationlà một loại giá trị - nếu không thì vấn đề này sẽ không được áp dụng vì dù sao readonlynó cũng không bảo vệ chống lại điều đó.)
  2. Bất kỳ cuộc gọi đến một phương thức vượt qua giá trị là outhoặc ref. Một lần nữa, điều này không thực sự phù hợp với trường chỉ đọc, bởi vì outrefcác tham số có xu hướng bị sửa đổi, và dù sao đó cũng là lỗi trình biên dịch để chuyển giá trị chỉ đọc thành out hoặc ref.

Nói cách khác, mặc dù việc chuyển đổi trường chỉ đọc sang thuộc tính chỉ đọc phá vỡ tính tương thích nhị phân, mã nguồn khác chỉ cần được biên dịch lại mà không có bất kỳ thay đổi nào để xử lý thay đổi này. Điều đó làm cho sự đảm bảo thực sự dễ dàng để phá vỡ.

Tôi không thấy bất kỳ lợi thế nào cho readonlylĩnh vực thành viên và việc không thể có thứ đó trên giao diện là đủ bất lợi cho tôi rằng tôi sẽ không sử dụng nó.


Không quan tâm, tại sao các trường chỉ đọc tĩnh công khai được ? Có phải chỉ vì các phương thức loại trừ loại bỏ hầu hết các trường hợp sử dụng cho các thuộc tính trên các trường không thay đổi (như đếm lần truy cập, tải nhanh, v.v.)?
Ben Aaronson

Nhược điểm chính của trường chỉ đọc công khai so với thuộc tính chỉ đọc đã biến mất - các thành viên tĩnh không thể là một phần của giao diện để chúng có cùng bước đi. Một thuộc tính tĩnh chỉ đọc hoặc cần lưu trữ được khởi tạo hoặc nó cần xây dựng lại giá trị trả về của nó mỗi lần nó được gọi, trong khi trường chỉ đọc sẽ có cú pháp đơn giản hơn và được khởi tạo bởi hàm tạo tĩnh. Vì vậy, tôi nghĩ rằng một trường chỉ đọc tĩnh công khai có khả năng tối ưu hóa tốt hơn bởi JIT mà không có bất kỳ nhược điểm nào mà bạn thường gặp phải khi phơi bày một trường.
Erik

2

Từ kinh nghiệm của tôi, readonlytừ khóa không phải là điều kỳ diệu mà nó hứa hẹn.

Ví dụ, bạn có một lớp trong đó hàm tạo được sử dụng đơn giản. Với cách sử dụng, (và thực tế là một số thuộc tính / trường lớp là bất biến sau khi xây dựng), bạn có thể nghĩ rằng nó không quan trọng, vì vậy bạn đã sử dụng readonlytừ khóa.

Sau đó, lớp trở nên phức tạp hơn, và hàm tạo của nó cũng vậy. (Hãy nói rằng điều này xảy ra trong một dự án mà là một trong hai thí nghiệm hoặc có đủ tốc độ cao phóng to ra khỏi phạm vi / Kiểm soát nhưng bạn cần phải làm cho nó hoạt bằng cách nào đó.) Bạn sẽ thấy rằng các lĩnh vực được readonlychỉ có thể được sửa đổi trong các nhà xây dựng - bạn không thể sửa đổi nó trong các phương thức ngay cả khi phương thức đó chỉ được gọi từ một hàm tạo.

Giới hạn kỹ thuật này đã được các nhà thiết kế của C # thừa nhận và có thể được sửa trong phiên bản tương lai. Nhưng cho đến khi nó được sửa chữa, việc sử dụng readonlybị hạn chế hơn và có thể khuyến khích một số kiểu mã hóa xấu (đặt mọi thứ vào phương thức constructor).

Xin nhắc lại, readonlythuộc tính setter không công khai cũng không cung cấp bất kỳ sự đảm bảo nào về "đối tượng khởi tạo hoàn toàn". Nói cách khác, chính người thiết kế một lớp quyết định "khởi tạo hoàn toàn" nghĩa là gì, và làm thế nào để bảo vệ nó trước việc sử dụng vô tình, và sự bảo vệ đó thường không thể đánh lừa được, có nghĩa là ai đó có thể tìm ra cách xung quanh nó.


1
Tôi muốn giữ nhà xây dựng đơn giản - xem brendan.enrick.com/post/... , blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple như vậy trong thực tế, chỉ đọc khuyến khích tốt phong cách mã hóa
AlexFoxGill

1
Một hàm tạo có thể truyền một readonlytrường dưới dạng outhoặc reftham số cho các phương thức khác, sau đó sẽ tự do sửa đổi nó khi chúng thấy phù hợp.
supercat

1

Các lĩnh vực là LUÔN chi tiết thực hiện. Bạn không muốn tiết lộ chi tiết thực hiện của bạn.

Một nguyên lý cơ bản của công nghệ phần mềm là chúng ta không biết những yêu cầu sẽ có trong tương lai. Mặc dù readonlylĩnh vực của bạn có thể tốt ngày hôm nay, không có gì cho thấy rằng nó sẽ là một phần của yêu cầu ngày mai. Điều gì sẽ xảy ra nếu ngày mai cần phải thực hiện một bộ đếm lần truy cập để xác định số lần truy cập trường đó? Điều gì nếu bạn cần cho phép các lớp con ghi đè lên trường?

Các thuộc tính rất dễ sử dụng và linh hoạt hơn nhiều so với các trường công cộng mà tôi không thể nghĩ đến một trường hợp sử dụng duy nhất trong đó tôi sẽ chọn c # làm ngôn ngữ của mình và sử dụng các trường chỉ đọc công khai trên một lớp. Nếu hiệu suất quá chặt đến mức tôi không thể thực hiện được cuộc gọi phương thức bổ sung, có lẽ tôi sẽ sử dụng một ngôn ngữ khác.

Chỉ vì chúng ta có thể làm gì đó với ngôn ngữ không có nghĩa là chúng ta nên làm gì đó với ngôn ngữ.

Tôi thực sự tự hỏi liệu các nhà thiết kế ngôn ngữ có hối tiếc khi cho phép các lĩnh vực được công khai. Tôi không thể nhớ lần cuối cùng tôi thậm chí đã cân nhắc sử dụng các trường công khai không phải là mã trong mã của mình.


1
Tôi hiểu tầm quan trọng của việc xây dựng tính linh hoạt trong tương lai, nhưng chắc chắn nếu tôi viết một lớp như, nói Rational, với các thành viên, bất biến NumeratorDenominatorthành viên, tôi có thể cực kỳ tin tưởng rằng các thành viên đó sẽ không yêu cầu tải nhanh hoặc truy cập hoặc giống?
Ben Aaronson

Nhưng bạn có thể tự tin rằng việc triển khai sử dụng floatcác loại cho NumeratorDenominatorsẽ không cần phải thay đổi thành doubles?
Stephen

1
Vâng, er, không, họ chắc chắn sẽ không phải floatvà cũng không double. Họ nên int, longhoặc BigInteger. Có thể những người đó cần thay đổi ... nhưng nếu vậy họ cũng cần thay đổi trong giao diện công cộng, vì vậy thực sự sử dụng các thuộc tính không thêm phần đóng gói xung quanh chi tiết đó.
Ben Aaronson

-3

Không. Nó không phải là một biện minh.

Sử dụng chỉ đọc như một sự đảm bảo về tính không linh hoạt trong việc không biện minh giống như một vụ hack.

Chỉ đọc có nghĩa là giá trị được gán bởi một nhà xây dựng o khi biến được xác định.

Tôi nghĩ rằng nó sẽ là một thực hành xấu

Nếu bạn thực sự muốn đảm bảo tính bất biến, hãy sử dụng một giao diện, nó có nhiều ưu điểm hơn khuyết điểm.

Ví dụ giao diện đơn giản: nhập mô tả hình ảnh ở đây


1
"Sử dụng giao diện" Làm thế nào?
Ben Aaronson
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.