abstractions ต้องลดการอ่านรหัสหรือไม่?


19

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

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

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

คำถามของฉันคือ: เมื่อกรอบงานหรือรูปแบบนำเสนอค่าใช้จ่ายมากเช่นนี้มันจะคุ้มหรือไม่ มันเป็นอาการของรูปแบบการใช้งานที่ไม่ดีหรือไม่?

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

คำตอบ:


17

นี่เป็นความคิดเห็นที่ยาวกว่าในคำตอบของ @kevin cline

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

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

ในทางตรงกันข้ามเมื่อคุณพบเห็นมันใน Java คุณมีแนวโน้มที่จะเห็นความแตกต่างของ "enterprise hello world" ที่เป็นที่รู้จักกันดีซึ่งแทนที่จะเป็นคลาสที่น่ารำคาญที่ทำอะไรง่ายๆคุณจะได้คลาสพื้นฐานที่เป็นนามธรรม และคลาสที่ได้รับอย่างเป็นรูปธรรมซึ่งใช้อินเตอร์เฟส X และสร้างขึ้นโดยคลาสโรงงานในกรอบงาน DI เป็นต้นโค้ด 10 บรรทัดที่ทำงานจริงถูกฝังไว้ภายใต้โครงสร้างพื้นฐาน 5,000 บรรทัด

บางอย่างขึ้นอยู่กับสภาพแวดล้อมอย่างน้อยเท่ากับภาษา - ทำงานโดยตรงกับสภาพแวดล้อมแบบหน้าต่างเช่น X11 และ MS Windows มีชื่อเสียงในการเปลี่ยนโปรแกรม "hello world" เล็กน้อยเป็น 300+ บรรทัดของขยะที่ไม่สามารถถอดรหัสได้ เมื่อเวลาผ่านไปเราได้พัฒนาชุดเครื่องมือต่าง ๆ เพื่อป้องกันเราด้วย - แต่ 1) ชุดเครื่องมือเหล่านั้นค่อนข้างไม่สำคัญตัวเองและ 2) ผลลัพธ์สุดท้ายนั้นไม่เพียง แต่ใหญ่กว่าและซับซ้อนกว่า แต่ยังยืดหยุ่นน้อยกว่า กว่าโหมดข้อความเทียบเท่า (เช่นแม้ว่าจะเป็นเพียงการพิมพ์ข้อความการเปลี่ยนเส้นทางไปยังไฟล์นั้นแทบจะเป็นไปไม่ได้ / รองรับ)

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


7

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


+1 สำหรับการกล่าวถึง YAGNI และ abstractions ด้วยจุดอ้างอิงเดียว บทบาทหลักของการทำสิ่งที่เป็นนามธรรมคือการแยกแยะประเด็นร่วมกันของหลายสิ่ง หากมีการอ้างถึงสิ่งที่เป็นนามธรรมเพียงจุดเดียวเราไม่สามารถพูดถึงสิ่งที่พบเห็นได้ทั่วไปสิ่งที่เป็นนามธรรมเช่นนี้เป็นสาเหตุของปัญหา yoyo ฉันจะขยายนี้เพราะนี้เป็นจริงสำหรับทุกชนิดของนามธรรม: ฟังก์ชั่น generics แมโครสิ่งที่ ...
Calmarius

3

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

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

เพื่อให้บรรลุสิ่งที่เป็นนามธรรมที่ดีอย่างหนึ่งควร KISS: เห็นคำตอบของฉันคำถามนี้จะรู้ว่าสิ่งที่จะปฏิบัติตามเพื่อหลีกเลี่ยงชนิดของปัญหาเหล่านั้น

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

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


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

คลาสไม่ได้เป็นเพียงนามธรรมที่เราใช้: ฟังก์ชั่น, โมดูล / ไลบรารี, บริการ, ฯลฯ ในชั้นเรียนของคุณคุณมักจะสรุปแต่ละฟังก์ชั่นที่อยู่เบื้องหลังฟังก์ชั่น / วิธีการที่สามารถเรียกวิธีอื่น ๆ
Klaim

1
@Statement: การสรุปข้อมูลเป็นสิ่งที่เป็นนามธรรม
Ed S.

อย่างไรก็ตามลำดับชั้นของเนมสเปซนั้นดีมาก
JAB

2

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

มีสิ่งที่ใช้ประโยชน์ได้จริงที่ช่วยให้คุณวัดความซับซ้อนของ codebase ซึ่งไม่ได้กล่าวถึงบ่อยครั้งในทางทฤษฎี SE เช่นลึกเข้าไปใน call stack ที่คุณจะได้รับก่อนที่จะถึงจุดสิ้นสุดและความลึกที่คุณต้องไปก่อนด้วย ความมั่นใจอย่างมากเข้าใจผลข้างเคียงที่เป็นไปได้ทั้งหมดที่อาจเกิดขึ้นที่ระดับของ call stack รวมถึงในกรณีที่มีข้อยกเว้น

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

มีหลายสิ่งมากเกินไปที่จะใช้ประโยชน์ได้

ทางเลือกก่อนตอนที่ผมทำงานใน codebases ก่อนหน้านี้เป็นระบบที่มีหลายร้อยหลายพันของวัตถุส่วนใหญ่มีขนาดเล็กเมื่อเทียบกับระบบโหลขนาดใหญ่ไม่กี่กับวัตถุบางอย่างที่ใช้เพียงเพื่อให้ผ่านข้อความจากวัตถุหนึ่งไปยังอีก ( Messageวัตถุเช่นที่มีของ ส่วนต่อประสานสาธารณะของตัวเอง) นั่นคือสิ่งที่คุณจะได้รับแบบอะนาล็อกเมื่อคุณย้อนกลับ ECS กลับไปยังจุดที่องค์ประกอบมีการทำงานและการรวมกันที่ไม่ซ้ำกันของส่วนประกอบในเอนทิตีทำให้ประเภทวัตถุของตัวเอง และที่จะมีแนวโน้มที่จะให้ผลผลิตที่มีขนาดเล็ก, ฟังก์ชั่นที่เรียบง่ายได้รับการถ่ายทอดและให้บริการโดยรวมกันไม่มีที่สิ้นสุดของวัตถุที่ความคิดรุ่นเล็ก ( ParticleวัตถุเทียบกับPhysics System, เช่น). อย่างไรก็ตามมันยังมีแนวโน้มที่จะให้กราฟที่ซับซ้อนของการพึ่งพาระหว่างกันซึ่งทำให้มันยากที่จะให้เหตุผลเกี่ยวกับสิ่งที่เกิดขึ้นจากระดับกว้าง ๆ เพียงเพราะมีหลายสิ่งมากมายใน codebase ที่สามารถทำอะไรบางอย่างได้จริง - ประเภทที่ไม่ใช่ประเภท "ข้อมูล" แต่เป็นประเภท "วัตถุ" ที่มีฟังก์ชันการทำงานที่เกี่ยวข้อง ประเภทที่ใช้เป็นข้อมูลที่บริสุทธิ์โดยไม่มีฟังก์ชั่นที่เกี่ยวข้องไม่สามารถผิดพลาดได้เนื่องจากพวกเขาไม่สามารถทำอะไรได้ด้วยตนเอง

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

ข้อมูลเพิ่มเติม, ฟังก์ชั่นน้อยลง

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

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


0

คำถามของฉันคือเมื่อกรอบหรือรูปแบบนำเสนอค่าใช้จ่ายมากเช่นนี้มันจะคุ้มหรือไม่ มันเป็นอาการของรูปแบบการใช้งานที่ไม่ดีหรือไม่?

อาจเป็นอาการของการเลือกภาษาการเขียนโปรแกรมที่ไม่ถูกต้อง


1
ฉันไม่เห็นว่าสิ่งนี้เกี่ยวข้องกับภาษาที่เลือกได้อย่างไร Abstractions เป็นแนวคิดที่ไม่ขึ้นกับภาษาระดับสูง
Ed S.

@Ed: บทคัดย่อบางอย่างมีความเข้าใจได้ง่ายกว่าในบางภาษามากกว่าในบางภาษา
kevin cline

ใช่ แต่นั่นไม่ได้หมายความว่าคุณไม่สามารถเขียนสิ่งที่เป็นนามธรรมและเข้าใจได้ง่ายในภาษาเหล่านั้น ประเด็นของฉันคือคำตอบของคุณไม่ตอบคำถามหรือช่วย OP ในทางใดทางหนึ่ง
Ed S.

0

ความเข้าใจที่ไม่ดีของรูปแบบการออกแบบมีแนวโน้มที่จะเป็นสาเหตุสำคัญของปัญหานี้ หนึ่งในสิ่งที่แย่ที่สุดที่ฉันเคยเห็นสำหรับ Yo-yo'ing นี้และเด้งจากส่วนต่อประสานไปยังส่วนต่อประสานที่ไม่มีข้อมูลที่เป็นรูปธรรมมากในนั้นคือส่วนขยายสำหรับ Grid Control ของ Oracle
ดูเหมือนว่าใครบางคนมีวิธีการที่เป็นนามธรรมจากโรงงานและการสำเร็จความใคร่รูปแบบมัณฑนากรทั่วรหัส Java ของฉัน และมันทำให้ฉันรู้สึกกลวงและเดียวดาย


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