Tại sao không nên có thuộc tính chỉ thiết lập?


9

Hôm nay tại nơi làm việc, một trong những đồng nghiệp của tôi đã xem lại mã của tôi và đề nghị tôi xóa một thuộc tính chỉ thiết lập và sử dụng một phương thức thay thế.

Khi cả hai chúng tôi đang bận rộn với những thứ khác, anh ấy bảo tôi xem Property Designphần từ cuốn sách "Nguyên tắc thiết kế khung". Trong cuốn sách người viết chỉ nói tránh:

Các thuộc tính với setter có khả năng truy cập rộng hơn getter

Và bây giờ tôi đang tự hỏi tại sao không nên có tài sản chỉ thiết lập? Ai đó có thể làm rõ cho tôi?


6
Bạn có thể mô tả tình huống mà bạn nghĩ rằng một thuộc tính chỉ thiết lập là phù hợp không? Nó có thể làm cho câu trả lời phù hợp hơn một chút.
JohnFx

1
Tôi đang cố gắng nghĩ về một ví dụ có ý nghĩa về mặt ngữ nghĩa. Điều duy nhất nảy ra trong đầu là một Passwordtài sản trên một Userlớp. Bạn có thể đặt nó, nhưng bạn không thể lấy nó. Sau đó bạn có thể có một HashedPasswordtài sản chỉ đọc . Gọi bộ sẽ làm băm và thay đổi thuộc HashedPasswordtính. Tôi sẽ không mắng bạn nếu bạn làm điều đó.
Scott Whitlock

Câu trả lời:


15

Tôi nghĩ rằng nó có thể phải làm với kỳ vọng. Các thuộc tính chỉ thiết lập là không phổ biến và các thuộc tính thường được sử dụng cho các bộ "câm" chỉ để lưu trữ một giá trị mà không cần xử lý nhiều. Nếu bạn đang thực hiện nhiều công việc trong một setter, tốt hơn là sử dụng một phương thức - mọi người mong đợi các phương thức có khả năng mất nhiều thời gian để thực thi và có khả năng có tác dụng phụ. Việc thực hiện loại hành vi tương tự trong một tài sản có thể dẫn đến mã vi phạm các kỳ vọng.

Đây là phần có liên quan của Nguyên tắc sử dụng tài sản của Microsoft :

Thuộc tính so với phương thức

Các nhà thiết kế thư viện lớp thường phải quyết định giữa việc thực hiện một thành viên lớp như một thuộc tính hoặc một phương thức. Nói chung, các phương thức đại diện cho các hành động và các thuộc tính đại diện cho dữ liệu. Sử dụng các hướng dẫn sau đây để giúp bạn lựa chọn giữa các tùy chọn này.

  • Sử dụng một thuộc tính khi thành viên là thành viên dữ liệu logic. Trong các khai báo thành viên sau đây, Namelà một thuộc tính vì nó là thành viên logic của lớp.
public string Name
{
    get 
    {
        return name;
    }
    set 
    {
        name = value;
    }
}

Sử dụng phương pháp khi:

  • Các hoạt động là một chuyển đổi, chẳng hạn như Object.ToString.
  • Các hoạt động đủ tốn kém mà bạn muốn giao tiếp với người dùng mà họ nên xem xét lưu trữ kết quả.
  • Có được một giá trị tài sản bằng cách sử dụng bộ truy cập getsẽ có tác dụng phụ có thể quan sát được.
  • Gọi thành viên hai lần liên tiếp tạo ra kết quả khác nhau.
  • Trình tự thực hiện là quan trọng. Lưu ý rằng các thuộc tính của một loại sẽ có thể được thiết lập và truy xuất theo bất kỳ thứ tự nào.
  • Thành viên là tĩnh nhưng trả về một giá trị có thể thay đổi.
  • Các thành viên trả về một mảng. Các thuộc tính trả về mảng có thể rất sai lệch. Thông thường cần phải trả về một bản sao của mảng bên trong để người dùng không thể thay đổi trạng thái bên trong. Điều này, cùng với thực tế là người dùng có thể dễ dàng cho rằng đó là một thuộc tính được lập chỉ mục, dẫn đến mã không hiệu quả. Trong ví dụ mã sau, mỗi lệnh gọi thuộc tính Phương thức tạo một bản sao của mảng. Kết quả là, 2 ^ n + 1 bản sao của mảng sẽ được tạo trong vòng lặp sau.
Type type = // Get a type.
for (int i = 0; i < type.Methods.Length; i++)
{
   if (type.Methods[i].Name.Equals ("text"))
   {
      // Perform some operation.
   }
}

[... bỏ qua ví dụ dài hơn ...]

Thuộc tính chỉ đọc và chỉ ghi

Bạn nên sử dụng thuộc tính chỉ đọc khi người dùng không thể thay đổi thành viên dữ liệu lôgic của thuộc tính. Không sử dụng thuộc tính chỉ ghi.


Có - nguyên tắc ít bất ngờ nhất hoạt động ở đây.
Paul Butcher

6

Bởi vì nó đơn giản là không có ý nghĩa trong hầu hết các trường hợp. Tài sản nào bạn có thể có mà bạn có thể đặt nhưng không đọc được?

Nếu OO có nghĩa là đại diện tốt hơn cho thế giới thực, một thuộc tính chỉ được thiết lập có khả năng gợi ý rằng mô hình của bạn khá đẹp.

Chỉnh sửa : Xem thêm: /programming/4564928/are-set-only-properies-bad-practice về cơ bản nói rằng nó không trực quan và thuộc tính chỉ thiết lập về cơ bản là một phương thức theo tên khác vì vậy bạn nên sử dụng một phương pháp.


1
Tôi đã sử dụng các thuộc tính chỉ thiết lập trước đó. Họ viết vào một trường riêng của đối tượng để cấu hình hành vi của nó. Chúng rất hữu ích khi mã bên ngoài không cần biết giá trị hiện tại, nhưng có thể cần thay đổi nó. Điều đó hiếm, tất nhiên, nhưng tôi đã thấy nó xảy ra.
Mason Wheeler

@Mason - Tôi chắc chắn sẽ không bao giờ đi xa như nói rằng bạn không bao giờ nên sử dụng chúng nhưng về cơ bản chúng nên là ngoại lệ chứ không phải là quy tắc.
Jon Hopkins

@MasonWheeler không phải là khoảng Foo Foo { private get; set; }? Tôi sẽ không gọi nó chỉ viết
Caleth

6

Chà, tôi tưởng tượng rằng nếu bạn có thể đặt thuộc tính trên một thứ gì đó nhưng không bao giờ có được nó, bạn sẽ không bao giờ biết nếu có thứ gì khác thay đổi / ghi đè lên giá trị mà bạn đặt. Đó có thể là một vấn đề nếu bạn dựa vào giá trị mà bạn đã đặt và bạn không thể (vì một số lý do) để duy trì nó cho đến khi bạn muốn có được nó.

Sử dụng một phương thức thay vì thuộc tính chỉ thiết lập sẽ gây khó hiểu hơn một chút cho người dùng. Các tên của phương pháp này thường chỉ ra set- hoặc GET , nhưng tên thuộc tính không bình thường cho thấy một cái gì đó mà có thể chỉ được thiết lập và không được nhận. Tôi cho rằng nếu tài sản là một cái gì đó như "ReadOnlyBackgroundColour" thì nó sẽ không gây nhầm lẫn cho các lập trình viên khác, nhưng điều đó sẽ trông thật kỳ lạ.


Tôi đồng ý, nhưng làm thế nào một phương thức setter sẽ khác đi trong trường hợp này?
Travis Christian

1
@Travis Christian: Có vẻ như OP đang làm việc với một tình huống có setter nhưng không có getter. Vì vậy, họ có thể thiết lập một cái gì đó, nhưng họ sẽ không bao giờ biết nếu nó thay đổi sau này.
Thất vọngWithFormsDesigner

@Frustrated Nhưng họ cũng sẽ không bao giờ biết nếu một cái gì đó được thay đổi một lần nữa bằng một phương pháp.
Adam Lear

@Anna Lear ♦: Nếu có một getter, ít nhất bạn có thể kiểm tra giá trị trước khi sử dụng nếu bạn có một điểm trong mã của mình, nơi giá trị bạn đã đặt có thể bị nghi ngờ.
Thất vọngWithFormsDesigner

3
@Frustrated Tôi đồng ý. Chỉ là câu hỏi là về việc sử dụng một thuộc tính chỉ thiết lập so với sử dụng một phương thức để làm điều tương tự.
Adam Lear

-1

Đây là một chủ đề rất cũ nhưng một chủ đề đã nhảy vào tầm nhìn của tôi ở giai đoạn cuối này và tôi muốn có một số ý kiến ​​khi tôi cố gắng tạo ra một trường hợp cho các thuộc tính chỉ viết ...

Tôi có một tập hợp các ActiveReportlớp là một phần của trang web được khởi tạo và chạy trên postback sau một số lựa chọn của người dùng.

Mã VB trông giống như thế này:

  Public Class SomeReport
    Private greader As New GenericReporting.CommonReader("AStoredProcedure", 
                                      {New SqlParameter("budget_id", 0)})

    Public WriteOnly Property BudgetID As Integer
      Set(value As Integer)
        greader.Parameters("budget_id").Value = value
      End Set
    End Property

    Public Sub New(Optional budget_id As Integer = 0)
      ' This call is required by the designer.
      InitializeComponent()

      ' Add any initialization after the InitializeComponent() call.
      BudgetID = budget_id
    End Sub
  End Class

Các báo cáo này sử dụng can đảm chung, CommonReaderthực hiện một quy trình được lưu trữ và một mảng SqlParameters mặc định , mỗi trong số đó có thuộc tính WriteOnly liên quan, tùy thuộc vào thiết kế báo cáo, có thể được truyền vào dưới dạng tham số trên khởi tạo hoặc được đặt bởi người dùng sau khi khởi tạo trước gọi Runphương thức báo cáo .

  '''''''''''''''''''''''
  ' Parameter taken from a user selected row of a GridView
  '
  Dim SomeBudgetID As Integer = gvBudgets.SelectedDataKey.Values(budget_id)

  '''''''''''''''''''''''      
  ' On Instantiation
  '
  Dim R as ActiveReport = New SomeReport(SomeBudgetID)
  R.Run()

  '''''''''''''''''''''''      
  ' Or On Instantiation using "With" syntax
  '
  Dim R as ActiveReport = New SomeReport() With {.BudgetID = SomeBudgetID}
  R.Run()

  '''''''''''''''''''''''
  ' Or After
  '
  Dim R as ActiveReport = New SomeReport()
  R.BudgetID = SomeBudgetID
  R.Run()

Vì vậy, như tôi thấy, có tài sản chỉ ghi trong trường hợp này

  1. Cho phép kiểm tra loại mạnh hơn vì SqlParameters là loại chung
  2. Linh hoạt hơn trong việc tạo báo cáo, báo cáo có thể được khởi tạo ngay lập tức nếu tất cả các tham số có sẵn hoặc được thêm vào sau khi chúng có sẵn.
  3. Thuộc tính hỗ trợ cú pháp "With" khi khởi tạo
  4. Là một "getter" thực sự cần thiết vì người dùng đã biết các thông số và không bị thay đổi bởi Báo cáo?
  5. SqlParameters là một lớp và không phải là giá trị nguyên thủy, WriteOnly Properties cho phép giao diện đơn giản hơn để đặt tham số

Đó là suy nghĩ của tôi.

Tôi có thể chuyển đổi nó thành một phương thức thay thế? chắc chắn nhưng giao diện có vẻ ... kém đẹp

  R2.BudgetID = SomeBudgetID

đấu với

  R2.SetBudgetID(SomeBudgetID)
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.