แยก source code เพื่อจัดระเบียบ

Posted

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

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

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

ผู้เขียนใช้ภาษา C ในการเขียนโปรแกรม เดิมเขียนโปรแกรมรวมอยู่ในไฟล์เดียว ตัวอย่าง คือ ไฟล์ mplayer.c ก็ #include และ อื่นๆ ไว้ที่เดียว และเดิมก็ประกาศ structure ไว้ในไฟล์ mplayer.c ด้วยซ้ำไป ต่อมาเห็นว่าโปรแกรมเริ่มเยอะและแก้ไขยากจึงได้แบ่งไฟล์ออกเป็นส่วนๆ ไปและเก็บไว้ในโฟลเดอร์ชื่อ src ส่วนหน้าจอ gui ก็ใช้โปรแกรม glade ในการออกแบบทำให้งานเร็วขึ้น มิฉะนั้นต้องเดาเอาท์พุทขณะเขียนคำสั่งไว้ใน mplayer.c และทำให้โค้ดมีความยาวมาก และปรับแต่แก้ไขหน้าจอยากพอสมควร
รูปที่ 1: หน้าจอรวมทุกสรรพสิ่งไว้ในโค้ดเดียวเท่านั้น

แยก sourcecode


โครงสร้างไดเร็คทอรี

  • src
  • ui

โดย src จะเก็บไฟล์ source code ทั้ง .c และ .h ส่วน ui เก็บไฟล์ glade ที่เป็นรูปแบบของ xml สำหรับสร้างหน้าจอไว้ใช้ในโปรแกรม โดยปกติหากอ้างอิงกับไฟล์ xml โดยตรงก็ทำได้ง่ายๆ แต่มักจะมีปัญหาต้องเอาไฟล์ glade ไปด้วย ทั้งไม่สะดวก และถูกแก้ไขหน้าจอได้ง่าย จึงใช้วิธีการคอมไพล์ glade ให้เป็น resources แล้วรวมเข้ากับโปรแกรม

การแยกไฟล์ของภาษา C มีสองแบบ แบบแรกแยกไฟล์ที่เป็น prototype หรือเป็นส่วนการประกาศค่าต่างๆ เพื่อแจ้งให้โปรแกรมรู้ว่าส่วนอื่นๆ อยู่ตรงไหนบ้าง ผ่านการใช้งาน #include

#include คือ การนำเอาข้อมูลจากไฟล์แยกมารวมไว้ในโปรแกรมเพื่อให้โปรแกรมค้นหาได้ว่าตัวแปร ฟังค์ชันอะไร อยู่ตรงไหน

สำหรับไฟล์ .h ต้องผ่านการ #include เท่านั้น ส่วนไฟล์ .c จะ #include ในส่วนหัวโปรแกรมหรือจะระบุตอนคอมไพล์ได้

สิ่งที่ควรทำ


แยกหมวดหมู่ฟังค์ชันตามหน้าที่ ในตัวอย่างที่ผู้เขียนทำไว้ แยกเป็น:

  • ส่วนที่เป็น callbacks สำหรับเชื่อมต่อสัญญาณต่างๆ ในโปรแกรม เช่น on_btnPlay_clicked
  • ส่วนจัดการไฟล์ในระบบ เช่น read_files_from_directory
  • ส่วนจัดการฐานข้อมูลในโปรแกรม เช่น load_playlist_from_database
  • ส่วนการเล่นเพลง เช่น play, play_next_song
  • ส่วนของโปรแกรม เช่น โหลด ui

.h คืออะไร
ภาษา C จะมีไฟล์ที่มีนามสกุล หรือลงท้ายด้วย .h หมายถึง header file มีไว้เพื่อกำหนดค่าหรือระบุสิ่งแวดล้อม ข้อมูลที่เกี่ยวข้องในระบบ เช่น ตัวแปร structure และ prototype (เก็บรูปแบบของฟังค์ชันที่อยู่ในไฟล์อื่นๆ)

การใช้งาน
วิธีการใช้งานไฟล์ .h นั้นให้ใส่ไว้ตอนต้นของโปรแกรมด้วยคำสั่ง #include หรือ #include "ชื่อไฟล์.h" สังเกตนิดหนึ่งว่าทั้งสองแบบต่างกันตรงที่เครื่องหมาย <> และ "" โดยแบบแรก <> หมายถึงไฟล์ .h นั้นเป็นของระบบอยู่ในส่วนของ libraries ที่ติดตั้งไว้ และอันที่สอง "" หมายถึงไฟล์ .h ที่อยู่ในไดเรคทอรี่ที่กำหนด ถ้าไม่กำหนดก็หมายถึงไดเรคทอรีเดียวกันกับโปรแกรม

ตัวอย่างเช่น
ไฟล์ mplayer.h
รูปที่ 2: ไฟล์ mplayer.h เป็นไฟล์หลักที่จะถูกเรียกใช้ครั้งแรกเสมอ

สังเกตว่าในไฟล์ mplayer.h จะมีการเรียก #include และ และอื่นๆ ซึ่งปกติแล้วเราต้องเรียกส่วนนี้ใน mplayer.c แต่ตอนนี้เราแยกส่วนการประกาศมาไว้ที่ mplayer.h ดังนั้นเราจึงประกาศการ include ส่วนนี้ไว้ใน mplayer.h แทน และใน mplayer.c ก็ include ไฟล์ mplayer.h แทน

ไฟล์ mplayer.c
รูปที่ 3: หน้าจอหลักของโปรแกรม mplayer จะเรียก mplayer.h ก่อน


บรรทัดแรก #include "mplayer.h" เป็นการเก็บข้อมูลของโปรแกรมที่รันใน mplayer.c และ #include "callbacks.h" คือ ฟังค์ชันที่ใช้ในโปรแกรม callbacks.c ขอยกตัวอย่างไฟล์ callbacks.h และ callbacks.c มาอธิบายดังนี้

ไฟล์ callbacks.h:
รูปที่ 4: หน้าจอแสดงไฟล์ callbacks.h

ในไฟล์ callbacks.h ตามในรูปที่ 2 จะเป็นการประกาศฟังค์ชันที่มีอยู่ในไฟล์ callbacks.c ซึ่งอยู่ที่ไหนสักแห่ง เวลาคอมไพล์เราต้องระบุอีกครั้งหนึ่ง
จากในรูป 2 ให้สังเกตว่ามีการเรียก #include "mplayer.h" อีกครั้งหนึ่ง นั่นเป็นเพราะใน callbacks.h มีการเรียกใช้ structure AppData ซึ่งมีประกาศไว้ในไฟล์ mplayer.h

ไฟล์ callbacks.c:
รูปที่ 5: หน้าจอแสดงฟังค์ชันใน callbacks.c ซึ่งก็เหมือนประกาศไว้ใน callbacks.h

ในไฟล์ callbacks.c จะเรียกหลายๆ ไฟล์ในส่วนของ header นั่นเป็นเพราะในฟังค์ชันที่ประกาศด้านล่างนั้นมีเรียกใช้งานหลายๆ ส่วน เช่น แต่ละฟังค์ชันจะส่ง AppData *data ซึ่งเป็น structure ที่มีอยู่ใน mplayer.h และประกาศในส่วนของ sqlitedb.h เพราะฟังค์ชัน callbacks ชื่อ on_window_destroy มีการเรียกฟังค์ชันบันทึกข้อมูลที่มีอยู่ในไฟล์ sqlitedb.c และเพิ่มส่วน audioplay.h ไว้เพราะฟังค์ชัน callbacks ของปุ่มกดต่างๆ มีเรียกใช้ฟังค์ชันเล่นเพลง ที่มีอยู่ในไฟล์ audioplay.c นั่นเอง

และให้สังเกตดีๆ ส่วนหัวของไฟล์ .h ทุกไฟล์จะต้องมี
#ifndef ...
#define ...
และส่วนท้ายสุดต้องมี
#endif /* ... */

คำสั่งเหล่านี้กำหนดขึ้นเพื่อให้ระบบตรวจสอบก่อนว่ามีการโหลด header เหล่านั้นหรือยัง ถ้ายังก็โหลดเพิ่ม โดยจะต้องกำหนดชื่อ header เป็น Capital Charactor หรืออักษรตัวใหญ่ของภาษาอังกฤษ และตามด้วย _H

ไฟล์โดยรวมของระบบ
รูปที่ 6: รูปแสดงไดเร็คทอรี่ที่เก็บไฟล์ของ src และ ui


ไฟล์ gresources.xml
รูปที่ 7: รายละเอียดไฟล์ gresources.xml

ในรูปที่ 7 สังเกตว่าภายในจะกำหนดให้ใช้ /ui นำหน้าชื่อไฟล์ที่โหลดในระบบ ในที่นี้หากจะอ้างอิงต้องเรียก /ui/mplayer.glade (ดูตัวอย่างการเรียกใช้งานในไฟล์ mplayer.c)


Author
Categories gtk