จำนวนครั้งที่สำคัญฉันไม่สามารถคิดเหตุผลที่มีวัตถุแทนคลาสแบบสแตติก วัตถุมีประโยชน์มากกว่าที่ฉันคิดหรือไม่ [ปิด]


9

ฉันเข้าใจแนวคิดของวัตถุและในฐานะที่เป็นโปรแกรมเมอร์ Java ฉันรู้สึกว่ากระบวนทัศน์ OO นั้นค่อนข้างเป็นธรรมชาติสำหรับฉันในทางปฏิบัติ

อย่างไรก็ตามเมื่อเร็ว ๆ นี้ฉันพบว่าตัวเองกำลังคิด:

รอสักครู่สิ่งที่เป็นประโยชน์จริงของการใช้วัตถุมากกว่าการใช้คลาสคงที่ (มีการห่อหุ้มที่เหมาะสมและการปฏิบัติ OO)?

ฉันนึกถึงประโยชน์สองประการของการใช้วัตถุ (ทั้งสองอย่างมีความสำคัญและมีประสิทธิภาพ):

  1. ความแตกต่าง: ช่วยให้คุณสามารถสลับการทำงานแบบไดนามิกและยืดหยุ่นในช่วงรันไทม์ ยังอนุญาตให้เพิ่ม 'ส่วน' การทำงานใหม่และทางเลือกให้กับระบบได้อย่างง่ายดาย ตัวอย่างเช่นหากมีCarคลาสที่ออกแบบมาเพื่อทำงานกับEngineวัตถุและคุณต้องการเพิ่ม Engine ใหม่ให้กับระบบที่รถยนต์สามารถใช้ได้คุณสามารถสร้างEngineคลาสย่อยใหม่ และเพียงส่งวัตถุของคลาสนี้ไปยัง Carวัตถุโดยไม่ต้อง Carเปลี่ยนอะไรเกี่ยวกับ และคุณสามารถตัดสินใจได้ในระหว่างรันไทม์

  2. ความสามารถในการ 'ผ่านฟังก์ชันการทำงานรอบ ๆ ': คุณสามารถส่งผ่านวัตถุรอบ ๆ ระบบแบบไดนามิก

แต่มีข้อได้เปรียบเพิ่มเติมใด ๆ กับวัตถุที่อยู่เหนือคลาสแบบคงที่

บ่อยครั้งที่ฉันเพิ่ม 'ส่วน' ใหม่ลงในระบบฉันทำได้โดยการสร้างคลาสใหม่และสร้างอินสแตนซ์ของวัตถุจากมัน

แต่เมื่อเร็ว ๆ นี้เมื่อฉันหยุดและคิดเกี่ยวกับมันฉันรู้ว่าชั้นคงที่จะทำเช่นเดียวกับวัตถุในสถานที่จำนวนมากที่ฉันมักจะใช้วัตถุ

ตัวอย่างเช่นฉันกำลังทำงานเพื่อเพิ่มกลไกการบันทึก / โหลดไฟล์ลงในแอพของฉัน

ด้วยวัตถุบรรทัดของรหัสโทรจะมีลักษณะเช่นนี้: Thing thing = fileLoader.load(file);

ด้วยคลาสแบบคงที่มันจะมีลักษณะเช่นนี้: Thing thing = FileLoader.load(file);

ความแตกต่างคืออะไร?

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

มีข้อได้เปรียบเพิ่มเติมใด ๆ กับวัตถุอื่นจากทั้งสองที่ฉันระบุไว้? กรุณาอธิบาย.

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

ท่วงทำนองก็เป็นวัตถุเช่นกันเพราะมันมีประโยชน์ที่จะผ่านมันไป ดังนั้นคอร์ดและเครื่องชั่งก็เป็นเช่นนั้น

แต่สิ่งที่เกี่ยวกับส่วน 'คงที่' ของระบบ - ที่จะไม่ถูกส่งไปรอบ ๆ ? ตัวอย่างเช่น - กลไก 'บันทึกไฟล์' เหตุใดฉันจึงควรนำไปใช้ในวัตถุและไม่ใช่คลาสแบบสแตติก


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

จะเกิดอะไรขึ้นเมื่อคุณต้องการแลกเปลี่ยนFileLoaderสิ่งที่อ่านจากซ็อกเก็ต หรือเยาะเย้ยสำหรับการทดสอบ? หรือไฟล์ที่เปิดไฟล์ zip?
Benjamin Hodgson


1
@delnan โอเคฉันคิดคำถามที่คำตอบจะช่วยให้เข้าใจ: ทำไมคุณจะใช้ส่วน 'คงที่' ของระบบเป็นวัตถุหรือไม่ เหมือนตัวอย่างในคำถาม: กลไกการบันทึกไฟล์ ฉันจะได้อะไรจากการนำไปใช้ในวัตถุ
Aviv Cohn

1
ค่าเริ่มต้นควรใช้วัตถุที่ไม่คงที่ ใช้คลาสสแตติกเท่านั้นถ้าคุณรู้สึกว่ามันเป็นการแสดงออกถึงความตั้งใจของคุณได้ดียิ่งขึ้น System.Mathใน. NET เป็นตัวอย่างของบางสิ่งที่มีความหมายมากกว่าคลาสแบบสแตติก: คุณไม่จำเป็นต้องสลับมันหรือเยาะเย้ยมันและไม่มีการดำเนินการใด ๆ ฉันไม่คิดว่าตัวอย่าง 'การบันทึก' ของคุณจะเหมาะกับใบเรียกเก็บเงินนั้น
Benjamin Hodgson

คำตอบ:


14

ฮ่า ๆ ๆ คุณฟังดูเหมือนทีมที่ฉันเคยทำงาน;)

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

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

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

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

2) กฎของ OO นั้นค่อนข้างเข้าใจกันดี หลังจากผ่านไประยะหนึ่งคุณสามารถดูการออกแบบคลาสและดูว่าตรงตามเกณฑ์เช่น SOLID หรือไม่ และหลังจากการทดสอบหน่วยฝึกหัดจำนวนมากคุณพัฒนาความรู้สึกที่ดีของสิ่งที่ทำให้คลาส "ขนาดเหมาะสม" และ "เชื่อมโยงกัน" แต่ไม่มีกฎที่ดีสำหรับคลาสที่มีวิธีการคงที่ทั้งหมดและไม่มีเหตุผลที่แท้จริงว่าทำไมคุณไม่ควรรวมทุกอย่างไว้ในนั้น คลาสเปิดอยู่ในตัวแก้ไขของคุณดังนั้นห่าอะไร เพียงเพิ่มวิธีการใหม่ของคุณที่นั่น หลังจากผ่านไปครู่หนึ่งใบสมัครของคุณจะเปลี่ยนเป็น "God Objects" ที่แข่งขันกันจำนวนมากซึ่งแต่ละคนพยายามครองโลก อีกครั้งการเปลี่ยนโครงสร้างให้เป็นหน่วยเล็ก ๆ นั้นเป็นเรื่องส่วนตัวสูงและยากที่จะบอกได้ว่าคุณทำถูกต้องแล้ว

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

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

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

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

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

ชั้นเชิงอื่น ๆ คือการใช้ภาษาไม่ดีและพวกเขาบ่นเกี่ยวกับปัญหาที่เกิดขึ้นทั้งหมด แต่นั่นใช้ได้กับเทคโนโลยีทุกชนิด

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

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


คุณส่วนใหญ่พูดคุยเกี่ยวกับการรักษาสิ่งเล็กและโมดุลและรักษาสถานะและการทำงานร่วมกัน ไม่มีอะไรที่ห้ามไม่ให้ฉันทำเช่นนี้กับคลาสแบบคงที่ พวกเขาสามารถมีสถานะ และคุณสามารถแค็ปซูลพวกมันด้วย getters-setters และคุณสามารถแบ่งระบบออกเป็นคลาสเล็ก ๆ ที่ห่อหุ้มฟังก์ชันและสถานะได้ ทั้งหมดนี้ใช้กับทั้งคลาสและวัตถุ และนี่คือภาวะที่กลืนไม่เข้าคายไม่ออกของฉัน: ฉันกำลังเพิ่มฟังก์ชั่นใหม่ให้กับแอพของฉัน เห็นได้ชัดว่ามันจะไปในชั้นเรียนอื่นเพื่อเชื่อฟัง SRP แต่ทำไมฉันต้องยกตัวอย่างชั้นเรียนนี้ นี่คือสิ่งที่ฉันไม่เข้าใจ
Aviv Cohn

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

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

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

1
@Doval ฉันไม่เข้าใจว่าทำไมการอภิปรายของ OO ทุกครั้งจึงต้องกลายเป็นการอภิปรายเกี่ยวกับการเขียนโปรแกรมเชิงฟังก์ชัน
Rob

6

คุณถาม:

แต่มีข้อได้เปรียบเพิ่มเติมใด ๆ กับวัตถุที่อยู่เหนือคลาสแบบคงที่

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

อย่างไรก็ตามสิ่งเหล่านั้นไม่ใช่ผลประโยชน์ ประโยชน์คือ:

  1. abstractions ที่ดีกว่า
  2. การบำรุงรักษาที่ดีขึ้น
  3. ทดสอบได้ดีขึ้น

คุณพูดว่า:

แต่เมื่อเร็ว ๆ นี้เมื่อฉันหยุดและคิดเกี่ยวกับมันฉันรู้ว่าชั้นคงที่จะทำเช่นเดียวกับวัตถุในสถานที่จำนวนมากที่ฉันมักจะใช้วัตถุ

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

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

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

คุณถาม:

ด้วยวัตถุบรรทัดของรหัสโทรจะมีลักษณะเช่นนี้: Thing thing = fileLoader.load(file);

ด้วยคลาสแบบคงที่มันจะมีลักษณะเช่นนี้: Thing thing = FileLoader.load(file);

ความแตกต่างคืออะไร?

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

คุณถาม:

มีข้อได้เปรียบเพิ่มเติมจากวัตถุอื่นที่ฉันได้แสดงไว้หรือไม่? กรุณาอธิบาย.

ฉันแสดงประโยชน์ (ข้อดี) ของการใช้กระบวนทัศน์ OO ฉันหวังว่าพวกเขาจะอธิบายตนเอง ถ้าไม่ฉันจะยินดีที่จะอธิบายอย่างละเอียด

คุณถาม:

แต่สิ่งที่เกี่ยวกับส่วน 'คงที่' ของระบบ - ที่จะไม่ถูกส่งไปรอบ ๆ ? ตัวอย่างเช่น - กลไก 'บันทึกไฟล์' เหตุใดฉันจึงควรนำไปใช้ในวัตถุและไม่ใช่คลาสแบบสแตติก

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

  1. บันทึกไว้ในไฟล์ CSV
  2. บันทึกไว้ในไฟล์ XML
  3. บันทึกในไฟล์ json
  4. บันทึกเป็นไฟล์ไบนารีพร้อมการถ่ายโอนข้อมูลโดยตรง
  5. บันทึกลงในตารางโดยตรงในฐานข้อมูล

วิธีเดียวที่จะให้ตัวเลือกดังกล่าวคือการสร้างอินเทอร์เฟซและสร้างวัตถุที่ใช้อินเทอร์เฟซ

สรุปแล้ว

ใช้แนวทางที่เหมาะสมสำหรับปัญหาที่เกิดขึ้น เป็นการดีกว่าที่จะไม่นับถือศาสนาเกี่ยวกับแนวทางหนึ่งเทียบกับอีกวิธีหนึ่ง


นอกจากนี้ถ้าหลายประเภทวัตถุที่แตกต่างกัน (เรียน) จำเป็นที่จะต้องถูกบันทึกไว้ (เช่นMelody, Chord, Improvisations, SoundSampleและอื่น ๆ ) แล้วมันจะประหยัดในการลดความซับซ้อนของการดำเนินงานผ่านทางนามธรรม
rwong

5

ขึ้นอยู่กับภาษาและบริบทบางครั้ง ตัวอย่างเช่นPHPสคริปต์ที่ใช้สำหรับการให้บริการคำขอมักจะมีหนึ่งคำขอให้บริการและหนึ่งคำตอบเพื่อสร้างตลอดอายุการใช้งานของสคริปต์ดังนั้นวิธีการคงที่เพื่อดำเนินการตามคำขอและสร้างการตอบสนองอาจเหมาะสม แต่ในเซิร์ฟเวอร์ที่เขียนNode.jsอาจมีคำขอและการตอบกลับที่แตกต่างกันมากมายในเวลาเดียวกัน ดังนั้นคำถามแรกคือ - คุณแน่ใจหรือไม่ว่าคลาสแบบสแตติกสอดคล้องกับวัตถุซิงเกิลจริงๆ?

ประการที่สองแม้ว่าคุณจะมี singletons โดยใช้วัตถุที่ช่วยให้คุณสามารถใช้ประโยชน์จากความแตกต่างโดยใช้เทคนิคเช่นDependency_injectionและFactory_method_pattern มักใช้ในรูปแบบต่าง ๆ ของInversion_of_controlและมีประโยชน์สำหรับการสร้างวัตถุจำลองสำหรับการทดสอบบันทึก ฯลฯ

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


ขอบคุณสำหรับคำตอบ. ในความเห็นของคุณ: เมื่อสร้างส่วน 'คงที่' ของระบบสิ่งที่จะไม่ 'ผ่านไป' - ตัวอย่างเช่นส่วนของระบบที่เล่นเสียงหรือส่วนของระบบที่บันทึกข้อมูลลงในไฟล์: ฉันควรใช้มันในวัตถุหรือในคลาสคงที่?
Aviv Cohn

3

นอกจากโพสต์ของ Rob Y แล้ว


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

อย่างไรก็ตามทันทีที่ฟังก์ชั่นสองอย่างเหล่านี้เริ่มมีการใช้งานทั่วไปคุณต้องการให้สามารถจัดกลุ่มได้ ลองนึกภาพคุณไม่เพียง แต่มีloadฟังก์ชั่น แต่ยังมีhasValidSyntaxฟังก์ชั่น คุณจะทำอะไร?

     if (FileLoader.hasValidSyntax(myfile))
          Thing thing = FileLoader.load(myfile);
     else
          println "oh noes!"

ดูทั้งสองอ้างอิงถึงmyfileที่นี่? คุณเริ่มที่จะทำซ้ำตัวเองเพราะสถานะภายนอกของคุณจะต้องผ่านสำหรับทุกสาย Rob Y ได้อธิบายถึงวิธีที่คุณปรับสถานะภายใน (ที่นี่file) เพื่อให้คุณสามารถทำสิ่งนี้:

     FileLoader myfileLoader = new FileLoader(myfile)
     if (myfileLoader.hasValidSyntax())
          Thing thing = myfileLoader.load();
     else
          println "oh noes!"

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

2

ปัญหาสำคัญเมื่อใช้คลาสสแตติกคือพวกเขาบังคับให้คุณซ่อนการอ้างอิงของคุณและบังคับให้คุณขึ้นอยู่กับการใช้งาน ในลายเซ็นตัวสร้างต่อไปนี้การอ้างอิงของคุณคืออะไร:

public Person(String name)

ถ้าตัดสินจากลายเซ็นคนเราแค่ต้องการชื่อใช่มั้ย ถ้าไม่ใช่การนำไปปฏิบัติ:

public Person(String name) {
    ResultSet rs = DBConnection.getPersonFilePathByName(name);
    File f = FileLoader.load(rs.getPath());
    PersonData.setDataFile(f);
    this.name = name;
    this.age = PersonData.getAge();
}

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

public void testPersonConstructor() {
    Person p = new Person("Milhouse van Houten");
    assertEqual(10, p.age);
}

สิ่งนี้ควรผ่านแม้ว่าใช่ไหม ฉันหมายถึงเราห่อหุ้มสิ่งอื่นทั้งหมดใช่ไหม? จริงๆแล้วมันมีข้อยกเว้น ทำไม? โอ้ใช่การพึ่งพาที่ซ่อนเร้นที่เราไม่ทราบว่ามีอยู่ทั่วโลก DBConnectionความต้องการเริ่มต้นและเชื่อมต่อ FileLoaderความต้องการเริ่มต้นกับFileFormatวัตถุ (ชอบXMLFileFormatหรือCSVFileFormat) ไม่เคยได้ยินเรื่องเหล่านี้หรือ นั่นคือประเด็น รหัสของคุณ (และคอมไพเลอร์ของคุณ) ไม่สามารถบอกได้ว่าคุณต้องการสิ่งเหล่านี้เนื่องจากการโทรคงที่ซ่อนการอ้างอิงเหล่านั้น ฉันพูดทดสอบหรือไม่ ฉันหมายถึงผู้พัฒนารุ่นน้องใหม่เพิ่งส่งบางอย่างเช่นนี้ในรุ่นล่าสุดของคุณ หลังจากทั้งหมดรวบรวม = ทำงานใช่มั้ย

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

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

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


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

แต่การพึ่งพาที่ซ่อนอยู่สามารถมีอยู่ในคลาสที่ไม่คงที่ใช่ไหม? ดังนั้นฉันจึงไม่เห็นว่าทำไมนี่จึงเป็นข้อเสียเปรียบของคลาสที่คงที่ แต่ไม่ใช่ของคลาสที่ไม่คงที่
valenterry

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

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

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

1

แค่สองเซ็นต์ของฉัน

สำหรับคำถามของคุณ:

แต่สิ่งที่เกี่ยวกับส่วน 'คงที่' ของระบบ - ที่จะไม่ถูกส่งไปรอบ ๆ ? ตัวอย่างเช่น - กลไก 'บันทึกไฟล์' เหตุใดฉันจึงควรนำไปใช้ในวัตถุและไม่ใช่คลาสแบบสแตติก

คุณสามารถถามตัวคุณเองว่ากลไกของส่วนหนึ่งของระบบที่เล่นเสียงหรือส่วนของระบบที่บันทึกข้อมูลไปยังไฟล์ที่จะเปลี่ยนหรือไม่ ถ้าคำตอบคือใช่ที่บ่งบอกว่าคุณควรจะเป็นนามธรรมพวกเขาโดยใช้/abstract class interfaceคุณอาจถามฉันจะรู้สิ่งต่าง ๆ ในอนาคตได้อย่างไร แน่นอนว่าเราทำไม่ได้ ดังนั้นหากสิ่งนั้นไร้สัญชาติคุณสามารถใช้ 'คลาสคงที่' เช่นjava.lang.Mathมิฉะนั้นให้ใช้วิธีการเชิงวัตถุ


มีคำแนะนำในการใช้ลิ้น: สามนัดและคุณ refactorซึ่งแสดงให้เห็นว่าหนึ่งสามารถระงับจนกว่าจะมีการเปลี่ยนแปลงที่จำเป็นจริง สิ่งที่ฉันหมายถึงโดย "เปลี่ยน" คือ: ต้องบันทึกวัตถุมากกว่าหนึ่งประเภท หรือต้องการบันทึกมากกว่าหนึ่งรูปแบบไฟล์ (ประเภทการจัดเก็บ); หรือความซับซ้อนที่เพิ่มขึ้นอย่างมากของข้อมูลที่บันทึกไว้ แน่นอนว่าหากมีความมั่นใจพอสมควรว่าการเปลี่ยนแปลงจะเกิดขึ้นในไม่ช้าผู้คนสามารถรวมการออกแบบที่ยืดหยุ่นมากขึ้นล่วงหน้า
วงศ์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.