เหม็นกว่าตด ก็ Code Smell ใน Project นะครับเอ่อ

Thanawat Masileerungsri
6 min readAug 17, 2019

--

ก่อนที่จะเข้าเนื้อหา Code Smell ทั้ง 7 + 1 เรามาอ่านเกล็ดความรู้เล็กๆน้อยๆกัน

อาการตด
ภาพจำลอง กริยา ตด : verb to fart

ตด คือ การผ่ายลมของร่างกายที่เกิดจาก Gas ในลำไส้ กระเพาะ และทางเดินอาหารของเรา โดยปกติคนเราจะตดประมาณ 14–23 ครั้งต่อวัน และมักจะมีกลิ่น ซึ่งกลิ่นตดนั้นจะขึ้นอยู่กับโปรตีนที่เรารับประทานเข้าไป (เช่น เนื้อสัตว์ ไข่ ชีส และนม และถั่วชนิดต่างๆ เป็นต้น) เมื่อโปรตีนถูกย่อยจะเกิดแก๊สสะสมไว้ภายในลำไส้ และกลิ่นของมันนั้นเหม็น ถึงเหม็นมาก ในทำนองเดียวกัน code smell คือ การตดของ software ที่เวลาเราเข้าไปกราดสายตามอง ตรวจตรา หรือไปแก้ไข แล้วจะได้กลิ่นเหม็นๆของโค้ด ( ผิดๆ เป็นกลิ่นตดของ คนที่เขียนโค้ดนี้ไว้ เอ่ย ถูกแล้ว) แล้วเราจะสังเกตได้อย่างไรว่า โค้ดที่เห็นอยู่นั้น มีกลิ่นเหม็นๆอยู่

(TL;DR)เนื้อหาอย่างเป็นทางการอยู่ย่อหน้าที่ 4

วิธีการสังเกต

มาตรวจดูในโปรเจคของคุณกัน : Keep looking @ your project.

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

  1. อาการปวดหัว เพียงแค่คุณผู้ชมเริ่มไล่ Logic แล้วพบว่า code ไรหว่ะ อ่านแล้วไม่เข้าใจได้อย่างทันทีเลยนะ เวลา Interpret ก็เกิดอาการ หน้ามืดตามัว คล้ายๆคนกำลังจะเป็นลม
  2. อาการตาลาย ถัดมา นอกจากอาการปวดหัวเฉียบพลัน ก็มีอาการแทรกซ้อนคือ ตาลาย ตาหวานเยิ้มเลย เรียกได้ว่า หวานดั่งน้ำผึ้งเดือน 5 เพราะ เห็นแล้วก็มีความรู้สึก 555 แล้วมีน้ำตาซ่อนอยู่ ไม่รู้ทำไม
  3. อาการประหลาดใจ เรียกได้ว่า เหมือนเจอแก้วกาแฟที่มีชื่อนึงเขียนอยู่ จนแทบ อยากจะเอามือมาแทบอกแล้วเรียกชื่อบนแก้วกาแฟนั้นเบาๆ K.พระ เช่นโค้ดทำงานเหมือนกัน แต่ implement หลายๆแบบ ใน project เดียวกัน
  4. อาการโคลิด คือ ร้องไห้อย่างหนักโดยไม่ทราบสาเหตุและไม่สามารถกล่อมให้หยุดร้องไห้ได้ โค้ดไรหว่ะ งงกับความบันเทิงของโค้ด ร้
  5. อาการปลงไว้อาลัย เกิดเป็น developer สายบุญขึ้นมาทันทีกับการที่เข้าใจเรื่องสัจธรรม นานาจิตตัง ขอให้โค้ดทำงานได้อย่างปกติ ด้วยเถอะ

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

(โปรดใช้วิจารณญาณในการอ่าน ผู้อ่านควรศึกษาวิชาการเพิ่มเติม ก่อนตัดสินใจบำบัดอาการด้วยตนเอง)

วิธีการรักษาอาการ

จัดการอย่างไรมาดูกัน : How to relief is here!
  1. ระบายความคิดต่างๆ เพื่อให้จิตสงบ ลบสิ่งที่กวนจิตกวนใจออกให้หมด
  2. ทำจิตให้คงมั่น เพ่งสมาธิ แล้วปลุกจิตวิญญาณ ของ Developer ที่ซ่อนอยู่ในตัวคุณขึ้นมา
  3. มองหาวิธีการแก้ไข โดยเราจะต้องแยกแยะ กลิ่นของ code ออกมาก่อน ว่า code นั้นจัดอยู่ในประเภทไหน ถ้าแยกได้แล้วก็เอา How to มาขจัดกลิ่น

Code Smells ที่มักเจอบ่อย ซึ่งสามารถแก้ไขได้โดยง่าย

มาสูดดมกลิ่น Code กัน ฉันต้องการ refactoring ด่วนๆ : Try to breath on code smell, but I want some fresh air!

Smell เหล่านี้หาเจอได้จากการ สังเกต เท่านั้น หากใครมีญาณทิพย์ หรือมีลางสังหรณ์ โปรดหลังไมค์ด้วยครับ อยากมีความสามารถพิเศษนี้ติดตัวเหมือนกัน ซึ่งมีความเชื่อว่าหากเราเปิด code ใน Project ของเราดู อาจจะเจอหรือไม่เจอก็เป็นได้ โดยขึ้นอยู่กับ Pattern และ Code review ของ Project เพื่อให้ไม่เสียเวลาเรามาดู Code Smells ทั้ง 7+1 กัน

1. Duplicate code

ใครเคยเจอโค้ดที่เขียนซ้ำๆกัน เห็นโค้ดเดียวกันอยู่หลายๆที่บ้าง ถ้าเคยก็ยินดีด้วย คุณมีโอกาสเป็นฮีโร่ เพื่อพิชิตโค้ดอัปลักษณ์นี้แล้ว (เสียงแองเจลีน่าโจลี่ ยัยโค้ดอัปลักษณ์ — Maleficent)

ข้อเสีย Duplicate Code : หากเราต้องแก้ไขโค้ดให้มีการทำงานเปลี่ยนไป วิธีแก้ของเราคือ ไล่แก้โค้ดที่ซ้ำๆกันทุกจุด แล้วทีนี้ความบันเทิงจะอยู่ที่ เราอาจะลืมแก้ไขโค้ดบางจุดไป ทำให้ Program มีบุพเพสันนิวาสกับบัคกี้ได้ หรือหากเราไม่ได้เขียนเทสครอบไว้ เราจะมั่นใจได้อย่างไรว่า โค้ดที่แก้ไปนั้นไม่มีข้อผิดพลาด Do it with your own risk

วิธีจัดการ : วิธี basic สำหรับ Duplicate code คือ การ Extract Method โดย group ของที่ duplicate กันเป็น Method เพื่อให้เกิด single contract หรือ single spec หากเวลามีการเปลี่ยนวิธีการคิดจะสามารถทำให้แก้ได้ทันที

ตัวอย่าง :

ตัวอย่าง Duplicate Code

ดูจากตัวอย่างแล้ว เห็นวิธีการ เขียนโค้ดที่มีความซับซ้อนเหมือนกัน แต่โค้ดที่อยู่ใน method solve() จะวิวัฒนาการโดยรวบ logic สำหรับการหาผลลัพท์ของการคำนวณราคาสินค้าพร้อมส่วนลด ไปอยู่ใน calcluatePriceWithDiscount() ของ Class Product

2. Long Parameter List

ยินดีต้อนรับเหล่าขบวนพารามิเตอร์ : Welcome to method with long parameters

ใครเคยเจอ method ที่มี parameter ยาวๆ นับตั้งแต่ 4 ตัวขึ้นไป จนเป็น กิโล นับว่าเป็น Long Parameter ไม่นับตรง constructor method นะครับ หลายคนอาจจะสงสัยว่าโค้ดที่ตัวเองเขียนอยู่นั้น จะเข้าค่ายด้วยหรือเปล่านะ เอ้ยเข้าข่าย มาลองดูตัวอย่างกัน

ข้อเสีย Long Parameter List : เวลา call method ต้องมานั่งจัดลำดับในการใส่เข้า method หรือ หากมีการปรับ signature ของ method จะน้ำตาตกในได้ และการที่ต้อง Pass ของเยอะๆ ยาวๆ อาจจะเผลอสลับลำดับ หรือใช้เวลาในการใส่ parameter นานเกิน หรือ อาจจะทำให้การ debug error ได้ยาก

วิธีจัดการ : Grouping เป็น object แล้วส่งเป็น Object parameter แทน

ตัวอย่าง :

ตัวอย่าง Long Parameter List

จะเห็นว่ายิ่งยาวยิ่งอ่านเป็นไงครับ เยอะเน๊อะ แล้วพอมาจับมัดเป็น Grouping Parameter ไปแล้วเป็นไงครับ กุ๊กๆๆๆๆๆ กันเลยทีเดียว 555+ (กุ๊กๆ = ไก่, ไก่ = ง่าย)

3. Long Method

ยิ่งเมธอดยาวเท่าไหร่ ยิ่งใช้เวลา track ยาวเท่านั้น : The longer method , The more wasted time consuming

อันนี้เป็นอีกเคส ที่เจอได้บ่อยมาก ลองคิดเล่นๆกันว่า Method นึง เราควรจะมีโค้ดอยู่กี่บรรทัด หากใครเคยเจอ โค้ดมากกว่า 30 บรรทัดขึ้นไป คุณรู้สึกบ้างไหมครับว่ามันไม่น่าอ่าน มันไม่น่าเข้าไปสำรวจเลยจริงๆ สำหรับโค้ดไม่เกิน 30 บรรทัด อาจจะน้อยไปหน่อย จริงๆผมเคยเจอโคตะระ method ที่มีความยาวกว่า 3,000 บรรทัดมาแล้ว ขอบอก (โม้จริงๆเรา) ความประทับใจในการ debug นั้นยังจำได้ดี “( — . — )”

ข้อเสีย Long Method : คือเวลาแก้โค้ด หากเจอโค้ดที่เขียนไว้หลายบรรทัด อาจจะมี side effect หลายๆอย่างที่ซ่อนอยู่ หากจุดที่เราแก้นั้นคือจุดเริ่มต้นของ method ซึ่งมีการปรับ stage value เรื่อยๆ จนถึงท้าย method ซึ่งมีความอันตรายอย่างมาก และอาจเกินอาการตาพร่ามัว ทำให้เหล่า mini Bug เกิดขึ้นใหม่อย่างมากมาย

วิธีจัดการ : พยามแบ่งการทำงานภายใน method ให้ไม่เกิน 30 บรรทัด บอกง่ายจังแต่ทำยากจุง Don’t be serious! ทำบ่อยๆ เดี๋ยวก็ทำได้เอง

ตัวอย่าง :

ตัวอย่าง Long Method

หลายๆครั้ง เราเองก็อาจจะเผลอเขียน code ยาวๆ โดยที่มั่นใจว่าโค้ดอ่านง่าน อ่านรู้เรื่องแต่พอเอาเข้าจริง เวลาที่เราย้อนกลับมาอ่านอีกที สิ่งที่เราเคยสร้างเป็นไว้เป็นพงศาวดาร นั้น อาจจะอ่านไม่ง่านเสมอไป ฉะนั้นเราควรแบ่งหน้าของของ function ออกมาอย่างชัดเจน และอย่าลืม Concept ของ function / method คือ

1 หน้าที่ 1 เมธอด : One Duty One Method”

4. Data clumps

ความหมายของคำว่า Clump จาก Google Translate

อารมณ์คล้ายๆกับ ‘โดนัลด์ ทรัมป์’ หรือเปล่า …. ไม่ใช่ครับบบบ มันคนละอย่างกัน อันนี้คือ Data clumps หากดูความหมายของ Clump ซึ่งหมายถึง กลุ่ม ก้อน หรือ กระจุก ซึ่งแปลได้ว่า กระจุกก้อนข้อมูล โดยหากเรามีการ Pass data แล้วเอามาทำงานบางอย่างซึ่งไม่สมควรอยู่ใน method นั้นเลย เพราะควรจะเป็นหน้าที่บางอย่างของ parameter ที่ pass เข้ามา ซึ่งเราสามารถ generic ขึ้นมาได้

Note : Data Clump จะไม่เหมือนกับ Long Parameter List ตรงที่ Data Clump จะหมายถึงกลุ่ม Data ที่เวลา pass ไปหลายๆ method จะส่งไปเป็นกลุ่มซึ่งมีความสัมพันธ์อย่างมีนัยยะบางอย่าง หรือ กลุ่ม data ที่มักโดนเรียกเพื่อ Get ค่าออกไปพร้อมกัน
ส่วน Long Parameter List นั้นจะเป็น กลุ่มของ parameter ที่ยาวจนเกินไป ซึ่งบางที parameterแต่ละตัวไม่ได้เป็นคุณสมบัติของกันและกัน หรือเป็น Object เดียวกันอย่างเห็นได้ชัด

ข้อเสีย Data clumps : หากเรามีการ Pass parameterที่เป็นกลุ่มแบบนี้ (หรือ การเอา parameter ไปประกอบของใน method ที่ Pass parameter กลุ่มนี้ไปเสมอ)ไปทุก method ซึ่งเขียนคล้ายๆกัน จะกลายปัญหาทันที หากเรามีการเปลี่ยนลำดับ parameter, เปลี่ยน type ของ parameter, การเปลี่ยนชื่อ parameter ที่รับไปใช้งาน หรือ การตัดค่าที่ไม่ได้ใช้งานแล้ว ซึ่งจะต้องแก้ code หลายๆจุด ที่ใช้งาน ทั้งนี้หากเราเขียนเทสไว้ด้วย ลองคิดเล่นๆว่า หากต้องแก้ code ที แก้เทสที ต้องแก้กันเป็นทอดๆเลย เปลืองน้ำมันใช่เล่นนะครับ เอ้ยเปลืองเวลาใช่เล่นนะครับ

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

วิธีจัดการ : รวบ data กลุ่มที่เดียวกันให้กลายเป็น class แล้วความงามของ object นั้นจะขจัด Data clumps smell บน source code ของคุณเอง

ตัวอย่าง :

ตัวอย่าง Data clumps

ตัวอย่างด้านซ้ายจะเป็นการสร้าง ClassRoom class ที่สร้างเพิ่มนักเรียนและ แสดงรายชื่อนักเรียนออกมา โดยจังหวะที่เพิ่มนักเรียนเข้าห้องเรียนนั้นจะมีการ ใส่ Properties ของ นักเรียนแต่ละคนเข้าไป ซึ่ง data กลุ่มนี้เองนี่แหละที่มี relationship ระหว่างกัน ในจังหวะนี้ใครที่เล็งเห็นโอกาส ก็จะ refactor code กลุ่มนี้ออกมาให้มีลักษณะตามด้านขวา คือ Extract code ออกไปเป็น class ใหม่เสียเลย

5. Comments

จะว่าใคร comment code ที่ยากๆไว้บ้างงงงงง : Who did comment code here?

ตรงๆตัวเลย อันนี้ comment มีการอธิบายโค้ดด้วย คอมเม้นต์ หากเราจะจับผู้ร้ายละก็เปิด code แล้ว annotate หรือ blame กันไปเลย แต่ในความเป็นจริง เราแทนที่จะ blame กันเอง เรามาหาทางออกที่สร้างสรรค์กันดีกว่า อาจจะร่วมด้วยช่วยกันหาวิธีแก้ปัญหานี้กัน

ข้อเสีย Comments : ซึ่งจะเห็นได้ว่าบางอย่างไม่จำเป็นต้องคอมเม้นต์ ก็คอมเม้นต์ โดยหากเราเขียนโค้ดให้ simple และเข้าใจง่าน comment ก็ไม่จำเป็นเลย และจริง code ที่เราเขียนลงไปก็ไม่ควร complicate มาก เพราะหากวันนึงมาอ่าน เราจะไม่เข้าใจเองก็ได้นะ ปล.complicate ในที่นี้ไม่รวมถึง Algorithm ที่หยิบมาใช้นะครับ มันเป็นคนละเรื่องกัน

วิธีจัดการ : เขียนโค้ดให้ simple ที่สุดแต่ไม่ silly (ไม่ฉลาด) อาจจะตั้งชื่อตัวแปร ตั้งชื่อ method ให้สื่อความหมาย เพื่อให้รู้ objective ของ method นั้นๆไปเลยโดยไม่จำเป็นต้อง comment code

ตัวอย่าง :

ตัวอย่าง Comment

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

6. Primitive Obsession

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

ข้อเสีย Primitive Obsession: หากเราต้องการ set ค่าที่เป็น Primitive Obsession หากเราไม่มีการ define ค่านั้นๆไว้เป็น instance กลางจะมีปัญหาเวลา เราต้องเข้าไปแก้ Properties ค่านั้นๆ เช่น จากตัวอย่างหากเขตมีการเปลี่ยนเลข Zip code ไป เราจะต้องไปไล่แก้ ค่า Zip code นั้นของทั้ง Project เลย

วิธีจัดการ : สำหรับวิธีการจัดการ Primitive Obsession ทำได้หลายวิธีเลย เช่น การทำ class enum ขึ้นมาเพื่อแทนค่านั้นๆ หรือ การสร้าง static value ขึ้นมาแทนค่า แล้วให้ใช้ตัวแปลนั้นแทนการใส่ค่าแบบตรงๆ

ตัวอย่าง :

บางที่การที่เรา define ของบางอย่างไว้ ณ วันที่เรา เดบิวท์ (debut) โค้ดของเราออกไปนั้นทุกอย่างยังจำได้อยู่หรอก แต่ Once ที่เราหวนกลับมาแก้ไข บางทีเราก็อาจจะลืม context ที่เราเคยทำไว้ก็เป็นได้ ฉะนั้นมาทำให้โค้ดชาวเราๆ cleanๆ กันดีกว่า

อ่านเพิ่มเติม Primitive Obsession ได้ที่นี่ here

7. Middle Man

มิดเดิ้ลแมนผู้สื่อรัก : Middle man who is the connector between of those

ยังไม่เขียนข้อ 7 เด้อ ขอทิ้งไว้ก่อนน่ะครับ ข้ามไป !!!

ขอเวลาอ่านทำความเข้าใจเรื่องนี้เพิ่มเติมนิดนึง เดี๋ยวกลับมาเติมให้สัญญา

8. (ข้อที่7+1) Feature Envy

Feature Envy !!! : I wish I wanna be your boyfriend (feature).

สุดท้ายเรามี method ที่ inspire method ของ Object อื่นๆให้มา call และเรียกใช้งาน จนทำให้โค้ดมีความเกี่ยวข้องกัน (couple) ซึ่งหากมีโค้ดแบบนี้น้อยๆ ยังพอที่จะอนุโลมให้ได้อยู่แหละ แต่หากวันใดที่มีการสะสมโค้ดเหล่านี้จนเป็นกลุ่มก้อนสมบัติละก็ เวลาที่ต้องกลับมาแก้โค้ดละก็ (— / | \ — ) อาจจะไปทำบุญเพิ่มกันเลยทีเดียว

ข้อเสียของ Feature Envy : เวลาที่เรามีสร้าง method ที่เอา class อื่นๆมาประกอบร่างใน method ของ class อื่นนั้น จะเกิดสิ่งที่ control ไม่ได้ในการ scope หา impact เพื่อแก้ไข code ยิ่งมี smell นี้เยอะเท่าไหร่ Project ก็จะมีความเสี่ยงในการ refactoring code หรือ code change มากขึ้นเท่านั้น

วิธีจัดการ : มีหลายวิธี ด้วยกัน แต่วิธีที่ simple ที่สุดสำหรับผมคือ Move method .... ส่วนอีกวิธีก็คือ Extract method

ตัวอย่าง

ตัวอย่าง Feature Envy

ตอนแรกผมก็คิดว่า ไม่เห็นเป็นไรเลยถ้าเราจะเขียนโค้ดแบบด้านซ้าย แต่พอเขียนโค้ดไปซักพักก็รู้สึกได้ว่า โค้ดทางด้านซ้าย หากเราต้องใส่โค้ดแบบนี้ในทุกที่ของ class อื่นๆ จะเริ่มเอ๊ะ นี่มัน duplicate code แล้วก็มีเอ๊ะทำไม class นี้ต้อง implement method นี้ด้วยทั้งๆที่ควรเป็น method ของ class beverage เท่านั้นแหละครับ ความเข้าใจที่ผมมี : D

นาทีทอง

แจก source code Github เข้าไปอ่าน เข้าไปตำได้ที่ https://github.com/ThanawatMas/CodeSmell

เพิ่มเติม
มากันจนถึงข้อสุดท้ายแล้ว อยากให้ทุกคนสำรวจโค้ดใน Project ของตัวกันหน่อยว่าเจอ Code smell ไหนบ้าง ผมอยากชวนให้ลองปรับ code ตรงส่วนที่มี smell ส่วนนั้นกันครับ เพื่อ source code เราจะได้ใสๆ > ,, < และทำให้ 4 ห้องหัวใจเราพองโต เมื่อเราเติบโตขึ้นไป (ภูมิใจที่เราได้เขียน Code ส่วนนั้นขึ้นมา)

นอกจากนี้ยังมี smell อื่นๆอีก ที่ไม่ได้กล่าวไว้ ซึ่งสามารถอ่านได้ที่

https://blog.codinghorror.com/code-smells
https://sourcemaking.com/refactoring/smells

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

รับรองว่าหากใครได้ลองเข้าไปดูแล้วมา share มุมมองกันได้นะครับ มีคำแนะนำจากปรมาจารย์เทพคนนึง กระซิบมาบอกว่า ใครอยากที่จะเขียน code ให้เมพขึ้น ลองอ่าน code open source หลายๆโปรเจค ดู ก็จะเห็นแบบอย่างการเขึยนโค้ดที่เราอาจไม่เคยเขียนมาก่อน แล้วลองนำมาปรับใช้กับงานที่เราทำๆอยู่ดู๊ !”

ท้ายสุดนี้ ขอทิ้งคำคมไว้ดังนี้

กลิ่นตดจางได้ในอากาศ CodeSmellจางได้ด้วยมือคุณ

ปล. หากใครชอบบทความ อย่าลืมกดไลค์ กด subscribe กันด้วยน้าาาา บับบายย

--

--

No responses yet