สิ่งที่เป็นนามธรรมมากเกินไปทำให้รหัสยากที่จะขยาย


9

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

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


10
เพิ่มตัวอย่างโค้ดเพื่อให้ความรู้ "" ปัญหา "" จะช่วยให้เข้าใจสถานการณ์ได้มากขึ้น
35717

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

ฉันสงสัยว่าวิธี A นั้นค่อนข้างยาวและทำมากกว่าหนึ่งอย่าง เป็นอย่างนั้นเหรอ?
Rad80

คำตอบ:


27

ถ้าฉันพยายามสร้างวิธีการใหม่เพื่อจัดการ B ที่แตกต่างกันมันจะถูกเรียกใช้สำหรับการทำสำเนารหัส

การทำสำเนารหัสทั้งหมดไม่เท่ากัน

total()สมมติว่าคุณมีวิธีการที่จะใช้เวลาสองพารามิเตอร์และเพิ่มพวกเขาร่วมกันเรียกว่า add()สมมติว่าคุณมีอีกคนหนึ่งที่เรียกว่า การใช้งานของพวกเขามีลักษณะเหมือนกันอย่างสมบูรณ์ พวกเขาควรจะรวมเป็นหนึ่งวิธี? NO !!!

หลักการDon't-Repeat-YourselfหรือDRYไม่ได้เกี่ยวกับการทำซ้ำรหัส มันเกี่ยวกับการกระจายการตัดสินใจแนวคิดไปรอบ ๆ เพื่อที่ว่าถ้าคุณเปลี่ยนความคิดของคุณคุณต้องเขียนใหม่ทุกที่ที่คุณกระจายความคิดนั้น Blegh มันแย่มาก อย่าทำมัน แทนที่จะใช้แห้งจะช่วยคุณในการตัดสินใจในสถานที่หนึ่ง

หลักการของ DRY (อย่าทำซ้ำตัวเอง)

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

wiki.c2.com - อย่าทำซ้ำตัวเอง

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

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

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

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

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

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

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

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

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

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

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

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

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

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


ฉันเห็นด้วยสุดใจ แห้งเป็นสามตัวอักษรย่อสำหรับสามคำบทกลอนไม่ซ้ำตัวเองซึ่งเป็นในทางกลับบทความ 14 หน้าในวิกิพีเดีย หากสิ่งที่คุณทำคือการพูดพึมพำตัวอักษรสามตัวโดยไม่อ่านและเข้าใจบทความ 14 หน้าคุณจะพบปัญหา นอกจากนี้ยังเกี่ยวข้องกับเมื่อและเพียงครั้งเดียว (OAOO)และอื่น ๆ หลวม ๆ ที่เกี่ยวข้องกับการเดี่ยวจุดของความจริง (SPOT) / แหล่งเดียวของความจริง (SSOT)
Jörg W Mittag

"การใช้งานของพวกเขาดูเหมือนกันอย่างสมบูรณ์พวกเขาควรจะรวมเป็นหนึ่งวิธีหรือไม่ไม่ !!!" - การสนทนานั้นเป็นจริงเช่นกันเพียงเพราะโค้ดสองชิ้นนั้นแตกต่างกันไม่ได้หมายความว่าพวกเขาจะไม่ซ้ำซ้อน มีคำพูดที่ยอดเยี่ยมจากรอนเจฟฟรีส์ในหน้าวิกิ OAOO : "ฉันเคยเห็นเบ็คประกาศสองแพทช์ของรหัสที่แตกต่างกันอย่างสิ้นเชิงเกือบจะเป็น" การทำซ้ำ "เปลี่ยนพวกเขาเพื่อให้พวกเขาซ้ำกัน เห็นได้ชัดว่ามีบางสิ่งที่ดีกว่า "
Jörg W Mittag

@ JörgWMittagแน่นอน สิ่งสำคัญคือความคิด หากคุณทำซ้ำความคิดด้วยรหัสการค้นหาที่แตกต่างกันคุณยังคงละเมิดอยู่
candied_orange

ฉันต้องจินตนาการถึง 14 หน้าบทความที่ไม่ทำซ้ำตัวเองมีแนวโน้มที่จะทำซ้ำตัวเองมาก
Chuck Adams

7

ปกติบอกว่าเราทุกคนอ่านที่นี่และมี:

ปัญหาทั้งหมดสามารถแก้ไขได้โดยเพิ่มเลเยอร์สิ่งที่เป็นนามธรรมอีกชั้นหนึ่ง

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

ทุกปัญหาสามารถแก้ไขได้โดยใช้ THE RIGHT ระดับนามธรรม

มีสองปัญหาที่แตกต่างกันในกรณีของคุณ:

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

ทั้งคู่ถูก corelated:

  • หากคุณสรุปวิธีที่ความเชี่ยวชาญทุกอย่างแตกต่างกันทุกอย่างก็ใช้ได้ ไม่มีใครเข้าใจปัญหาที่Shapeสามารถคำนวณได้surface()ในลักษณะพิเศษ
  • หากคุณสรุปการดำเนินการบางอย่างที่มีรูปแบบพฤติกรรมทั่วไปทั่วไปคุณมีสองทางเลือก:

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

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

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


5

เมื่อใดก็ตามที่ฉันเห็นวิธีที่พฤติกรรมเปลี่ยนชนิดของพารามิเตอร์ฉันจะพิจารณาก่อนทันทีว่าวิธีนั้นเป็นของพารามิเตอร์วิธีหรือไม่ ตัวอย่างเช่นแทนที่จะมีวิธีการเช่น:

public void sort(List values) {
    if (values instanceof LinkedList) {
        // do efficient linked list sort
    } else { // ArrayList
        // do efficient array list sort
    }
}

ฉันจะทำสิ่งนี้:

values.sort();

// ...

class ArrayList {
    public void sort() {
        // do efficient array list sort
    }
}

class LinkedList {
    public void sort() {
        // do efficient linked list sort
    }
}

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

class O {
    int x;
    int y;

    public void doIt(A a) {
        a.doIt(this.x, this.y);
    }
}

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

public void doIt(A a, boolean isTypeB) {
    if (isTypeB) {
        // do B stuff
    } else { 
        // do A stuff
    }
}

นี่ดูน่ากลัวมากอย่างที่instanceofฉันใช้ในตัวอย่างแรกของฉันยกเว้นว่าเรากำลังทำการตรวจสอบภายนอก ซึ่งหมายความว่าเราจะต้องเรียกมันด้วยวิธีใดวิธีหนึ่งจากสองวิธี:

o.doIt(a, a instanceof B);

หรือ:

o.doIt(a, true); //or false

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

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

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

หากเราย้ายวิธีการไปAตามที่แนะนำไว้ก่อนหน้านี้เราสามารถแทรกตัวอย่างของXเข้าไปAและมอบหมายวิธีการนั้นให้กับX:

class A {
    X x;
    A(X x) {
        this.x = x;
    }

    public void doIt(int x, int y) {
        x.doIt(x, y);
    }
}

เราสามารถดำเนินการA'และและกำจัดB' Bเราได้ปรับปรุงโค้ดโดยให้ชื่อกับแนวคิดที่อาจมีความหมายโดยนัยมากกว่าและอนุญาตให้เรากำหนดพฤติกรรมนั้นในเวลารันไทม์แทนการรวบรวมเวลา Aจริง ๆ แล้วก็กลายเป็นนามธรรมน้อยลงเช่นกัน แทนที่จะเป็นความสัมพันธ์ที่สืบทอดเพิ่มเติมการเรียกใช้เมธอดบนวัตถุที่มอบหมาย วัตถุนั้นเป็นนามธรรม แต่เน้นเฉพาะความแตกต่างในการนำไปใช้

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

B b = new B();
o.doIt(b, true);

เราสันนิษฐานว่าก่อนหน้านี้เมื่อเขียนว่าAมีXที่เป็นอย่างใดอย่างหนึ่งหรือA' B'แต่บางทีข้อสันนิษฐานนี้อาจไม่ถูกต้อง นี่คือสถานที่เดียวที่แตกต่างกันนี้ระหว่างAและBเรื่อง? ถ้าเป็นเช่นนั้นบางทีเราสามารถใช้แนวทางที่แตกต่างกันเล็กน้อย เรายังมีXที่เป็นอย่างใดอย่างหนึ่งA'หรือแต่มันไม่ได้อยู่ในB' AเพียงO.doItใส่ใจเกี่ยวกับเรื่องนี้จึงขอเพียงผ่านไปO.doIt:

class O {
    int x;
    int y;

    public void doIt(A a, X x) {
        x.doIt(a, x, y);
    }
}

ตอนนี้ไซต์การโทรของเราดูเหมือนว่า:

A a = new A();
o.doIt(a, new B'());

อีกครั้งหนึ่งที่หายไปและย้ายเข้าสู่นามธรรมเน้นB Xแม้ว่าเวลาAนี้จะง่ายกว่าโดยไม่รู้น้อย มันเป็นนามธรรมที่น้อยลง

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


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

1
@Baldrickk กระบวนทัศน์แต่ละแบบนำเสนอวิธีคิดของตัวเองด้วยข้อดีและข้อเสียที่เป็นเอกลักษณ์ ในการใช้งาน Haskell การจับคู่รูปแบบจะเป็นวิธีที่ดีกว่า แม้ว่าในภาษาเช่นนั้นบางแง่มุมของปัญหาดั้งเดิมจะเป็นไปไม่ได้เช่นกัน
cbojar

1
นี่คือคำตอบที่ถูกต้อง วิธีที่เปลี่ยนการใช้งานตามประเภทที่ดำเนินการควรเป็นวิธีการประเภทนั้น
Roman Reiner

0

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

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

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

ความสามารถในการขยายนี้ทำงานได้โดยมีคลาสฐานวัตถุที่สามารถขยายได้เก็บส่วนขยาย:

void f(CreditorBO creditor) {
    creditor.as(AllowedCreditorBO.class).ifPresent(allowedCreditor -> ...);
}

ภายในมีการทำแผนที่สันหลังยาวของวัตถุไปยังวัตถุที่ขยายโดยชั้นส่วนขยาย

สำหรับคลาส GUI และส่วนประกอบนั้นความสามารถในการขยายได้เหมือนกันส่วนหนึ่งมาจากการสืบทอด การเพิ่มปุ่มและเช่น

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

ดังนั้นจึงไม่มีทางออก แต่พยายามทำงานในบริบทปัจจุบัน


0

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

บทคัดย่อเป็นสิ่งที่ดี ฉันอารมณ์พวกเขาด้วยสองแนวทาง:

  • ดีกว่าที่จะไม่เป็นนามธรรมเร็วเกินไป รอตัวอย่างเพิ่มเติมของรูปแบบก่อนที่จะยกเลิก 'เพิ่มเติม' แน่นอนว่าเป็นเรื่องส่วนตัวและเฉพาะเจาะจงกับสถานการณ์ที่ยากลำบาก

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

ในทั้งสองกรณี 'มากขึ้นและ' มากเกินไป 'ไม่ใช่หมายเลขที่แน่นอน มันขึ้นอยู่กับ. นั่นคือสิ่งที่ทำให้มันยาก

ฉันชอบบทความนี้จาก Sandi Metz

https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction

การทำสำเนามีราคาถูกกว่าการทำผิดที่ผิด
และ
ชอบการทำซ้ำมากกว่าการทำสิ่งที่ไม่ถูกต้อง

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