เอกสารใน OOP ควรหลีกเลี่ยงการระบุว่า“ ผู้ได้รับ” ทำการคำนวณใด ๆ หรือไม่?


39

โปรแกรม CS ของโรงเรียนของฉันหลีกเลี่ยงการกล่าวถึงการเขียนโปรแกรมเชิงวัตถุดังนั้นฉันจึงต้องอ่านด้วยตัวเองเพื่อเสริม - โดยเฉพาะการสร้างซอฟต์แวร์เชิงวัตถุโดย Bertrand Meyer

เมเยอร์ชี้ให้เห็นซ้ำ ๆ ว่าคลาสควรซ่อนข้อมูลเกี่ยวกับการนำไปปฏิบัติให้มากที่สุดเท่าที่จะทำได้ โดยเฉพาะเขาให้เหตุผลซ้ำ ๆ ว่าแอตทริบิวต์ (เช่นคุณสมบัติแบบคงที่และไม่คำนวณของคลาส) และรูทีน (คุณสมบัติของคลาสที่สอดคล้องกับการเรียกใช้ฟังก์ชัน / โพรซีเดอร์) ไม่สามารถแยกกันได้

ตัวอย่างเช่นถ้าคลาสPersonมีคุณสมบัติageเขายืนยันว่ามันเป็นไปไม่ได้ที่จะบอกจากสัญลักษณ์ไม่ว่าจะPerson.ageสอดคล้องภายในกับสิ่งที่ชอบreturn current_year - self.birth_dateหรือเรียบง่ายreturn self.ageที่self.ageได้รับการกำหนดเป็นแอตทริบิวต์คงที่ เรื่องนี้ทำให้รู้สึกถึงฉัน อย่างไรก็ตามเขายังคงเรียกร้องต่อไปนี้:

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

กล่าวคือเขาอ้างว่าแม้แต่เอกสารสำหรับชั้นเรียนควรหลีกเลี่ยงการระบุว่า "ผู้เอา" ทำการคำนวณใด ๆ

นี่ฉันไม่ทำตาม เอกสารไม่ใช่ที่เดียวที่ควรแจ้งให้ผู้ใช้ทราบถึงความแตกต่างนี้หรือไม่ ถ้าฉันต้องออกแบบฐานข้อมูลที่เต็มไปด้วยPersonวัตถุมันจะไม่สำคัญที่จะรู้ว่าPerson.ageการโทรที่มีราคาแพงหรือไม่ดังนั้นฉันจึงสามารถตัดสินใจได้ว่าจะใช้แคชบางประเภทหรือไม่ ฉันเข้าใจผิดในสิ่งที่เขาพูดหรือเขาเป็นเพียงตัวอย่างที่ยอดเยี่ยมที่สุดของปรัชญาการออกแบบ OOP หรือไม่?


1
คำถามที่น่าสนใจ ฉันถามเกี่ยวกับสิ่งที่คล้ายกันมากเมื่อเร็ว ๆ นี้: ฉันจะออกแบบอินเทอร์เฟซอย่างไรให้ชัดเจนว่าคุณสมบัติใดที่อาจเปลี่ยนแปลงค่าของพวกเขาและสิ่งใดจะคงที่ . และฉันก็ได้รับคำตอบที่ดีซึ่งชี้ไปที่เอกสารนั่นคือสิ่งที่เบอร์ทรานด์เมเยอร์โต้เถียงกัน
stakx

ฉันยังไม่ได้อ่านหนังสือ ไมเยอร์ยกตัวอย่างรูปแบบของเอกสารที่เขาแนะนำหรือไม่ ฉันคิดว่ามันยากที่จะจินตนาการว่าคุณอธิบายว่าทำงานกับภาษาใด
user16764

1
@PatrickCollins ฉันขอแนะนำให้คุณอ่าน 'การดำเนินการในอาณาจักรคำนาม' และทำความเข้าใจกับคำกริยาและคำนามที่นี่ ประการที่สอง OOP ไม่เกี่ยวกับผู้ได้รับและผู้ตั้งค่าฉันขอแนะนำ Alan Kay (ผู้ประดิษฐ์ OOP): การเขียนโปรแกรมและมาตราส่วน
AndreasScheinert

@AndreasScheinert - คุณหมายถึงสิ่งนี้หรือไม่? ฉันหัวเราะเบา ๆ ที่ "ทั้งหมดสำหรับความต้องการของเล็บเกือกม้า" แต่ดูเหมือนว่าจะพูดจาโผงผางเกี่ยวกับการเขียนโปรแกรมเชิงวัตถุ
Patrick Collins

1
@PatrickCollinsใช่สิ่งนี้: steve-yegge.blogspot.com/2006/03/… ! มันให้คะแนนบางอย่างที่ต้องไตร่ตรองอีกประเด็นคือ: คุณควรเปลี่ยนวัตถุของคุณให้เป็นโครงสร้างข้อมูลโดย (ab) โดยใช้ setters
AndreasScheinert

คำตอบ:


58

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

แต่ coder ที่ใช้คลาสของคุณไม่จำเป็นต้องรู้ว่าคุณได้ติดตั้งไว้หรือไม่:

return currentAge;

หรือ:

return getCurrentYear() - yearBorn;

ลักษณะการทำงานระหว่างสองแนวทางนั้นน้อยมากจนไม่น่าจะเกิดขึ้น coder ที่ใช้คลาสของคุณไม่ควรสนใจสิ่งที่คุณมี นั่นคือจุดของ meyer

แต่นั่นไม่ใช่กรณีตัวอย่างเช่นสมมติว่าคุณมีวิธีการขนาดบนคอนเทนเนอร์ ที่สามารถนำมาใช้:

return size;

หรือ

return end_pointer - start_pointer;

หรืออาจเป็น:

count = 0
for(Node * node = firstNode; node; node = node->next)
{
    count++
}
return count

ความแตกต่างระหว่างสองคนแรกไม่น่าจะสำคัญ แต่สุดท้ายอาจมีผลการปฏิบัติงานอย่างจริงจัง นั่นเป็นเหตุผลที่ STL เช่นบอกว่าเป็น.size() O(1)มันไม่ได้ระบุขนาดของการคำนวณอย่างชัดเจน แต่มันให้คุณสมบัติด้านประสิทธิภาพกับฉัน

ดังนั้น : ปัญหาประสิทธิภาพของเอกสาร อย่าจัดทำเอกสารรายละเอียดการใช้งาน ฉันไม่สนใจว่า std :: เรียงลำดับสิ่งของของฉันอย่างไรตราบใดที่มันทำอย่างถูกต้องและมีประสิทธิภาพ ชั้นเรียนของคุณไม่ควรบันทึกว่ามันคำนวณสิ่งต่าง ๆ อย่างไร แต่ถ้ามีบางสิ่งที่มีโปรไฟล์ประสิทธิภาพที่ไม่คาดคิดให้บันทึกไว้


4
ยิ่งกว่านั้น: ความซับซ้อนของเวลา / พื้นที่เอกสารก่อนจากนั้นให้คำอธิบายว่าทำไมฟังก์ชันจึงมีคุณสมบัติเหล่านั้น เช่น:// O(n) Traverses the entire user list.
Jon Purdy

2
= (บางสิ่งเล็กน้อยเป็น Python lenล้มเหลวในการทำเช่นนี้ ... (อย่างน้อยในบางสถานการณ์มันก็เป็นO(n)อย่างที่เราเรียนรู้ในโครงการในวิทยาลัยเมื่อฉันแนะนำให้เก็บความยาวแทนการคำนวณซ้ำทุกลูป)
Izkata

@ Izkata อยากรู้อยากเห็น คุณจำโครงสร้างอะไรได้O(n)บ้าง
Winston Ewert

@WinstonEwert แต่น่าเสียดายที่ไม่ได้ นั่นคือ 4+ ปีที่แล้วในโครงการ Data Mining และฉันจะแนะนำให้เพื่อนของฉันรู้ใจเพราะฉันได้ทำงานร่วมกับ C ในชั้นเรียนอื่น ..
Izkata

1
@ JonPurdy ฉันจะเพิ่มว่าในรหัสธุรกิจปกติมันอาจไม่เหมาะสมที่จะระบุความซับซ้อนของ O ขนาดใหญ่ ตัวอย่างเช่นการเข้าถึงฐานข้อมูล O (1) น่าจะช้ากว่าการสำรวจรายการในหน่วยความจำ O (n) ดังนั้นเอกสารที่สำคัญ แต่มีบางกรณีที่ความซับซ้อนของการจัดทำเอกสารมีความสำคัญมาก (คอลเลกชันหรือรหัสอื่น ๆ ที่มีอัลกอริธึมหนัก)
svick

16

จากมุมมองของนักวิชาการหรือ CS purists มันเป็นความล้มเหลวในการอธิบายในเอกสารอะไรเกี่ยวกับการใช้งานคุณลักษณะของ internals นั่นเป็นเพราะผู้ใช้ของชั้นเรียนไม่ควรตั้งสมมติฐานเกี่ยวกับการใช้งานภายในของชั้นเรียน หากการใช้งานมีการเปลี่ยนแปลงผู้ใช้จะไม่สังเกตเห็นว่า - คุณลักษณะนี้สร้างสิ่งที่เป็นนามธรรมและผู้ใช้ภายในควรถูกซ่อนไว้อย่างสมบูรณ์

อย่างไรก็ตามโปรแกรมในโลกแห่งความเป็นจริงส่วนใหญ่ต้องทนทุกข์ทรมานจาก"กฎการรั่วซึมของนามธรรม"ซึ่งกล่าวไว้

"abstractions ที่ไม่น่ารำคาญในระดับหนึ่งก็รั่ว"

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

ดังนั้นคำแนะนำของฉันคือ: รวมข้อมูลเกี่ยวกับการโทรที่มีราคาแพงหากคุณเขียนเอกสารสำหรับโปรแกรมในโลกแห่งความเป็นจริงและยกเว้นสำหรับโปรแกรมที่คุณเขียนเพื่อวัตถุประสงค์ทางการศึกษาของหลักสูตร CS ของคุณเท่านั้นเนื่องจากข้อควรพิจารณาด้านประสิทธิภาพใด ๆ ออกจากขอบเขตโดยเจตนา


+1 บวกมากที่สุดของเอกสารที่ได้รับการสร้างขึ้นสำหรับโปรแกรมเมอร์ถัดไปที่จะรักษาโครงการของคุณไม่ได้เป็นโปรแกรมเมอร์ถัดใช้มัน
jmoreno

12

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

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


1
+1 สำหรับการกล่าวถึงว่าเอกสารเป็นส่วนหนึ่งของสัญญาของคลาสเป็นส่วนต่อประสาน
Bart van Ingen Schenau

ฉันสนับสนุนสิ่งนี้ โดยทั่วไปแล้วพยายามลดความต้องการของผู้ได้รับผลกระทบน้อยที่สุดโดยกำหนดวิธีการที่มีพฤติกรรม
sevenforce

9

ถ้าฉันต้องออกแบบฐานข้อมูลที่เต็มไปด้วยออบเจกต์ Person มันจะไม่สำคัญเลยที่จะต้องรู้ว่า Person.age นั้นเป็นการโทรที่มีราคาแพงหรือไม่?

ใช่.

นี่คือเหตุผลที่บางครั้งฉันใช้Find()ฟังก์ชั่นเพื่อระบุว่าการโทรอาจใช้เวลาสักครู่ นี่เป็นการประชุมมากกว่าสิ่งอื่นใด เวลาที่ใช้สำหรับฟังก์ชั่นหรือแอตทริบิวต์เพื่อส่งคืนไม่ได้แตกต่างจากโปรแกรม (แม้ว่าอาจจะเป็นของผู้ใช้) แม้ว่าในหมู่โปรแกรมเมอร์จะมีความคาดหวังว่าหากมีการประกาศเป็นแอตทริบิวต์ค่าใช้จ่ายในการโทร ต่ำ.

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


4
+1: การประชุมนั้นเป็นไปในทางที่แปลก เพิ่มเติมเอกสารควรจะทำในระดับอินเตอร์เฟซ - ณ จุดที่คุณไม่ทราบวิธีการใช้ Person.Age
Telastyn

@Telastyn: ฉันไม่เคยคิดถึงเอกสารในลักษณะนี้เลย นั่นคือควรทำในระดับอินเตอร์เฟส ดูเหมือนชัดเจนในขณะนี้ +1 สำหรับความคิดเห็นที่มีค่านั้น
stakx

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

+1 สำหรับการใช้Getวิธีการต่างๆกับแอตทริบิวต์เพื่อระบุการทำงานที่มีน้ำหนักมากขึ้น ฉันเห็นโค้ดเพียงพอที่นักพัฒนาสมมติว่าคุณสมบัติเป็นเพียงตัวเข้าถึงและใช้มันหลายครั้งแทนที่จะบันทึกค่าลงในตัวแปรท้องถิ่นแล้วจึงเรียกใช้อัลกอริทึมที่ซับซ้อนมากมากกว่าหนึ่งครั้ง หากไม่มีแบบแผนที่จะไม่ใช้คุณสมบัติดังกล่าวและเอกสารไม่ได้บอกกล่าวกับความซับซ้อนฉันก็หวังว่าจะมีใครที่จะต้องรักษาความโชคดีของแอปพลิเคชัน
enzi

การประชุมนี้มาจากไหน ฉันคิดว่า Java ฉันคิดว่ามันเป็นอีกทางหนึ่ง: getวิธีการเทียบเท่ากับการเข้าถึงคุณลักษณะและไม่แพงเลย
sevenforce

3

เป็นเรื่องสำคัญที่จะต้องทราบว่าหนังสือเล่มนี้ตีพิมพ์ครั้งแรกในปี 1988 ในช่วงแรก ๆ ของ OOP คนเหล่านี้ทำงานกับภาษาเชิงวัตถุล้วนๆซึ่งมีการใช้กันอย่างแพร่หลายในปัจจุบัน ภาษา OO ที่เป็นที่นิยมมากที่สุดของเราในวันนี้ - C ++, C # & Java - มีความแตกต่างอย่างมีนัยสำคัญจากการทำงานของ OO ยุคแรก ๆ ล้วนๆ

ในภาษาเช่น C ++ & Java คุณต้องแยกความแตกต่างระหว่างการเข้าถึงแอททริบิวและการเรียกเมธอด มีโลกของความแตกต่างระหว่างเป็นและinstance.getter_method instance.getter_method()อันที่จริงได้รับคุณค่าของคุณและอีกอันหนึ่งไม่ได้

เมื่อทำงานกับภาษา OO ที่บริสุทธิ์ยิ่งขึ้นของการชักชวน Smalltalk หรือ Ruby (ซึ่งปรากฏว่าภาษาไอเฟลที่ใช้ในหนังสือเล่มนี้คือ) มันจะกลายเป็นคำแนะนำที่ถูกต้องสมบูรณ์แบบ ภาษาเหล่านี้จะเรียกวิธีการให้คุณโดยปริยาย มีความแตกต่างระหว่างไม่กลายเป็นและinstance.attributeinstance.getter_method

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


1
ประเด็นที่สำคัญมากเกี่ยวกับการพิจารณาปีที่ทำข้อเสนอแนะ Nit: Smalltalk และ Simula ย้อนกลับไปในยุค 60 และ 70 ดังนั้น 88 จึงแทบจะไม่ "ยุคแรก"
luser droog

2

ในฐานะผู้ใช้คุณไม่จำเป็นต้องรู้ว่าจะมีการใช้งานอย่างไร

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


3
เป็นกรณีเสมอหรือไม่ว่าวิธีการคำนวณที่มีราคาแพงเป็นข้อผิดพลาด? เป็นตัวอย่างที่ไม่สำคัญสมมติว่าฉันเกี่ยวข้องกับการสรุปความยาวของอาร์เรย์ของสตริง ภายในฉันไม่รู้ว่าสายในภาษาของฉันเป็นแบบ Pascal หรือ C-style ในกรณีก่อนหน้านี้เนื่องจากสตริง "รู้" ความยาวของพวกเขาฉันสามารถคาดหวังความยาว - ข้อสรุปของฉัน - ห่วงที่จะใช้เวลาเชิงเส้นขึ้นอยู่กับจำนวนของสตริง ฉันควรทราบด้วยว่าการดำเนินการที่เปลี่ยนความยาวของสตริงจะมีค่าใช้จ่ายที่เกี่ยวข้องเนื่องจากstring.lengthจะถูกคำนวณใหม่ทุกครั้งที่มีการเปลี่ยนแปลง
แพทริคคอลลิน

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

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

ฉันเห็น. ดังนั้นการตอบสนองของ OO ต่อ "ฉันจะบีบประสิทธิภาพพิเศษบางอย่างออกมาจากโค้ดของฉันได้อย่างไรโดยอาศัยการใช้งานที่เฉพาะเจาะจง" คือ "คุณทำไม่ได้" และการตอบสนองต่อ "รหัสของฉันช้ากว่าที่ฉันคาดไว้ทำไม" คือ "ต้องเขียนใหม่" นั่นเป็นแนวคิดที่มากหรือน้อย
Patrick Collins

2
@PatrickCollins การตอบสนอง OO นั้นขึ้นอยู่กับอินเตอร์เฟสที่ไม่ได้ใช้งาน อย่าใช้อินเทอร์เฟซที่ไม่มีการรับประกันประสิทธิภาพซึ่งเป็นส่วนหนึ่งของข้อกำหนดของอินเทอร์เฟซ (เช่นตัวอย่างของ C ++ 11 List.size ที่รับประกันว่า O (1)) ไม่ต้องการรวมถึงรายละเอียดการใช้งานในข้อกำหนดของอินเทอร์เฟซ หากรหัสของคุณช้ากว่าที่คุณต้องการจะมีคำตอบอื่นใดนอกเหนือจากที่คุณจะต้องเปลี่ยนให้เป็นเร็วขึ้น (หลังจากทำโปรไฟล์เพื่อกำหนดคอขวด)?
หิน

2

ส่วนเอกสารเชิงโปรแกรมเมอร์ใด ๆ ที่ล้มเหลวในการแจ้งโปรแกรมเมอร์เกี่ยวกับต้นทุนความซับซ้อนของรูทีน / วิธีการนั้นมีข้อบกพร่อง

  • เรากำลังมองหาวิธีการที่ปราศจากผลข้างเคียง

  • หากการดำเนินการของวิธีการที่มีการทำงานซับซ้อนเวลาและ / หรือความซับซ้อนหน่วยความจำอื่น ๆ กว่าO(1), ในสภาพแวดล้อมที่หน่วยความจำหรือเวลาที่ จำกัด ก็สามารถได้รับการพิจารณาจะมีผลข้างเคียง

  • หลักการของความประหลาดใจไม่น้อยกว่าการละเมิดถ้าวิธีการทำอะไรบางอย่างที่ไม่คาดคิดได้อย่างสมบูรณ์ - ในกรณีนี้กินเวลาของหน่วยความจำหรือ CPU เสียเวลา


1

ฉันคิดว่าคุณเข้าใจเขาอย่างถูกต้อง แต่ฉันก็คิดว่าคุณมีจุดที่ดี หากPerson.ageมีการดำเนินการกับการคำนวณที่มีราคาแพงฉันคิดว่าฉันต้องการเห็นสิ่งนั้นในเอกสารประกอบด้วย มันสามารถสร้างความแตกต่างระหว่างการเรียกมันซ้ำ ๆ (ถ้าเป็นการดำเนินการที่ไม่แพง) หรือเรียกมันครั้งเดียวและแคชค่า (ถ้ามันแพง) ฉันไม่รู้แน่ชัด แต่ฉันคิดว่าในกรณีนี้เมเยอร์อาจเห็นด้วยว่าควรมีคำเตือนในเอกสารประกอบ

อีกวิธีหนึ่งในการจัดการสิ่งนี้อาจเป็นการแนะนำแอตทริบิวต์ใหม่ที่มีชื่อบอกเป็นนัยว่าการคำนวณที่มีความยาวอาจเกิดขึ้น (เช่นPerson.ageCalculatedFromDB) จากนั้นPerson.ageส่งคืนค่าที่แคชไว้ในคลาส แต่อาจไม่เหมาะสมเสมอไปและดูเหมือนจะซับซ้อนเกินไป สิ่งต่าง ๆ ในความคิดของฉัน


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

1
@Blrfl: ใช่แล้วการแคชควรจะทำในPersonชั้นเรียน แต่ฉันคิดว่าคำถามนั้นมีจุดประสงค์ที่กว้างกว่าและPerson.ageเป็นเพียงตัวอย่างเท่านั้น อาจมีบางกรณีที่ผู้โทรสามารถเลือกได้ - ผู้รับอาจมีอัลกอริธึมที่ต่างกันสองตัวในการคำนวณค่าเดียวกัน: หนึ่งเร็ว แต่ไม่ถูกต้องหนึ่งช้ากว่า แต่แม่นยำกว่า (การเรนเดอร์ 3 มิติมาเป็นหนึ่งเดียว สถานที่ที่อาจเกิดขึ้น) และเอกสารควรกล่าวถึงสิ่งนี้
FrustratedWithFormsDesigner

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

0

เอกสารสำหรับคลาส Object-oriented มักจะเกี่ยวข้องกับการแลกเปลี่ยนระหว่างการให้ผู้ดูแลความยืดหยุ่นของคลาสเปลี่ยนการออกแบบเมื่อเทียบกับการอนุญาตให้ผู้บริโภคของคลาสใช้ประโยชน์จากศักยภาพอย่างเต็มที่ ถ้าชั้นไม่เปลี่ยนรูปจะมีจำนวนของคุณสมบัติที่จะมีบางอย่างที่แน่นอนสัมพันธ์กับแต่ละอื่น ๆ (เช่นLeft, RightและWidthคุณสมบัติของสี่เหลี่ยมเรียงลำดับพิกัดจำนวนเต็มพิกัด) หนึ่งอาจออกแบบคลาสเพื่อจัดเก็บการรวมกันของสองคุณสมบัติและคำนวณที่สามหรือหนึ่งอาจออกแบบเพื่อเก็บทั้งสาม หากไม่มีอะไรเกี่ยวกับอินเตอร์เฟสทำให้ชัดเจนว่าคุณสมบัติใดถูกเก็บไว้โปรแกรมเมอร์ของคลาสอาจสามารถเปลี่ยนแปลงการออกแบบในกรณีที่ทำเช่นนั้นจะพิสูจน์ได้ว่ามีประโยชน์ด้วยเหตุผลบางประการ ในทางตรงกันข้ามถ้าเช่นคุณสมบัติสองอย่างถูกเปิดเผยเป็นfinalฟิลด์และคุณสมบัติที่สามไม่ใช่คลาสในอนาคตของคลาสจะต้องใช้คุณสมบัติทั้งสองเหมือนกันว่าเป็น "พื้นฐาน" เสมอ

หากคุณสมบัติไม่มีความสัมพันธ์ที่แน่นอน (เช่นเพราะมันเป็นfloatหรือdoubleมากกว่าint) ดังนั้นอาจจำเป็นต้องบันทึกว่าคุณสมบัติ "define" เป็นค่าของคลาสใด ตัวอย่างเช่นแม้ว่าLeftเครื่องหมายบวกWidthควรจะเท่ากันRightแต่คณิตศาสตร์จุดลอยตัวมักไม่แม่นยำ ตัวอย่างเช่นสมมติว่าRectangleซึ่งใช้ประเภทFloatยอมรับLeftและWidthเป็นพารามิเตอร์คอนสตรัคถูกสร้างด้วยLeftให้เป็น1234567fและเป็นWidth 1.1fการนำเสนอที่ดีที่สุดfloatของผลรวมคือ 1234568.125 [ซึ่งอาจแสดงเป็น 1234568.13]; ตัวที่เล็กกว่าถัดไปfloatคือ 1234568.0 ถ้าชั้นเรียนจัดเก็บจริงLeftและWidthมันอาจรายงานค่าความกว้างตามที่ระบุไว้ แต่ถ้าสร้างคำนวณRightขึ้นอยู่กับที่ผ่านมาในLeftและWidthและต่อมาคำนวณWidthอยู่บนพื้นฐานLeftและRightก็จะรายงานความกว้างเป็นมากกว่าที่จะเป็นผ่านใน1.25f1.1f

ด้วยคลาสที่ไม่สามารถเปลี่ยนแปลงได้สิ่งต่าง ๆ น่าสนใจยิ่งขึ้นเนื่องจากการเปลี่ยนแปลงหนึ่งในค่าที่เกี่ยวข้องกับอินเตอร์จะทำให้เกิดการเปลี่ยนแปลงอย่างน้อยหนึ่งค่า แต่มันอาจไม่ชัดเจนว่าจะเป็นอันใด ในบางกรณีก็อาจจะดีที่สุดเพื่อหลีกเลี่ยงวิธีการที่ "ชุด" คุณสมบัติเดียวเช่นนี้ แต่อาจมีวิธีการที่จะเช่นSetLeftAndWidthหรือSetLeftAndRightหรืออื่น ๆ ให้ชัดเจนสิ่งที่คุณสมบัติที่ถูกระบุและที่มีการเปลี่ยนแปลง (เช่นMoveRightEdgeToSetWidth, ChangeWidthToSetLeftEdgeหรือMoveShapeToSetRightEdge) .

บางครั้งมันอาจมีประโยชน์ในการมีคลาสที่คอยติดตามว่ามีการระบุค่าของคุณสมบัติใดและถูกคำนวณจากที่อื่น ตัวอย่างเช่นคลาส "โมเมนต์ในเวลา" อาจรวมถึงเวลาสัมบูรณ์เวลาท้องถิ่นและการชดเชยโซนเวลา เช่นเดียวกับหลาย ๆ ประเภทเช่นที่ได้รับข้อมูลสองชิ้นหนึ่งอาจคำนวณที่สาม รู้ที่อย่างไรก็ตามข้อมูลบางส่วนอาจมีความสำคัญ ตัวอย่างเช่นสมมติว่าเหตุการณ์ถูกบันทึกว่าเกิดขึ้นที่ "17:00 UTC, เขตเวลา -5, เวลาท้องถิ่น 12:00 pm" และอีกหนึ่งภายหลังพบว่าเขตเวลาควรได้รับ -6 หากใครรู้ว่า UTC ถูกบันทึกจากเซิร์ฟเวอร์แล้วควรจะแก้ไขเรคคอร์ดเป็น "18:00 UTC, เขตเวลา -6, เวลาท้องถิ่น 12:00 pm"; หากมีคนใส่กุญแจในเวลาท้องถิ่นควรเป็น "17:00 UTC, เขตเวลา -6, เวลาท้องถิ่น 11:00 น." โดยไม่ทราบว่าเวลาทั่วโลกหรือเวลาท้องถิ่นควรได้รับการพิจารณาว่าน่าเชื่อถือมากขึ้นอย่างไรก็ตามเป็นไปไม่ได้ที่จะทราบว่าควรใช้การแก้ไขใด อย่างไรก็ตามหากบันทึกนั้นติดตามว่ามีการระบุเวลาไว้การเปลี่ยนแปลงในเขตเวลาอาจทำให้เกิดการเปลี่ยนแปลงในขณะนั้น


0

กฎทั้งหมดเหล่านี้เกี่ยวกับวิธีการซ่อนข้อมูลในชั้นเรียนทำให้รู้สึกสมบูรณ์แบบในการสันนิษฐานว่าจำเป็นต้องป้องกันใครบางคนในหมู่ผู้ใช้ของชั้นเรียนที่จะทำผิดพลาดในการสร้างการพึ่งพาการใช้งานภายใน

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

นี่คือสิ่งที่ฉันเห็นมาก:

  1. วัตถุมีบิต "แก้ไข" ที่บอกว่าในบางกรณีล้าสมัย ง่ายพอ แต่แล้วพวกเขาก็มีวัตถุรองดังนั้นมันจึงตรงไปตรงมาที่ให้ "แก้ไข" เป็นฟังก์ชันที่รวมอยู่เหนือวัตถุรองทั้งหมด จากนั้นหากมีวัตถุรองหลายชั้น (บางครั้งการแบ่งปันวัตถุเดียวกันมากกว่าหนึ่งครั้ง) ง่ายๆ "รับ" ของคุณสมบัติ "แก้ไข" สามารถจบลงด้วยการใช้เวลาส่วนย่อยที่มีสุขภาพดี

  2. เมื่อวัตถุมีการปรับเปลี่ยนในทางใดทางหนึ่งสันนิษฐานว่าวัตถุอื่น ๆ ที่กระจัดกระจายไปทั่วซอฟต์แวร์นั้นจำเป็นต้องได้รับการ "แจ้งเตือน" สิ่งนี้สามารถเกิดขึ้นได้ในหลาย ๆ ชั้นของโครงสร้างข้อมูลหน้าต่าง ฯลฯ ที่เขียนโดยโปรแกรมเมอร์ที่แตกต่างกันและบางครั้งก็ทำซ้ำในการเรียกซ้ำแบบไม่สิ้นสุดที่จำเป็นต้องได้รับการปกป้อง แม้ว่าผู้เขียนทั้งหมดของตัวจัดการการแจ้งเตือนเหล่านั้นจะมีเหตุผลระมัดระวังไม่ให้เสียเวลาการโต้ตอบแบบรวมทั้งหมดสามารถจบลงได้ด้วยการใช้เวลาส่วนใหญ่ในการดำเนินการที่ไม่มีการคาดการณ์และเจ็บปวดอย่างมากและสันนิษฐานว่า

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

ดังนั้นไปคิด


0

เพิ่มรายละเอียดการดำเนินงานเช่น "การคำนวณหรือไม่" หรือ "ข้อมูลประสิทธิภาพการทำงาน" ทำให้มันมากขึ้นdifficuilt เพื่อให้รหัสและเอกสารในการซิงค์

ตัวอย่าง:

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

แน่นอนว่าเป็นเรื่องดีสำหรับผู้ดูแลโค้ดที่จะได้รับข่าวสารสำคัญทั้งหมดจากเอกสารรหัส แต่ฉันไม่ชอบเอกสารที่อ้างว่ามีบางสิ่งที่ไม่ถูกต้องอีกต่อไป (ไม่ซิงค์กับรหัส)


0

ในฐานะที่เป็นคำตอบที่ได้รับการยอมรับมาถึงข้อสรุป:

ดังนั้น:ปัญหาประสิทธิภาพของเอกสาร

และรหัสที่บันทึกด้วยตนเองจะถือว่าดีกว่าเอกสารอธิบายตามด้วยชื่อวิธีการที่ควรระบุผลการปฏิบัติงานที่ผิดปกติ

ยังคงPerson.ageใช้return current_year - self.birth_dateแต่ถ้าวิธีใช้การวนซ้ำเพื่อคำนวณอายุ (ใช่):Person.calculateAge()

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