มันเป็นการดีที่จะใช้ฟังก์ชั่นเพื่อรวมรหัสทั่วไปหรือไม่


20

ฉันพบปัญหานี้มากมาย ตัวอย่างเช่นฉันกำลังเขียนฟังก์ชั่นการอ่านและฟังก์ชั่นการเขียนและพวกเขาทั้งสองตรวจสอบว่าbufเป็นตัวชี้ NULL และmodeตัวแปรที่อยู่ภายในขอบเขตที่แน่นอน

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

ดังนั้นคุณควรใช้ฟังก์ชั่นสำหรับสิ่งนี้หรือไม่? ข้อดีและข้อเสียคืออะไร?


2
สิ่งบรรทัดเดียวต้องการมากกว่าการใช้ในหลาย ๆ สถานที่เพื่อรับประกันการย้ายเข้าสู่ฟังก์ชันที่แยกต่างหาก

1
เป็นคำถามที่ดีมาก มีหลายครั้งที่ฉันสงสัยเหมือนกัน
rdasxy

คำตอบ:


31

นี่คือการใช้ฟังก์ชั่นที่ยอดเยี่ยม

นี่จะเป็นฟังก์ชั่นโลหิตจาง (ไม่มาก) ...

ดีแล้ว. ฟังก์ชั่นควรทำสิ่งเดียวเท่านั้น

ค่อนข้างมีการแปล ...

ในภาษา OO ทำให้เป็นส่วนตัว

(ไม่ใช่วัตถุประสงค์ทั่วไป)

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

และไม่โดดเด่นในตัวของมันเอง (ไม่สามารถเข้าใจสิ่งที่คุณต้องการได้เว้นแต่คุณจะเห็นว่ามันถูกใช้อยู่ที่ใด)

ขึ้นมาด้วยชื่อที่ดีสำหรับมันและมันก็ยืนได้ด้วยตัวของมันเอง "ValidateFileParameters" หรือบางอย่าง ตอนนี้ยืนได้ด้วยตัวเอง


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

2
@deadalnix: จุดดี ในขณะที่ฉันกำลังเขียนฉันก็ตระหนักว่าคะแนนของฉันนั้นง่ายเกินไป การเขียนและการดีบักที่ง่ายกว่าย่อมเป็นประโยชน์ในการแยกสิ่งต่าง ๆ ออกเป็นฟังก์ชั่น (เช่นเดียวกับความสามารถในการทดสอบฟังก์ชั่นแยกหน่วย)
Kramii Reinstate Monica

11

มันควรจะเป็นฟังก์ชั่นโดยสิ้นเชิง

if (isBufferValid(buffer)) {
    // ...
}

อ่านได้ง่ายขึ้นและบำรุงรักษาได้มากขึ้น (หากตรรกะการตรวจสอบเปลี่ยนไปคุณจะเปลี่ยนได้ในที่เดียว)

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

ให้ฉันถามคำถามที่ดีกว่า วิธีนี้ไม่ได้เป็นการปฏิบัติที่ดีอย่างไร

ทำในสิ่งที่ถูกต้อง. :)


4
ถ้า isBufferValid นั้นง่ายreturn buffer != null;ฉันคิดว่าคุณกำลังทำร้ายการอ่านที่นั่น
pdr

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

4
@pdr ฉันไม่ทราบว่ามันเจ็บมาตรฐานการอ่านใด ๆ คุณได้ยกเว้นนักพัฒนาซอฟต์แวร์รายอื่นจากการใส่ใจว่าคุณกำลังทำอะไรอยู่และมุ่งเน้นไปที่สิ่งที่คุณทำ isBufferValidอ่านได้ง่ายกว่า (ในหนังสือของฉัน) มากกว่าbuffer != nullเพราะมันสื่อถึงจุดประสงค์ที่ชัดเจนยิ่งขึ้น และอีกครั้งไม่พูดถึงมันช่วยให้คุณประหยัดจากการทำซ้ำเช่นกันที่นี่ คุณต้องการอะไรอีก
Yam Marcovic

5

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

แน่นอนว่ามีข้อ จำกัด ที่กำหนดโดยสามัญสำนึก คุณไม่ต้องการมีWriteToConsole(text)วิธีการที่ร่างกายเป็นเพียงConsole.WriteLine(text)ตัวอย่างเช่น แต่การทำผิดพลาดในด้านของการอ่านเป็นวิธีปฏิบัติที่ดี


2

โดยทั่วไปควรใช้ฟังก์ชั่นเพื่อลบการทำซ้ำในโค้ด

อย่างไรก็ตามมันสามารถนำไปไกลเกินไป นี่คือการเรียกการตัดสิน

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

if (buf==null) throw new BufferException("Null read buffer!");

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

checkForNullBuffer(buf, "Null read buffer!");

นอกจากนี้การดำน้ำในฟังก์ชั่นเพื่อดูว่ามันทำอะไรเมื่อการดีบั๊กหมายความว่าการเรียกฟังก์ชั่นนั้นน้อยกว่า "โปร่งใส" สำหรับผู้ใช้


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

1
คุณคิดถึงจุดที่ฉันเห็นมัน ไม่ต้องก้าวข้ามหรือต้องพิจารณาตรรกะทุกครั้งที่คุณดีบักคือสิ่งที่ฉันกำลังมองหา ถ้าฉันต้องการทราบว่ามันเสร็จสิ้นแล้วฉันจะตรวจสอบอีกครั้ง ต้องทำทุกครั้งที่โง่เพียง
Yam Marcovic

หากข้อความแสดงข้อผิดพลาด ("Null read buffer") ถูกทำซ้ำการทำซ้ำนั้นควรจะถูกกำจัดอย่างแน่นอน
kevin cline

มันเป็นเพียงตัวอย่าง แต่สมมุติว่าคุณจะมีข้อความที่แตกต่างกันในแต่ละไซต์ที่เรียกใช้ฟังก์ชัน - ตัวอย่างเช่น "Null read buffer" และ "Null write buffer" เป็นต้น ถ้าคุณต้องการให้ข้อความเข้าสู่ระบบ / ข้อผิดพลาดเหมือนกันสำหรับแต่ละสถานการณ์ที่คุณไม่สามารถยกเลิกการทำซ้ำ .......
mikera

-1 Preconditions.checkNotNull เป็นแนวปฏิบัติที่ดีไม่ใช่แบบไม่ดี ไม่จำเป็นต้องรวมข้อความสตริงแม้ว่า google-collections.googlecode.com/svn/trunk/javadoc/com/google/…
ripper234

2

การรวมศูนย์กลางของรหัสเป็นความคิดที่ดีเสมอ เราต้องใช้รหัสซ้ำให้มากที่สุด

อย่างไรก็ตามมันเป็นสิ่งสำคัญที่จะต้องทราบวิธีการทำ ตัวอย่างเช่นเมื่อคุณมีรหัสที่ compute_prime_number () หรือ check_if_packet_is_bad () เป็นสิ่งที่ดี โอกาสที่อัลกอริทึมของฟังก์ชั่นของตัวเองจะมีวิวัฒนาการที่จะได้รับประโยชน์

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

นี่คือคำถามที่คุณควรถามก่อนถาม

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

  2. บริบทอื่นใดที่จะต้องใช้ฟังก์ชันเดียวกัน เป็นไปได้หรือไม่ที่คุณอาจต้องการวางมาตรฐาน API เล็กน้อยก่อนใช้งาน

  3. อะไรคือความคาดหวังของแอปพลิเคชั่น (ส่วนต่าง ๆ ของ) เมื่อคุณส่งข้อยกเว้น

  4. สถานการณ์อะไรที่จะเห็นว่าฟังก์ชั่นกำลังจะพัฒนาขึ้น?

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

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


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

1

ควรหลีกเลี่ยงการใส่รหัสซ้ำซ้อน ทุกครั้งที่คุณคาดหวังคุณควรหลีกเลี่ยงการทำซ้ำรหัส หากคุณไม่ได้คาดหวังให้ใช้กฎข้อที่ 3: refactor ก่อนที่โค้ดซ้ำกันจะถูกทำซ้ำ 3 ครั้งให้ใส่คำว่าโวหารเมื่อทำซ้ำ 2 ครั้ง

รหัสซ้ำคืออะไร

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

พิจารณาตัวอย่างด้านล่าง:

if(user.getPrileges().contains("admin")) {
    // Do something
}

กลายเป็น

if(user.isAdmin()) {
    // Do something
}

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


1
ตัวอย่าง Btw แสดงให้เห็นถึงกฎหมายของ Demeter
เม.ย.

1

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


0

หากคุณเห็นว่ามันซ้ำซ้อนคุณควรหาวิธีที่จะรวบรวมมันไว้ที่ส่วนกลาง

ฟังก์ชั่นเป็นวิธีที่ดี (อาจจะไม่ดีที่สุด แต่ขึ้นอยู่กับภาษา) แม้ว่าฟังก์ชั่นจะเป็นโลหิตจางอย่างที่คุณบอกว่าไม่ได้หมายความว่ามันจะยังคงอยู่แบบนั้น

ถ้าคุณต้องตรวจสอบอย่างอื่นด้วยล่ะ

คุณจะหาสถานที่ทั้งหมดที่คุณต้องเพิ่มการตรวจสอบพิเศษหรือเพียงแค่เปลี่ยนฟังก์ชั่นหรือไม่?


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

1
@EpsilonVector กฎง่ายๆคือถ้าฉันต้องเปลี่ยนมันอีกครั้งฉันก็อาจจะปรับโครงสร้างได้อีกครั้ง ดังนั้นในกรณีของคุณฉันจะทิ้งไว้และถ้าฉันต้องเปลี่ยนมันจะกลายเป็นฟังก์ชั่น
Thanos Papathanasiou

0

มันเกือบจะดีเสมอหากปฏิบัติตามเงื่อนไขต่อไปนี้:

  • ปรับปรุงการอ่าน
  • ใช้ภายในขอบเขตที่ จำกัด

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

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