Designs patterns in GoLang

Designs patterns in GoLang

รวม design patterns ที่ยังสามารถใช้ได้ดีในปัจจุบันกับภาษา Go
Chantachat Choedsuwan
Chantachat Choedsuwan
Software Engineer

Share this?

Key Highlights:

  • Design patterns คือแนวคิดในการแก้ปัญหาของการออกแบบซอฟต์แวร์ ซึ่งปัญหาในการเขียนโปรแกรมนั้นส่วนใหญ่จะเป็นเรื่องเดิม  ซ้ำ  ที่เหล่าโปรแกรมเมอร์ทั่วโลกประสบพบเจอกัน จึงได้เกิดกลุ่มบุคคลที่รวมตัวกันเพื่อแก้ไขปัญหานี้ หรือเรียกพวกเขาว่า Gang of Four (GoF) และพวกเขาได้เขียนหนังสือรวบรวมวิธีแก้ไขปัญหาต่าง  ไว้ในหนังสือที่มีชื่อว่า Design Patterns: Elements of Reusable Object-Oriented Software

จากหนังสือที่เขียนมานั้นตั้งแต่ปี 1994 ซึ่งปฏิเสธไม่ได้เลยว่า Design patterns นี้ได้ช่วยเหลือให้เหล่าโปรแกรมเมอร์มากมาย แก้ปัญหามาอย่างยาวนาน และในวันนี้เราจะมาดูกันครับว่า Design patterns แบบไหนที่ยังสามารถแก้ไขปัญหาได้ดีในปัจจุบันโดยเฉพาะอย่างยิ่งในมุมมองของภาษา Go และจะขอหยิบยกมาพูดถึงในบทความนี้ครับ

Builder

Builder หรือชื่ออื่น ๆ ที่เรียกกัน เช่น functional options หรือ fluent interfaces ยังฮอตฮิตตดลมบนมาจนถึงปัจจุบัน ปัญหายุ่งยากในการสร้าง object ที่มีความซับซ้อนจะหมดไป ด้วยเจ้าสิ่งนี้นี่เอง โดยในบทความนี้จะขอยกตัวอย่างการใช้งาน builder เพื่อสร้าง *http.Request object

และนี่คือเจ้าตัว builder ของเราซึ่งเรากำหนดให้ input เป็น url เพราะส่วนประกอบอื่นๆ เราสามารถทำการตั้งค่าเริ่มต้นได้ สำหรับส่วนประกอบตัวอื่นๆ ก็สามารถปรับแต่งได้ผ่านตัว HTTPBuilder ที่เป็น interface นั้นเอง

ในตัวอย่างการใช้งานเราสามารถเรียกใช้งานการสร้าง http.Request ง่ายๆ ได้ด้วยการเรียก NewBuilder และ input url เข้าไปได้เลย หากอยากเพิ่ม header ก็เพียงแค่เรียก .AddHeader() ตาม และต่อด้วย .Build() เพียงเท่านี้เราก็ได้จะ object ของตัว http.Request แบบเขียนง่าย อ่านง่าย สบายตา

Abstract factory/factory method

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

ยกตัวอย่างง่ายๆ เราจะทำการทดสอบระบบซึ่งต้องทำการเปิดการเชื่อมต่อของ socket ไปที่ service อื่น ถ้าเราใช้ตัว net.Dialer โดยตรง มันจะทำให้เราทดสอบระบบได้อย่างยากลำบาก แต่ถ้าเราสามารถสร้าง object ของ net.Conn การทดสอบระบบก็จะเป็นเรื่องที่ง่ายขึ้นดังตัวอย่างต่อไปนี้

เราต้องทำการจำลอง net.Addr กับ net.Conn และทำการคืนค่า struct และ factory property ใน socketClient จะมีหน้าตาแบบนี้ func(ctx context.Context, network, address string) (net.Conn, error) ซึ่งเหมือนกับ net.Dialer เป๊ะเลย เพียงเท่านี้ในการทดสอบระบบก็จะทำได้ง่ายขึ้น

Adapter

อีกหนึ่งสิ่งที่นิยมใช้กันอย่างแพร่หลายในปัจจุบันนั้นคือ เจ้า adapter ของเรานั้นเอง ตัวอย่างที่เห็นได้ชัดเจนคือ เจ้าตัว database driver ที่สร้างจาก driver.Driver interface กับ registers กับ database/sql ซึ่งไม่ได้สนใจเลยว่าจะใช้งาน database เจ้าไหนเลย

สามารถศึกษาเพิ่มเติมได้จาก go-clound

Decorator

สามารถนำ decorator มาใช้งานเพื่อทำการ เพิ่ม/ลด ความสามารถให้กับ object ขณะ runtime ซึ่งจะทำการสร้าง wrapper class ที่สามารถเพิ่มความสามารถเข้าไปได้เรื่อยๆ และยังสามารถนำ wrapper class มา wrap ตัว wrapper class ที่เราต้องการความสามารถนั้นๆ

ในตัวอย่างที่ยกมาเราจะทำการเพิ่ม buffer ไปที่ io.Reader ซึ่งวิธีนี้ไม่ใช่วิธีการจูนประสิทธิภาพแต่เป็นตัวอย่างที่จะทำให้เห็นภาพที่ชัดเจนขึ้น

Facade

คือทำให้ของที่ใช้งานยากๆซับซ้อนๆ สามารถใช้งานได้แบบง่ายๆ โดยการ สร้าง class ที่ทำงานกับ subsystem ที่วุ่นวายๆ แล้วจัดการเรื่องที่ client ต้องเรียกใช้ทั้งหมด สร้างช่องทางให้ client เรียกใช้งานแบบง่ายๆ

ศึกษาตัวอย่างดีๆ ได้ที่ go-cloud 

Proxy

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

Chain of responsibility and Command

หนึ่งแบบดีอยู่แล้ว รวมสองแบบดียิ่งกว่า!!!

และนี่คือการรวมร่างกันระหว่าง Chain of Responsibility และ Command ไปดูตัวอย่างกันเลย

และนี่คือ command ของเราที่เอาไว้จัดการกับ HTTP Request ซึ่งมันไม่สำคัญว่าเราจะเขียนโค้ดออกมาแบบไหนเพียงแค่ว่ามี signature เหมือนกันก็พอ

ทั้งสองตัวอย่าง สามารถใช้งานแทนกันได้ สิ่งที่สำคัญคือ input และ output จะต้องเหมือนกัน ซึ่งทั้งสองตัวอย่างทำการรับค่า http.ResponseWriter และ http.Request พร้อมทั้งไม่มีการรีเทิร์นค่ากลับไปเหมือนกันนั้นเอง

มี Command แล้วเจ้า Chain of Responsibility อยู่ไหนละ อยู่ที่ Middlewares หรือเปล่านะ ไปดูกันต่อเลยดีกว่า 

เกือบจะเหมือนกับตัว http.Handler แต่มีบางอย่างที่แตกต่างนั้นคือการรับพารามิเตอร์ next นั้นเอง ลองไปใช้งานกันดีกว่า ว่าจะเป็นอย่างไร

ในตอนนี้เราได้ทำการใช้งาน MiddlewareToHandler method เพื่อทำการเพิ่ม log จาก middleware ไปยัง OkHandler เราสามารถทำการเพิ่มฟีเจอร์ใน middleware อีกเท่าไหร่ก็ได้ โดยที่เราไม่ต้องไปเปลี่ยนตัว command เลย ในตัวอย่างมี middleware ตัวเดียว หากต้องการศึกษาให้ลึกและระเอียดมากกว่านี้ตามไปที่ gorilla/mux ได้เลย

Iterator

ก่อนอื่นต้องยอมรับก่อนว่าในภาษา Go เองนั้นไม่มีไลบรารี่เหมือนภาษาอื่น ๆ สำหรับเจ้าตัว iterator นี้ แต่สิ่งที่ทำคือ สร้างตัว iterator ขึ้นมาใช้งานโดยเฉพาะ ซึ่งในเวอร์ชั่น 1.18 จะมีเจ้าตัว generics เข้ามาก็จะทำให้ชีวิตง่ายขึ้น ไม่ต้องทนใช้ for…range อีกต่อไป สำหรับเจ้าตัว iterator ที่ฮิตฮอตที่สุดใน go นั้นก็คือ sql.Rows นั้นเอง ซึ่งทุกคนต้องผ่านหู ผ่านตากับการจัดการ SQL ใน Go

Observer

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

Strategy

อย่างที่ทราบกันดีว่า Strategy pattern นั้นเป็นการสร้างการทำงานหลาย ๆ แบบ แยกออกเป็น class และมี interface ร่วมกัน ยกตัวอย่างเช่นการจัดเรียงข้อมูล ไม่ว่าจะเป็น merge sort, bubble sort, quick sort เราสามารถนำมาประยุกต์ใช้งานได้โดยการสร้างเพียง method เดียวและทำการกำหนด signature ให้เหมือนกันนั้นเอง

จากตัวอย่างจะให้ได้ว่าเราไม่จำเป็นจะต้องแยก class ของแต่ละอัน 

Template Method

ในปัจจุบันสิ่งที่ใช้กันอยู่พูดได้ว่า  Template Function มากกว่า ซึ่งจะมี abstract method  ไว้พร้อมใช้งานใน sub class ซึ่งนั้นก็คือ function นั้นเอง สำหรับตัวอย่าง Template Method ในบทความนี้จะขอใช้การ Sorting objects

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

ยังมี Design patterns แบบอื่น ๆ ที่ไม่ได้ถูกยกมาพูดถึงในบทความนี้อีกมากหมาย ซึ่งไม่ได้หมายความว่า แบบอื่น ๆ จะใช้ไม่ได้แล้วในภาษา Go 

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

 

หากผู้เขียนอธิบายส่วนไหนไม่เข้าใจหรืออธิบายผิด กราบขออภัยไว้ ณ ที่นี้ ด้วยครับ

Chantachat Choedsuwan
Chantachat Choedsuwan
Software Engineer

Share this?

More from this Author?

Get in Touch

Get in Touch

Sign up to our newsletter

Sign up to our newsletter

Thank You for reaching out to us!

Thank You
for reaching out to us!

We’ll get back to you as soon as we can.

Thank You for signing up to our newsletter!

Thank You for signing up
to our newsletter!

We’ll serve you with amazing stories.