เหตุใดจึงไม่แนะนำให้มีคุณสมบัติแบบเซ็ตเท่านั้น


9

วันนี้ที่ทำงานเพื่อนร่วมงานคนหนึ่งของฉันตรวจสอบรหัสของฉันและแนะนำให้ฉันลบคุณสมบัติที่กำหนดเท่านั้นและใช้วิธีการแทน

เมื่อเราทั้งคู่กำลังยุ่งกับสิ่งอื่นเขาบอกให้ฉันดูProperty Designหัวข้อจากหนังสือ "แนวทางการออกแบบกรอบงาน" ในหนังสือเล่มนี้ผู้เขียนพูดเพียงเพื่อหลีกเลี่ยง:

คุณสมบัติที่มี setter ที่เข้าถึงได้กว้างกว่า getter

และตอนนี้ฉันสงสัยว่าทำไมมันไม่แนะนำให้มีคุณสมบัติเป็นชุดเท่านั้น? บางคนสามารถอธิบายให้ฉันได้ไหม


6
คุณช่วยอธิบายสถานการณ์ที่คุณคิดว่าทรัพย์สินชุดเดียวเหมาะสมหรือไม่? มันอาจทำให้คำตอบมีความเกี่ยวข้องเพิ่มขึ้นอีกเล็กน้อย
JohnFx

1
ฉันพยายามคิดถึงตัวอย่างที่ทำให้รู้สึกถึงความหมาย สิ่งเดียวที่อยู่ในใจคือPasswordทรัพย์สินในUserชั้นเรียน คุณสามารถตั้งค่า แต่คุณไม่สามารถรับได้ จากนั้นคุณสามารถมีHashedPasswordคุณสมบัติแบบอ่านอย่างเดียว การเรียกชุดจะทำการแฮชและเปลี่ยนHashedPasswordคุณสมบัติ ฉันจะไม่ตะโกนใส่คุณถ้าคุณทำอย่างนั้น
Scott Whitlock

คำตอบ:


15

ฉันคิดว่ามันอาจเกี่ยวข้องกับความคาดหวัง คุณสมบัติ Set-only เป็นเรื่องแปลกและโดยทั่วไปคุณสมบัติจะใช้สำหรับ "dumb" ตั้งค่าเพื่อเก็บค่าโดยไม่ต้องประมวลผลมาก หากคุณทำงานหนักในเซ็ตเตอร์มันจะดีกว่าถ้าใช้วิธีการ - คนคาดว่าวิธีการอาจใช้เวลานานในการดำเนินการและอาจมีผลข้างเคียง การใช้พฤติกรรมที่คล้ายกันในคุณสมบัติอาจส่งผลให้เกิดรหัสที่ละเมิดความคาดหวัง

นี่คือส่วนที่เกี่ยวข้องของหลักเกณฑ์การใช้ทรัพย์สินของ Microsoft :

คุณสมบัติเทียบกับวิธีการ

ผู้ออกแบบไลบรารีคลาสต้องตัดสินใจระหว่างการนำสมาชิกคลาสมาเป็นคุณสมบัติหรือวิธีการ โดยทั่วไปวิธีการแสดงถึงการกระทำและคุณสมบัติเป็นตัวแทนของข้อมูล ใช้แนวทางต่อไปนี้เพื่อช่วยคุณเลือกระหว่างตัวเลือกเหล่านี้

  • ใช้คุณสมบัติเมื่อสมาชิกเป็นสมาชิกข้อมูลเชิงตรรกะ ในการประกาศสมาชิกต่อไปนี้Nameเป็นทรัพย์สินเพราะมันเป็นสมาชิกตรรกะของชั้นเรียน
public string Name
{
    get 
    {
        return name;
    }
    set 
    {
        name = value;
    }
}

ใช้วิธีการเมื่อ:

  • Object.ToStringการดำเนินการเป็นแปลงเช่น
  • การดำเนินการมีราคาแพงพอที่คุณต้องการสื่อสารกับผู้ใช้ว่าพวกเขาควรพิจารณาแคชผลลัพธ์
  • การรับค่าคุณสมบัติโดยใช้getaccessor จะมีผลข้างเคียงที่สังเกตได้
  • การเรียกสมาชิกสองครั้งติดต่อกันจะให้ผลลัพธ์ที่ต่างกัน
  • ลำดับของการดำเนินการเป็นสิ่งสำคัญ โปรดทราบว่าคุณสมบัติของประเภทควรจะสามารถตั้งค่าและดึงข้อมูลในลำดับใดก็ได้
  • สมาชิกเป็นแบบคงที่ แต่คืนค่าที่สามารถเปลี่ยนแปลงได้
  • สมาชิกส่งคืนอาร์เรย์ คุณสมบัติที่ส่งคืนอาร์เรย์อาจทำให้เข้าใจผิดมาก โดยปกติจำเป็นต้องส่งคืนสำเนาของอาร์เรย์ภายในเพื่อให้ผู้ใช้ไม่สามารถเปลี่ยนสถานะภายในได้ สิ่งนี้ประกอบกับความจริงที่ว่าผู้ใช้สามารถสันนิษฐานได้ว่ามันเป็นคุณสมบัติที่มีการจัดทำดัชนีทำให้เกิดโค้ดที่ไม่มีประสิทธิภาพ ในตัวอย่างโค้ดต่อไปนี้การเรียกไปยังคุณสมบัติวิธีการแต่ละครั้งจะสร้างสำเนาของอาร์เรย์ ดังนั้นจะมีการสร้างอาร์เรย์ 2 ^ n + 1 ชุดในลูปต่อไปนี้
Type type = // Get a type.
for (int i = 0; i < type.Methods.Length; i++)
{
   if (type.Methods[i].Name.Equals ("text"))
   {
      // Perform some operation.
   }
}

[... ข้ามตัวอย่างที่ยาวกว่า ... ]

คุณสมบัติอ่านอย่างเดียวและเขียนอย่างเดียว

คุณควรใช้คุณสมบัติอ่านอย่างเดียวเมื่อผู้ใช้ไม่สามารถเปลี่ยนสมาชิกข้อมูลโลจิคัลของคุณสมบัติได้ อย่าใช้คุณสมบัติเขียนอย่างเดียว


ใช่ - หลักการของความประหลาดใจน้อยที่สุดทำงานที่นี่
Paul Butcher

6

เพราะมันไม่สมเหตุสมผลในกรณีส่วนใหญ่ คุณมีคุณสมบัติใดที่คุณสามารถตั้งค่า แต่ไม่อ่าน

หาก OO นั้นมีความหมายที่จะเป็นตัวแทนของโลกแห่งความเป็นจริงได้ดีขึ้นมีเพียงคุณสมบัติเดียวเท่านั้นที่มีแนวโน้มที่จะแนะนำให้คุณสร้างโมเดลขึ้นมาใหม่

แก้ไข : ดูเพิ่มเติมที่: /programming/4564928/are-set-only-properties-bad-practiceซึ่งโดยหลักแล้วบอกว่ามันใช้งานไม่ได้และคุณสมบัติการตั้งค่าเพียงอย่างเดียวนั้นเป็นวิธีการโดยใช้ชื่ออื่นดังนั้นคุณควรใช้ วิธี.


1
ฉันเคยใช้คุณสมบัติ set-only มาก่อน พวกเขาเขียนไปยังเขตข้อมูลส่วนตัวของวัตถุเพื่อกำหนดค่าพฤติกรรมของมัน มีประโยชน์เมื่อรหัสภายนอกไม่จำเป็นต้องรู้ค่าปัจจุบัน แต่อาจต้องเปลี่ยน แน่นอนว่าเป็นของหายาก แต่ฉันเคยเห็นมันเกิดขึ้น
Mason Wheeler

@ Mason - ฉันไม่เคยไปไกลเท่าที่บอกว่าคุณไม่ควรใช้มัน แต่พวกเขาควรจะเป็นข้อยกเว้นมากกว่ากฎ
Jon Hopkins

@MasonWheeler ไม่ได้ประมาณว่าFoo Foo { private get; set; }? ฉันจะไม่เรียกที่เขียนเท่านั้น
Caleth

6

ฉันคิดว่าถ้าคุณสามารถตั้งค่าคุณสมบัติในบางสิ่ง แต่ไม่เคยได้มาคุณจะไม่มีทางรู้ว่ามีอะไรเปลี่ยนแปลงหรือเขียนทับค่าที่คุณตั้งไว้ นั่นอาจเป็นปัญหาหากคุณพึ่งพาค่าที่คุณตั้งไว้และคุณไม่สามารถ (ด้วยเหตุผลบางอย่าง) คงอยู่จนกว่าจะถึงเวลาที่คุณต้องการรับ

การใช้เมธอดแทนคุณสมบัติที่ตั้งค่าอย่างเดียวจะทำให้ผู้ใช้เกิดความสับสนเล็กน้อย ชื่อของวิธีการที่มักจะหมายถึงการตั้งหรือget-แต่ชื่อคุณสมบัติไม่ปกติบ่งบอกถึงสิ่งที่สามารถเพียง แต่ได้รับการตั้งค่าและไม่ได้รับอากาศ ฉันคิดว่าสถานที่ให้บริการเป็นสิ่งที่ชอบ "ReadOnlyBackgroundColour" มันจะไม่สับสนกับ coders อื่น ๆ แต่นั่นก็จะดูแปลก ๆ


ฉันเห็นด้วย แต่วิธีการตั้งค่าจะแตกต่างกันอย่างไรในกรณีนี้
Travis Christian

1
@ Travis Christian: ดูเหมือนว่า OP จะทำงานร่วมกับสถานการณ์ที่มีหมา แต่ไม่มีใครทะเลาะกัน ดังนั้นพวกเขาจึงสามารถตั้งค่าบางอย่าง แต่พวกเขาจะไม่มีทางรู้ว่ามันจะเปลี่ยนแปลงในภายหลัง
FrustratedWithFormsDesigner

@ เฟร็ด แต่พวกเขาก็ไม่เคยรู้เลยว่ามีบางสิ่งที่เปลี่ยนแปลงอีกครั้งด้วยวิธีการเช่นกัน
Adam Lear

@Anna Lear ♦: หากมีผู้ทะเยอทะยานคุณสามารถทดสอบค่าอย่างน้อยก่อนที่จะใช้ถ้าคุณมีจุดในรหัสของคุณซึ่งค่าที่คุณตั้งไว้อาจมีข้อสงสัยอย่างฉับพลัน
FrustratedWithFormsDesigner

3
@Frustrated ฉันเห็นด้วย เป็นเพียงคำถามที่เกี่ยวกับการใช้คุณสมบัติ set-only vs การใช้วิธีการทำสิ่งเดียวกัน
Adam Lear

-1

นี่เป็นหัวข้อที่เก่ามาก แต่มีสิ่งหนึ่งที่พุ่งเข้ามาในมุมมองของฉันในช่วงปลายปีนี้และฉันต้องการความคิดเห็นบางอย่างเนื่องจากฉันพยายามสร้างเคสสำหรับคุณสมบัติแบบเขียนอย่างเดียว ...

ฉันมีชุดActiveReportคลาสที่เป็นส่วนหนึ่งของเว็บไซต์ที่มีอินสแตนซ์และเรียกใช้บน postback หลังจากมีการเลือกผู้ใช้จำนวนมาก

รหัส VB ​​มีลักษณะดังนี้:

  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

รายงานเหล่านี้ใช้ความกล้าทั่วไปCommonReaderรับโพรซีเดอร์ที่เก็บไว้และอาเรย์ของค่าเริ่มต้นSqlParameterซึ่งแต่ละรายการมีคุณสมบัติ WriteOnly ที่เกี่ยวข้องซึ่งสามารถส่งผ่านเป็นพารามิเตอร์ในการสร้างอินสแตนซ์หรือตั้งค่าโดยผู้ใช้หลังจากการสร้างอินสแตนซ์ เรียกRunวิธีการรายงาน

  '''''''''''''''''''''''
  ' 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()

อย่างที่ฉันเห็นมันมีคุณสมบัติแบบเขียนอย่างเดียวในกรณีนี้

  1. ช่วยให้การตรวจสอบประเภทที่แข็งแกร่งขึ้นเป็นSqlParameterประเภททั่วไป
  2. มีความยืดหยุ่นมากขึ้นในการสร้างรายงานรายงานสามารถสร้างอินสแตนซ์ได้ทันทีหากพารามิเตอร์ทั้งหมดพร้อมใช้งานหรือเพิ่มหลังจากนั้นเมื่อพร้อมใช้งาน
  3. คุณสมบัติรองรับไวยากรณ์ "พร้อม" ในการสร้างอินสแตนซ์
  4. "getter" จำเป็นจริงๆหรือไม่เนื่องจากผู้ใช้รู้จักพารามิเตอร์และไม่ได้ทำการเปลี่ยนแปลงโดยรายงาน
  5. เนื่องจากSqlParameters เป็นคลาสและไม่ใช่ค่าดั้งเดิมคุณสมบัติ WriteOnly อนุญาตให้มีอินเตอร์เฟสที่ง่ายกว่าสำหรับการตั้งค่าพารามิเตอร์

นั่นคือความคิดของฉัน

ฉันสามารถแปลงเป็นวิธีการแทนได้หรือไม่? แน่นอน แต่ดูเหมือนว่าอินเตอร์เฟสจะไม่ค่อยดีนัก

  R2.BudgetID = SomeBudgetID

กับ

  R2.SetBudgetID(SomeBudgetID)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.