เมื่อใดที่คุณควรและไม่ควรใช้คำหลัก 'ใหม่'


15

ฉันดูการนำเสนอของ Google Tech Talk เกี่ยวกับการทดสอบหน่วยที่ Misko Hevery มอบให้และเขาบอกว่าเพื่อหลีกเลี่ยงการใช้newคำหลักในรหัสตรรกะทางธุรกิจ

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

ฉันสงสัยว่าฉันทำอะไรผิดหรือเปล่าเมื่อฉันใช้คำหลักใหม่สำหรับโปรแกรมของฉัน และเราจะทำลาย 'กฎ' นั้นได้ที่ไหน


2
คุณสามารถเพิ่มแท็กที่เกี่ยวข้องกับภาษาการเขียนโปรแกรมได้หรือไม่ ใหม่มีอยู่ในหลายภาษา (C ++, Java, Ruby เพื่อตั้งชื่อไม่กี่) และมีความหมายที่แตกต่างกัน
sakisk

คำตอบ:


16

นี่เป็นแนวทางมากกว่ากฎที่ยากและรวดเร็ว

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

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

แน่นอนคุณสามารถทำสิ่งนี้ได้ไกลเกินไป ถ้าคุณต้องการคืน ArrayList ใหม่แสดงว่าคุณอาจตกลง - โดยเฉพาะถ้านี่จะเป็นรายการที่ไม่เปลี่ยนรูป

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


1
ดีถ้าคุณต้องการให้มันเป็นรายการที่ไม่เปลี่ยนรูปคุณควรใช้Collections.unmodifiableListหรืออะไรบางอย่าง แต่ฉันรู้ว่าคุณหมายถึงอะไร :)
MatrixFrog

ใช่ แต่คุณต้องอย่างใดที่จะสร้างรายชื่อเดิมที่คุณแล้วแปลงไปเป็น unmodifiable ...
บิลมิเชลล์

5

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

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


5

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

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

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

หลักเกณฑ์ทั่วไปบางประการ:

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

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

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


3

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


2

คนอื่น ๆ ได้กล่าวถึงประเด็นนี้แล้ว แต่ต้องการอ้างจาก Clean Code ของ Uncle Bob (Bob Martin) เพราะอาจทำให้เข้าใจแนวคิดได้ง่ายขึ้น:

"กลไกอันทรงพลังสำหรับการแยกการก่อสร้างออกจากการใช้งานคือDependency Injection (DI), การประยุกต์ใช้Inversion of Control (IoC) กับการจัดการการพึ่งพา Inversion of Control การย้ายการควบคุมย้ายความรับผิดชอบรองจากวัตถุไปยังวัตถุอื่น ๆเดี่ยวรับผิดชอบหลักการ . ในบริบทของการจัดการการพึ่งพาวัตถุไม่ควรจะต้องรับผิดชอบสำหรับอินสแตนซ์พึ่งพาตัวเอง. แต่มันควรจะผ่านความรับผิดชอบนี้ไปยังอีกกลไก "เผด็จการ" จึง inverting ควบคุม. เนื่องจากการตั้งค่าเป็นกังวลทั่วโลกนี้ กลไกที่มีสิทธิ์โดยปกติจะเป็นทั้งชุดคำสั่ง "หลัก" หรือคอนเทนเนอร์วัตถุประสงค์พิเศษ

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

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