วิธีการส่วนตัวที่มีรูปแบบการอ้างอิงเดียวไม่ดีหรือไม่?


139

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

มีฉันทามติเกี่ยวกับเรื่องนี้หรือไม่? มันจะดีกว่าไหมถ้ามีวิธีสาธารณะยาว ๆ หรือแบ่งมันเป็นชิ้นเล็ก ๆ แม้ว่าแต่ละชิ้นจะไม่สามารถใช้ซ้ำได้?


14
การทำซ้ำที่เป็นไปได้ของมันเป็น
ริ้น

7
คำตอบทั้งหมดขึ้นอยู่กับความคิดเห็น ผู้เขียนดั้งเดิมมักคิดว่ารหัสราวีโอลี่ของพวกเขาสามารถอ่านได้มากขึ้น บรรณาธิการที่ตามมาเรียกว่าราวีโอลี่
Frank Hileman

5
ฉันแนะนำให้คุณอ่าน Clean Code มันแนะนำสไตล์นี้และมีตัวอย่างมากมาย นอกจากนี้ยังมีทางออกสำหรับปัญหา "การกระโดดไปรอบ ๆ "
Kat

1
ฉันคิดว่าสิ่งนี้ขึ้นอยู่กับทีมของคุณและมีโค้ดอะไรบ้าง
หวังว่าช่วยได้

1
เฟรมเวิร์กทั้งหมดของ STRUTS ไม่ได้ใช้เมธอด Getter / Setter แบบใช้ครั้งเดียวทั้งหมดซึ่งเป็นวิธีแบบใช้ครั้งเดียวทั้งหมดหรือไม่
Zibbobz

คำตอบ:


203

ไม่นี่ไม่ใช่สไตล์ที่ไม่ดี ในความเป็นจริงมันเป็นสไตล์ที่ดีมาก

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

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

หากคุณแบ่งฟังก์ชั่นนี้ออกเป็นชิ้นเล็ก ๆ มันก็ยัง "ทำงาน" ได้มากเหมือน แต่ก่อนในชิ้นเล็ก ๆ มันเรียกฟังก์ชั่นอื่น ๆ ซึ่งควรมีชื่ออธิบาย ฟังก์ชั่นหลักอ่านเหมือนหนังสือ: ทำ A, ทำ B แล้วทำ C เป็นต้นฟังก์ชั่นที่เรียกอาจเรียกได้ในที่เดียว แต่ตอนนี้มันเล็กกว่า ฟังก์ชั่นพิเศษใด ๆ จำเป็นต้องมี sandboxed จากฟังก์ชั่นอื่น: มันมีขอบเขตที่แตกต่างกัน

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

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

  • สถานที่อ้างอิง ตอนนี้มันเป็นไปไม่ได้ที่จะประกาศและใช้ตัวแปรแล้วให้มันวนไปวนมาและใช้อีก 100 บรรทัดในภายหลัง ฟังก์ชั่นเหล่านี้มีขอบเขตที่แตกต่างกัน

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

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

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


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

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

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

7
สัญลักษณ์แสดงหัวข้อย่อย "การทดสอบ" เป็น IMO ที่น่าสงสัย หากสมาชิกส่วนตัวของคุณต้องการที่จะทดสอบพวกเขาอาจอยู่ในชั้นเรียนของตนเองในฐานะสมาชิกสาธารณะ ได้รับขั้นตอนแรกในการแยกคลาส / อินเตอร์เฟสคือการแยกวิธี
Mathieu Guindon

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

36

มันอาจเป็นความคิดที่ยอดเยี่ยม!

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

function step1(){
  // ...
  step2(zarb, foo, biz);
}

function step2(zarb, foo, biz){
  // ...
  step3(zarb, foo, biz, gleep);
}

function step3(zarb, foo, biz, gleep){
  // ...
}

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

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

function foo(){
  f = getFrambulation();
  g = deglorbFramb(f);
  r = reglorbulate(g);
}

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

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

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


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

34
@ FrankHileman เพื่อความเป็นธรรมฉันไม่เคยเห็นนักพัฒนาคนใดเลยที่ยกย่องโค้ดของเขาหรือเธอ
ต. Sar

4
@ ก่อนหน้านักพัฒนาก่อนหน้านี้เป็นสาเหตุของปัญหาทั้งหมด!
Frank Hileman

18
@ TS ฉันไม่เคยชื่นชมนักพัฒนาก่อนหน้านี้เมื่อฉันเป็นนักพัฒนาก่อนหน้านี้
IllusiveBrian

3
สิ่งที่ดีเกี่ยวกับการย่อยสลายคือคุณสามารถเขียน: foo = compose(reglorbulate, deglorbFramb, getFrambulation);;)
Warbo

19

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

  • ฟังก์ชั่นส่วนตัวมากมายที่มีผลข้างเคียงทำให้รหัสอ่านยากและค่อนข้างบอบบาง โดยเฉพาะอย่างยิ่งเมื่อฟังก์ชันส่วนตัวเหล่านี้ขึ้นอยู่กับผลข้างเคียงของกันและกัน หลีกเลี่ยงการมีฟังก์ชั่นการทำงานร่วมกันอย่างแน่นหนา

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

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

  • รหัสที่ย่อยสลายอย่างหนักจะชัดเจนต่อผู้เขียนมากกว่ารหัสอื่น ๆ นี่ไม่ได้หมายความว่ามันยังไม่ชัดเจนสำหรับคนอื่น ๆ แต่มันง่ายที่จะพูดว่ามันสมเหตุสมผลดีที่bar()ต้องถูกเรียกมาก่อนfoo()ในเวลาที่เขียน

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

ฟังก์ชั่นการสลายตัวขึ้นอยู่กับการรวมกันภายในและการมีเพศสัมพันธ์ของพวกเขาไม่ใช่ความยาวแน่นอน


6
ไม่สนใจความสามารถในการอ่านลองดูที่การรายงานบั๊ก: หากผู้ใช้รายงานว่ามีข้อผิดพลาดเกิดขึ้นMainMethod(ง่ายพอที่จะรับโดยทั่วไปจะมีสัญลักษณ์รวมอยู่ด้วยและคุณควรมีไฟล์การอ้างอิงสัญลักษณ์) ซึ่งเป็น 2k บรรทัด รายงานข้อผิดพลาด) และมันเป็น NRE ที่สามารถเกิดขึ้นได้ที่ 1k ของบรรทัดเหล่านั้นดีฉันจะถูกสาปถ้าฉันมองเข้าไปในนั้น แต่ถ้าพวกเขาบอกฉันว่ามันเกิดขึ้นSomeSubMethodAและมันมี 8 บรรทัดและข้อยกเว้นคือ NRE ฉันอาจจะหาและแก้ไขได้ง่ายพอ
410_Gone

2

IMHO คุณค่าของการดึงบล็อกออกมาอย่างหมดจดเพื่อทำลายความซับซ้อนมักจะเกี่ยวข้องกับความแตกต่างของความซับซ้อนระหว่าง:

  1. คำอธิบายภาษามนุษย์ที่สมบูรณ์และถูกต้องเกี่ยวกับสิ่งที่รหัสทำรวมถึงวิธีจัดการกรณีมุมและ

  2. รหัสตัวเอง

หากรหัสมีความซับซ้อนมากกว่าคำอธิบายการแทนที่รหัสในบรรทัดด้วยการเรียกใช้ฟังก์ชันอาจทำให้เข้าใจรหัสได้ง่ายขึ้น ในทางตรงกันข้ามแนวคิดบางอย่างสามารถแสดงได้อย่างชัดเจนในภาษาคอมพิวเตอร์มากกว่าภาษามนุษย์ ฉันจะถือว่าตัวอย่างเช่นในขณะที่อ่านได้มากขึ้นกว่าw=x+y+z;w=addThreeNumbersAssumingSumOfFirstTwoDoesntOverflow(x,y,z);

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


2
ชื่อฟังก์ชันของคุณทำให้เข้าใจผิด ไม่มีอะไรใน w = x + y + z ที่บ่งบอกถึงการควบคุมการโอเวอร์โฟลว์ใด ๆ ในขณะที่ชื่อเมธอดด้วยตัวเองดูเหมือนจะบ่งบอกเป็นอย่างอื่น ชื่อที่ดีกว่าสำหรับฟังก์ชั่นของคุณคือ "addAll (x, y, z)" ตัวอย่างเช่นหรือแม้แต่เพียงแค่ "Sum (x, y, z)"
ต. Sar

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

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

1
@ TSar: ฉันเป็นคนขี้อายกับชื่อ แต่จุดสำคัญคือ (เปลี่ยนเป็นค่าเฉลี่ยเพราะมันเป็นตัวอย่างที่ดีกว่า) โปรแกรมเมอร์ที่เห็น "(x + y + z + 1) / 3" ในบริบทจะเป็น ไกลวางดีกว่าที่จะรู้ว่าไม่ว่าจะเป็นวิธีการที่เหมาะสมในการคำนวณค่าเฉลี่ยที่กำหนดรหัสโทร, int average3(int n1, int n2, int n3)กว่าคนที่กำลังมองหาที่ทั้งความหมายหรือการเรียกร้องของเว็บไซต์ การแยกฟังก์ชั่น "เฉลี่ย" หมายความว่าใครบางคนที่ต้องการดูว่ามันเหมาะกับความต้องการหรือไม่นั้นต้องดูสองแห่ง
supercat

1
หากเราใช้ C # คุณจะมีวิธี "เฉลี่ย" เพียงวิธีเดียวซึ่งสามารถทำได้เพื่อยอมรับพารามิเตอร์จำนวนเท่าใดก็ได้ ในความเป็นจริงใน c # มันใช้งานได้กับคอลเลกชันตัวเลขใด ๆ - และฉันแน่ใจว่ามันง่ายกว่าที่จะเข้าใจมากกว่าการแก้ปัญหาคณิตศาสตร์ดิบในรหัส
T. Sar

2

มันเป็นความท้าทายที่สมดุล

ดีกว่า

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

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

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

การบำรุงรักษาที่ตามมาจะช่วยเกินไปเพราะการตั้งชื่อเพิ่มเติมจะช่วยทำให้รหัสตนเอง documenting

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

จนกว่ามันจะดีเกินไป

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

  • มีการโอเวอร์โหลดมากเกินไปที่ไม่ได้เป็นตัวแทนของลายเซ็นทางเลือกสำหรับตรรกะที่จำเป็นเหมือนกัน แต่เป็นสแต็กการโทรคงที่เดียว
  • คำพ้องใช้เพื่ออ้างถึงแนวคิดเดียวกันซ้ำ ๆ ("การตั้งชื่อเพื่อความบันเทิง")
  • การตั้งชื่อที่ทำลวก ๆ มากมายเช่นXxxInternalหรือDoXxxโดยเฉพาะอย่างยิ่งหากไม่มีการรวมแบบแผนในการแนะนำสิ่งเหล่านั้น
  • ชื่อที่เงอะงะนานกว่าการใช้งานตัวเองเช่น LogDiskSpaceConsumptionUnlessNoUpdateNeeded

1

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

แต่บางครั้งฉันมีวิธีสาธารณะขนาดใหญ่ที่สามารถแบ่งออกเป็นขั้นตอนเล็ก ๆ

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

ทำไมไม่สลายตัวด้วยวิธีส่วนตัว? นี่คือสาเหตุบางประการ:

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

แต่ฉันกังวลว่าการบังคับให้ทุกคนที่อ่านวิธีการข้ามไปยังวิธีส่วนตัวต่าง ๆ จะทำให้การอ่านง่ายขึ้น

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


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

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

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