<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Perajit</title>
    <description>The latest articles on DEV Community by Perajit (@perajit).</description>
    <link>https://dev.to/perajit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3889814%2F9fd0b414-7ecd-47a0-9a41-67960fda3f4d.png</url>
      <title>DEV Community: Perajit</title>
      <link>https://dev.to/perajit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/perajit"/>
    <language>en</language>
    <item>
      <title>Go กับ Concurrency ตอนที่ 2: Channel</title>
      <dc:creator>Perajit</dc:creator>
      <pubDate>Fri, 01 May 2026 01:31:00 +0000</pubDate>
      <link>https://dev.to/perajit/concurrency-ain-go-tnthii-2-channel-417g</link>
      <guid>https://dev.to/perajit/concurrency-ain-go-tnthii-2-channel-417g</guid>
      <description>&lt;p&gt;ตอนที่ 2 ของบทความ &lt;strong&gt;"Concurrency ใน Go"&lt;/strong&gt; เราจะมาพูดถึงวิธีการที่แนะนำให้ใช้สื่อสารกันระหว่างหลายๆ goroutine ซึ่งก็คือ Channel&lt;/p&gt;

&lt;h1&gt;
  
  
  Channel
&lt;/h1&gt;

&lt;p&gt;แนวทางการสื่อสารสำหรับ concurrency ตาม best practice ก็คือ&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Do not communicate by sharing memory; instead, share memory by communicating."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ดังนั้น ในการสื่อสารระหว่างหลาย routine ปกติเราจะไม่แก้ค่าในตัวแปรกลาง แต่จะใช้การรับส่งค่าผ่าน channel แทน&lt;/p&gt;

&lt;p&gt;โดย channel จะเป็นตัวแปรแบบ typed ต้องกำหนดตั้งแต่เริ่มว่าตัวแปรที่จะส่งค่าเป็นประเภทไหน&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;การส่งค่าเข้าไปยัง channel เขียนแบบนี้ &lt;code&gt;c &amp;lt;- v&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;การรับค่าจาก channel เขียนแบบนี้ &lt;code&gt;&amp;lt;- c&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;โดยที่ &lt;code&gt;c&lt;/code&gt; คือ channel, &lt;code&gt;v&lt;/code&gt; คือค่าตัวแปร&lt;/p&gt;

&lt;p&gt;ตัวอย่าง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
  &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จะเห็นว่าไม่มีการใช้ WaitGroup แต่ก็ main ก็ไม่ได้จบไปก่อน นั่นก็เพราะว่า channel มีกลไก blocking อยู่แล้ว มันจะหยุดรออัตโนมัติเมื่อพยายามรับหรือส่งข้อมูล&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;บล็อกการส่งค่าเข้าใน channel จนกว่าค่าที่ค้างอยู่จะถูกรับออกไป&lt;/li&gt;
&lt;li&gt;บล็อกการรับค่าจาก channel รอจนกว่าจะมีค่าส่งเข้ามา&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;หมายความว่า เราสามารถใช้ channel เพื่อการรอในลักษณะคล้ายๆ กับ WaitGroup ก็ได้ เช่น ใช้ channel bool เพื่อรอสัญญาณว่างานจบแล้ว&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ready&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ready&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;มันจะรอจนกว่า &lt;code&gt;foo()&lt;/code&gt; จะส่งค่า true เข้ามาใน &lt;code&gt;ready&lt;/code&gt; (ซึ่งจริงๆ จะส่ง false ก็ทำงานได้ แต่ตามหลักควรส่ง true)&lt;/p&gt;

&lt;p&gt;สังเกตว่าเราไม่ได้รับค่าออกไปใช้เลย เราแค่รอให้มีการส่งค่าเข้ามาใน channel เฉยๆ&lt;/p&gt;

&lt;p&gt;ในการใช้งาน channel ในการรับค่าเป็น stream ปกติแล้วเรามักจะวนรับค่าโดยใช้ &lt;code&gt;for range&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sending"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Millisecond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Receiving"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Done"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;close(c)&lt;/code&gt; คือการสั่งปิด channel เพื่อไม่ให้ลูปของเราค้างรอค่าไปเรื่อยๆ เนื่องจากในฝั่งผู้รับเราใช้ &lt;code&gt;for range&lt;/code&gt; ซึ่งจะไม่จบลูปจนกว่า channel จะปิด (กรณีที่ลูปเป็น 1:n ก็ไม่จำเป็นต้องสั่ง &lt;code&gt;close()&lt;/code&gt; เพราะมีจำนวนลูปที่แน่นอนอยู่แล้ว)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;หมายเหตุ&lt;/strong&gt;: ให้สั่งปิด channel ที่ฝั่งผู้ส่งเมื่อฝั่งผู้รับจำเป็นต้องรู้ว่าข้อมูลหมดแล้ว การสั่งที่ฝั่งผู้รับจะเสี่ยงเกิด Panic ถ้าฝั่งผู้ส่งยังพยายามส่งข้อมูล&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Buffered Channel
&lt;/h1&gt;

&lt;p&gt;โดย default channel จะไม่มีความจุ (capacity = 0) หมายความว่ารับส่งค่าได้ทีละค่า และมันจะบล็อกการทำงานอย่างที่บอกไป แต่เราสามารถกำหนดค่า capacity หรือ buffer ให้กับ channel ได้ เพื่อให้มันทำงานต่อโดยไม่บล็อกจนกว่า buffer จะเต็ม เราจะเรียก channel แบบนี้ว่า buffered channel&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เลข 3 ที่เรากำหนดคือ capacity (buffer) ของ channel&lt;/p&gt;

&lt;p&gt;ในกรณีนี้&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เราจะส่งค่าให้ channel ได้ 3 ค่าโดยไม่มีการบล็อกแม้จะไม่มีการดึงค่าออกจาก channel เลย แต่จะบล็อกหลังจากการส่งค่าครั้งที่ 4&lt;/li&gt;
&lt;li&gt;ไม่บล็อกการรับค่าจาก channel จนกว่าไม่มีค่าค้่างอยู่ใน buffer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;สำหรับ unbuffered channel ก็เหมือนการับส่งของกันด้วยมือ ส่วน buffered channel จะเหมือนการมีช่องรับของเพิ่มมาให้ฝากของไว้ได้ มีประโยชน์ในเคสที่ผู้รับกับผู้ส่งทำงานด้วยความเร็วต่างกันชั่วคราว เช่น มี producer กับ consumer ที่มีโอกาสที่บางช่วงจะทำงานช้ากว่าหรือเร็วกว่ากัน ถ้าไม่มี buffer เลยก็จะต้องรอกันไปมา การใช้ buffer จะช่วยให้รันได้ต่อเนื่องลื่นไหลขึ้น&lt;/p&gt;

&lt;h1&gt;
  
  
  Channel Direction
&lt;/h1&gt;

&lt;p&gt;ตอนที่ส่ง channel เข้าไปเป็น parameter ในฟังก์ชั่น เราสามารถกำกับทิศทางของ channel ได้ด้วย&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ส่งได้อย่างเดียว&lt;/strong&gt;: &lt;code&gt;func producer(c chan&amp;lt;- int)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;รับได้อย่างเดียว&lt;/strong&gt;: &lt;code&gt;func consumer(c &amp;lt;-chan int)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Select Statement
&lt;/h1&gt;

&lt;p&gt;การใช้ &lt;code&gt;select&lt;/code&gt; จะคล้ายๆ กับ &lt;code&gt;switch&lt;/code&gt; คือควบคุมการทำงานโดยเช็คค่าเงื่อนไข แต่สำหรับ &lt;code&gt;select&lt;/code&gt; จะใช้จัดการกับ channel&lt;/p&gt;

&lt;p&gt;โดยที่ &lt;code&gt;select&lt;/code&gt; จะบล็อกการทำงานไว้จนกว่าจะมี case ใดเคสหนึ่งเป็นจริง&lt;/p&gt;

&lt;p&gt;สามารถใช้ในกรณีอย่างเช่น มี 2 channel ที่เราไม่รู้ว่าค่าจะมาลงที่ channel ไหน แต่เราสนใจแค่ channel ที่ได้รับค่าก่อน&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;c1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;c2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="s"&gt;"A"&lt;/span&gt;
  &lt;span class="p"&gt;}()&lt;/span&gt;

  &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;c2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="s"&gt;"B"&lt;/span&gt;
  &lt;span class="p"&gt;}()&lt;/span&gt;

  &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received from c1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received from c2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;select&lt;/code&gt; จะรอจนกว่าจะมีข้อมูลจาก c1 หรือ c2 (ซึ่งจากโค้ดก็คือ c1) และจะจบการทำงาน เพราะฉะนั้นเราจะเห็นข้อความแค่ "Received from c1"&lt;/p&gt;

&lt;p&gt;หรือเราอาจจะใช้เลือกเคสที่มีการรับหรือส่งค่าก่อน เช่น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;inChan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;outChan&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sent b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;นอกจากนั้นก็ยังมีเทคนิคที่ใช้กันบ่อย เช่น&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;การทำ timeout&lt;/strong&gt;&lt;br&gt;
สำหรับกรณีที่ไม่มีข้อมูลส่งมาในเวลาที่กำหนด เราจะใช้รูปแบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received from c1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;After&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Timeout"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) &lt;strong&gt;การใช้ default&lt;/strong&gt;&lt;br&gt;
สำหรับกรณีที่จะไม่หยุดรอเลย เช่นในกรณีที่ channel ถูกบล็อกอยู่ เพื่อป้องกันไม่ให้โปรแกรมค้าง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received from c1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No message"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) &lt;strong&gt;ใช้เพื่อหยุดการทำงาน&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received from c1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;abortChan&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Abort"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>programming</category>
      <category>go</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Go กับ Concurrency ตอนที่ 1: Basic &amp; Synchronization</title>
      <dc:creator>Perajit</dc:creator>
      <pubDate>Fri, 01 May 2026 01:25:55 +0000</pubDate>
      <link>https://dev.to/perajit/concurrency-ain-go-tnthii-1-basic-synchronization-2jjc</link>
      <guid>https://dev.to/perajit/concurrency-ain-go-tnthii-1-basic-synchronization-2jjc</guid>
      <description>&lt;p&gt;ตอนแรกของบทความ &lt;strong&gt;"Concurrency ใน Go"&lt;/strong&gt; เราจะว่ากันด้วยพื้นฐานเกี่ยวกับ concurrency และ synchronization&lt;/p&gt;

&lt;h1&gt;
  
  
  Concurrency vs Parallelism
&lt;/h1&gt;

&lt;p&gt;ก่อนอื่นมาทำความเข้าใจสองคำนี้กันก่อน&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrency&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;นิยามคือ ความสามารถในการ&lt;strong&gt;จัดการงานหลายอย่างในช่วงเวลาเดียวกัน&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;การทำงานแต่ละงาน&lt;em&gt;ไม่จำเป็น&lt;/em&gt;ต้องเกิดขึ้นพร้อมกัน แค่สลับงานไปมาก็ถือว่า concurrency&lt;/li&gt;
&lt;li&gt;ลักษณะสำคัญคือ ช่วงเวลาเริ่มต้นของแต่ละงานมีการ overlap&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Parallelism&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;นิยามคือ ความสามารถในการ&lt;strong&gt;ประมวลผลงานหลายอย่างพร้อมกัน&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ต้องอาศัย hardware&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvhr40ouihbk2msz6up01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvhr40ouihbk2msz6up01.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;ภาพประกอบโดย Gemini&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;หมายเหตุ&lt;/strong&gt;: concurrency สามารถเป็น parallelism หรือไม่ก็ได้ ขึ้นอยู่กับ hardware mapping&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ต่อให้ไม่ได้รัน parallel แต่การรัน concurrent ก็สามารถทำให้ทำงานได้เร็วขึ้นได้ เพราะปกติในงานทั่วไปมักจะต้องรออะไรบางอย่างเสมอ เช่น รอดึงค่าจาก memory หรือเก็บค่าลง memory ในระหว่างที่รอนี้ถ้าเราสามารถสลับไปรันอย่างอื่นได้ก็จะทำให้ทำงานโดยรวมได้มีประสิทธิภาพมากขึ้น&lt;/p&gt;
&lt;h1&gt;
  
  
  Concurreny และ Parallelism ใน Go
&lt;/h1&gt;

&lt;p&gt;Go เป็นภาษาที่มี concurreny แบบ built-in คือไม่จำเป็นต้องใช้ library ช่วย&lt;br&gt;
และสามารถทำงานเป็น parallelism ได้ด้วย โดยการกำหนดค่า &lt;code&gt;GOMAXPROCS&lt;/code&gt; &amp;gt; 1&lt;/p&gt;

&lt;p&gt;ตัวอย่างการกำหนดค่าจากในโค้ด&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;runtime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GOMAXPROCS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หรือจะตั้งค่าผ่าน environment variable ก็ได้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GOMAXPROCS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;GOMAXPROCS&lt;/code&gt; คือการกำหนดจำนวน OS threads ที่สามารถประมวลผล Go code ได้พร้อมกันในช่วงเวลาหนึ่ง&lt;/p&gt;

&lt;p&gt;ใน Go เวอร์ชั่น 1.5 เป็นต้นไป ค่าเริ่มต้นของ GOMAXPROCS เท่ากับจำนวน Logical CPUs ของเครื่องที่ใช้งาน ซึ่งเป็นค่าที่ดึงประสิทธิภาพของ hardware ออกมาได้สูงสุดอยู่แล้ว ไม่จำเป็นต้องแก้ไข ถ้าตั้งค่าสูงเกินไปจะทำให้เกิด context switching สูงขึ้น ทำให้ทำงานช้าลง&lt;/p&gt;

&lt;h1&gt;
  
  
  Goroutines
&lt;/h1&gt;

&lt;p&gt;ปกติแล้ว concurrency ในภาษาอื่นจะใช้ &lt;strong&gt;Thread&lt;/strong&gt; แต่สำหรับใน Go เราจะใช้สิ่งที่เรียกว่า &lt;strong&gt;Goroutine&lt;/strong&gt; ซึ่งอาจจะเรียกว่าเป็น Lightweight Thread&lt;/p&gt;

&lt;p&gt;แล้วมันต่างกันยังไง?&lt;/p&gt;

&lt;p&gt;อย่างที่บอกว่า concurrency อาศัยการสลับงานไปมา หรือเรียกว่าการจัดตารางงาน (scheduling) และสำหรับ thread ตัวที่ทำหน้าที่จัดตารางงานก็คือ OS&lt;/p&gt;

&lt;p&gt;แต่ใน Go เนี่ย goroutine จะรันอยู่ภายใน thread อีกที (สามารถมีได้ถึงหลายพันหรือหลายหมื่นภายใน thread เดียว และใช้ memory น้อยกว่า thread มาก) และจะอาศัย &lt;strong&gt;Go Runtime Scheduler&lt;/strong&gt; ทำหน้าที่จัดตารางงาน&lt;/p&gt;

&lt;p&gt;เมื่อเราสั่งรัน &lt;code&gt;main()&lt;/code&gt; Go จะสร้าง goroutine ตัวแรกสุดที่เรียกว่า main routine ขึ้นมารัน ถ้าเราต้องการให้โปรแกรมทำงานแบบ concurrency เราก็สามารถรันงานที่ต้องการในอีก routine ได้ง่ายๆ แค่เขียน keyword &lt;code&gt;go&lt;/code&gt; ข้างหน้าตอนเรียกฟังก์ชั่นเท่านั้นเอง&lt;/p&gt;

&lt;p&gt;มาลองกันดูหน่อย&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"New Routine"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main Routine"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ผลลัพธ์ก็คือ เราจะเห็นแค่ข้อความเดียว...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;Main&lt;/span&gt; &lt;span class="n"&gt;Routine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;อ้าว?!&lt;/p&gt;

&lt;p&gt;เรื่องของเรื่องก็คือ &lt;code&gt;main()&lt;/code&gt; จบการทำงานไปก่อนที่ goroutine ใหม่จะทำงาน ดังนั้นเราต้องสั่งให้ Go รอ goroutine ใหม่ของเราก่อน&lt;/p&gt;

&lt;p&gt;ซึ่งเราอาจจะสามารถสั่งให้ sleep รอก็ได้ เช่นรอไปเลย 100 ms แบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"New Routine"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Millisecond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main Routine"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แต่อย่างที่รู้กันว่าไม่ควร เพราะจริงๆ เราบอกไม่ได้ว่าต้อง sleep นานแค่ไหน วิธีรออย่างถูกต้องก็คือรอจนกว่ามันจะเสร็จนั่นแหละ แต่ทำยังไงเดี๋ยวเราจะพูดถึงกันหลังจากนี้&lt;/p&gt;

&lt;h1&gt;
  
  
  Synchronization และ Sync Package
&lt;/h1&gt;

&lt;p&gt;synchronization คือกลไกที่ใช้ควบคุมลำดับและการเข้าถึงทรัพยากรที่ใช้ร่วมกัน เพื่อป้องกันปัญหาที่เกิดจากการทำหลายงานชนกัน โดยเฉพาะปัญหา race condition&lt;/p&gt;

&lt;p&gt;สำหรับใน Go เราจะใช้แพ็กเกจ &lt;code&gt;sync&lt;/code&gt; สำหรับทำ synchronization ซึ่งมีตัวช่วยหลายอย่าง แต่เราจะหยิบมาพูดถึง 3 ตัว คือ &lt;code&gt;sync.WaitGroup&lt;/code&gt;, &lt;code&gt;sync.Once&lt;/code&gt; และ &lt;code&gt;sync.Mutex&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  sync.WaitGroup
&lt;/h2&gt;

&lt;p&gt;หนึ่งในวิธีที่จะบังคับให้ Go รอจนกว่างานใน routine ย่อยจะเสร็จได้ก็คือการใช้ &lt;code&gt;sync.WaitGroup&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;หลักการการทำงานคือ&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WaitGroup จะเก็บตัวนับ ซึ่งก็คือตัวเลขจำนวนงานที่ต้องรอ&lt;/li&gt;
&lt;li&gt;ใช้ method &lt;code&gt;Add()&lt;/code&gt; เพื่อเพิ่มตัวนับขึ้น (ใส่ตัวเลขจำนวนงานเท่าไหร่ก็ได้ ไม่จำเป็นต้องเพิ่มทีละ 1)&lt;/li&gt;
&lt;li&gt;ใช้ method &lt;code&gt;Done()&lt;/code&gt; บอกมันเมื่อเสร็จแต่ละงาน เพื่อให้เลขลดลง&lt;/li&gt;
&lt;li&gt;ใช้ method &lt;code&gt;Wait()&lt;/code&gt; เพื่อสั่งให้รอจนตัวนับเป็น 0 ซึ่งหมายความว่าไม่มีอะไรให้รอแล้ว&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างการใช้งาน&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wg&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitGroup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"New Routine"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// บอกว่ารันจบแล้วงานนึง&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitGroup&lt;/span&gt;
  &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// บอกว่ามีเพิ่ม 2 งาน&lt;/span&gt;
  &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// บอกให้รอจนกว่างานทั้งหมดจะเสร็จ&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Main Routine"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;หมายเหตุ&lt;/strong&gt;: ต้องส่งค่า &lt;code&gt;wg&lt;/code&gt; เป็น pointer เพื่อให้ทุกตัวลดตัวนับที่ตัวเดียวกัน ถ้าส่งแบบ value จะทำให้โปรแกรมค้างเพราะได้ &lt;code&gt;wg&lt;/code&gt; เป็นค่า copy คนละตัวกัน&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;เราจะเห็นผลลัพธ์เป็น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;New Routine
New Routine
Main Routine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;นั่นก็คือตอนนี้โปรแกรมของเราสามารถรัน goroutine ย่อยๆ ได้โดยไม่จบการทำงานไปก่อน&lt;/p&gt;

&lt;h2&gt;
  
  
  sync.Once
&lt;/h2&gt;

&lt;p&gt;เป็นเครื่องมือที่ทำให้ฟังก์ชั่นที่ระบุถูกรันแค่ครั้งเดียวเท่านั้น ไม่ว่าจะมี goroutine เรียกใช้งานกี่ครั้งก็ตาม ใช้สำหรับการทำ initialization ที่ต้องทำแค่เพียงครั้งเดียว เช่น DB connection, setup state หรือทำ singleton pattern&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Setup"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;once&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Once&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitGroup&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="n"&gt;once&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จากตัวอย่าง ถึงจะมีการเรียก &lt;code&gt;once.Do(setup)&lt;/code&gt; จากหลาย goroutine (5 ตัว) แต่ &lt;code&gt;setup()&lt;/code&gt; จะถูกเรียกทำงานแค่ครั้งเดียว&lt;/p&gt;

&lt;h2&gt;
  
  
  sync.Mutex
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Mutex&lt;/code&gt; มาจาก Mutual Exclusion เป็นกลไกที่ใช้ป้องกันไม่ให้ goroutine หลายตัวเข้าถึง critical section ได้ในช่วงเวลาเดียวกัน&lt;/p&gt;

&lt;p&gt;หลักการทำงานคือ&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ใช้ &lt;code&gt;Lock()&lt;/code&gt; เพื่อล็อกไม่ให้ goroutine อื่นเข้ามาใช้งาน&lt;/li&gt;
&lt;li&gt;ใช้ &lt;code&gt;Unlock()&lt;/code&gt; เพื่อปลดล็อกให้ goroutine อื่นเข้ามาใช้งานต่อได้
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mu&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;
  &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WaitGroup&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

      &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
      &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;wg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;critical section ซึ่งในที่นี้ก็คือ &lt;code&gt;counter++&lt;/code&gt; จะถูกล็อกเอาไว้ให้รันได้ทีละ goroutine ทำให้อัพเดทค่าได้อย่างถูกต้อง ถ้าไม่มีการล็อกไว้ผลลัพธ์ของ &lt;code&gt;counter&lt;/code&gt; อาจไม่ใช่ 1000 (ถ้าใช้เยอะจะทำให้ performance แย่ลงเพราะต้องรอ)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;หมายเหตุ&lt;/strong&gt;: ปกติแล้วใน concurrency เราไม่นิยมแชร์ตัวแปรกลางและแก้ไขค่าร่วมกัน แต่มักจะใช้การส่งค่าผ่าน channel ซึ่งจะกล่าวถึงในบทความถัดไป&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>programming</category>
      <category>go</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>AWS vs GCP ตอนที่ 3: Organization &amp; User Management</title>
      <dc:creator>Perajit</dc:creator>
      <pubDate>Thu, 23 Apr 2026 23:23:51 +0000</pubDate>
      <link>https://dev.to/perajit/aws-vs-gcp-tnthii-3-organization-user-management-2omc</link>
      <guid>https://dev.to/perajit/aws-vs-gcp-tnthii-3-organization-user-management-2omc</guid>
      <description>&lt;p&gt;ออกตัวเหมือนบทความก่อนๆ คือเราไม่ได้เชี่ยวชาญเรื่องนี้ เลยจะเขียนสรุปคร่าวๆ แบบไม่ลงรายละเอียดมาก&lt;/p&gt;

&lt;h1&gt;
  
  
  Organization
&lt;/h1&gt;

&lt;h3&gt;
  
  
  สำหรับ AWS
&lt;/h3&gt;

&lt;p&gt;ใน AWS เราสามารถจัดการ "โครงสร้างองค์กร" ได้โดยอาศัยบริการ &lt;em&gt;AWS Organizations&lt;/em&gt; โดยจะทำให้พนักงานในองค์กรแยกใช้งานหลาย account ภายใต้องค์กรได้ (Multi-Account)&lt;/p&gt;

&lt;p&gt;เมื่อเราสร้าง Organization ขึ้นมา บัญชีที่ใช้งานอยู่จะกลายเป็น &lt;em&gt;"Management Account"&lt;/em&gt; (ขอเรียกว่าบัญชีหลัก) ในทันที ซึ่งในบัญชีหลักนี้เราจะสามารถจัดการสิ่งต่างๆ ในองค์กรได้ เช่น&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Organization Units (OUs)&lt;/strong&gt;: สร้างและจัดการ OUs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;สร้างบัญชีลูกในองค์กร โดยไม่ต้องกรอกข้อมูลบัตรเครดิต เพราะระบบจะไปตัดเงินรวมที่บัญชีหลัก (Consolidated Billing)&lt;/li&gt;
&lt;li&gt;ย้าย OU ของบัญชีลูก&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Service Control Policies (SCPs)&lt;/strong&gt;: สร้างและจัดการ SCPs&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;สำหรับ &lt;em&gt;Organization Units (OUs)&lt;/em&gt; คือกลุ่มย่อยที่ใช้สำหรับจัดกลุ่มบัญชีในองค์กร สามารถซ่อนกันเป็นลำดับชั้นได้ และมีการสืบทอด SCPs ลงด้านล่าง&lt;/p&gt;

&lt;p&gt;ส่วน &lt;em&gt;Service Control Policies (SCPs)&lt;/em&gt; คือนโยบายที่ใช้กำหนดขอบเขตสูงสุดของสิทธิ์ สามารถสั่ง Explicit Deny ซึ่งมีผลเหนือกว่าทุกอย่างแม้กระทั่ง Root User ก็ไม่สามารถฝ่าฝืนได้ โดยเราสามารถผูก SCPs ได้ 3 ระดับ&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ระดับ Root: มีผลกับทุกบัญชีในองค์กร&lt;/li&gt;
&lt;li&gt;ระดับ OU: มีผลกับทุกบัญชีใน OU&lt;/li&gt;
&lt;li&gt;ระดับ Account: มีผลแค่บัญชีนั้นๆ&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;หมายเหตุ: เพื่อความปลอดภัย ไม่ควรใช้ Management Account ในการทำงานทั่วไป ควรเก็บไว้สำหรับงาน admin และ billing เท่านั้น&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  สำหรับ GCP
&lt;/h3&gt;

&lt;p&gt;ตามที่เขียนถึงไปในบทความก่อนหน้า Organization ใน GCP เป็น root ของลำดับชั้นทรัพยากร ดังนั้น&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;มี Organization เสมอ ไม่ต้องใช้ service ไหนมาสร้างหรือจัดการ&lt;/li&gt;
&lt;li&gt;Organization ใน GCP ใช้ Folder เพื่อจัดกลุ่มจัดการทรัพยากร ในขณะที่ Organization ใน AWS ใช้ OUs เพื่อจัดกลุ่มบัญชี&lt;/li&gt;
&lt;li&gt;ใช้ Organization Policies ในการควบคุมกฎขององค์กร (เทียบได้กับ SCPs ใน AWS)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  User Management
&lt;/h1&gt;

&lt;h3&gt;
  
  
  สำหรับ AWS
&lt;/h3&gt;

&lt;p&gt;การจัดการ User เราจะใช้ Service ที่ชื่อว่า Identity Center (ชื่อเดิมคือ AWS SSO)&lt;/p&gt;

&lt;p&gt;Identity Center ถูกออกแบบมาให้เป็นระบบภายในองค์กร จะถูกเปิดใช้งานที่บัญชีหลักขององค์กร (หมายความว่าจำเป็นต้องสร้าง Organization ก่อนเสมอ) และมันจะมองเห็นเฉพาะบัญชีที่อยู่ภายใต้ององค์กรนั้นเท่านั้น&lt;/p&gt;

&lt;p&gt;สิ่งที่เราสามารถจัดการได้จาก Identity Center หลักๆ มีตามนี้&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;Multi-Account Access&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single Sign-On: โดยปกติถ้าเราสร้างบัญชีในองค์กร ถ้าไม่ใช้ Identity Center เราจะเข้าไปบัญชีนั้นโดยการ Switch Role จากบัญชีหลัก แต่เมื่อใช้ Identity Center จะมีลิงค์ที่เรียกว่า AWS Access Portal ซึ่งเป็น unique URL ของแต่ละองค์กร (พนักงานจำแค่ URL เดียว)​ รวบรวมรายการสิทธิ์ในการเข้าถึงบัญชีต่างๆ (Account + Role) ให้สามารถคลิกเข้าล็อกอินได้เลย&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;strong&gt;Identity Management&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;สามารถสร้างและจัดการ User และ Group ในทุกบัญชีขององค์กร&lt;/li&gt;
&lt;li&gt;สามารถเชื่อมต่อกับระบบ Identity Provider ข้างนอก (Federation)

&lt;ul&gt;
&lt;li&gt;ถ้าบริษัทมีระบบเดิมอยู่แล้ว เช่น Azure AD, Okta, Google Workspace ก็สามารถเข้า AWS ด้วยการล็อกอินระบบเดิมได้เลย (เข้าผ่าน Access Portal ที่จะ redirect ไปยัง IdP ที่เชื่อมต่อ)&lt;/li&gt;
&lt;li&gt;ไม่จำเป็นต้องสร้าง User ใหม่&lt;/li&gt;
&lt;li&gt;เบื้องหลังคือ ล็อกอินด้วย Identity แล้ว AWS จะทำการ Assume Role ให้เข้าไปใช้งาน (ไม่มี IAM User อยู่ในบัญชี)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;3) &lt;strong&gt;Permission Sets&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;สามารถสร้าง Permission Sets เพื่อจัดการสิทธิ์ของแต่ละบัญชี&lt;/li&gt;
&lt;li&gt;Permission Sets คือชุดของสิทธิ์ในองค์กร แทนที่เราจะเขียน Policy ในบัญชีลูกทีละบัญชี ก็สร้าง Permission Sets แล้วผูกเข้ากับบัญชีในองค์กรแทน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;strong&gt;Cloud Applications&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;สามารถใช้ Identity Center เป็นตัวกลางล็อกอินเข้าแอปพลิเคชั่นอื่นที่บริษัทใช้ได้ด้วย&lt;/li&gt;
&lt;li&gt;พนักงานล็อกอินที่เดียวแล้วสามารถใช้งานได้ทั้ง AWS และแอปพลิเคชั่นอื่น&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Identity Center กับ Organization ทำงานเชื่อมโยงกัน เมื่อมีการสร้าง User จาก Identity Center เราก็จะมองเห็น User จากหน้าคอนโซลของ Organizations (เพราะเป็น Account ขององค์กรนั้น) และในทางกลับกัน ถ้าสร้าง Account จากใน Organizations เราก็จะมองเห็น Account นั้นในหน้าของ Identity Center ด้วย&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;หมายเหตุ&lt;/strong&gt;: แต่ Permission Sets ต้องจัดการใน Identity Center เท่านั้น เช่นเดียวกับ SCPs ที่ต้องจัดการใน Organizations เท่านั้นเช่นกัน (แยกกฎของคนออกจากกฎของกลุ่ม)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  สำหรับ GCP
&lt;/h3&gt;

&lt;p&gt;GCP ไม่ได้มีระบบสร้าง User Account ในตัวเองเหมือน AWS IAM User แต่อาศัยการดึงข้อตัวตน (Identity) จากภายนอก:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Account&lt;/li&gt;
&lt;li&gt;Cloud Identity หรือ Google Workspace&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ถ้าต้องการใช้ตัวตนจากระบบอื่น เช่น Azure AD, Okta ต้องทำ Workforce Identity Federation (คล้ายกับใน AWS Identity Center) เพื่อให้ GCP ยอมรับและให้ถือเป็นคนใน Organization&lt;/p&gt;

&lt;h1&gt;
  
  
  สรุปข้อเปรียบเทียบ
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Organization ใน AWS ใช้จัดการบัญชี ส่วน Organization ใน GCP เป็น root ในการจัดการการเข้าถึงทรัพยากร&lt;/li&gt;
&lt;li&gt;ใน AWS ใช้ SCPs ควบคุมกฎขององค์กร ส่วนทางฝั่ง GCP ใช้ Organization Policies&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>AWS vs GCP ตอนที่ 2: IAM</title>
      <dc:creator>Perajit</dc:creator>
      <pubDate>Wed, 22 Apr 2026 20:11:03 +0000</pubDate>
      <link>https://dev.to/perajit/aws-vs-gcp-tnthii-2-iam-2e4a</link>
      <guid>https://dev.to/perajit/aws-vs-gcp-tnthii-2-iam-2e4a</guid>
      <description>&lt;p&gt;ออกตัวก่อนว่าเหมือนบทความที่แล้ว เราอาจจะเขียนคล้ายๆ ประมาณโน้ตส่วนตัว สรุปคร่าวๆ แบบไม่ลงรายละเอียด เพราะไม่ได้เชี่ยวชาญ&lt;/p&gt;

&lt;p&gt;ตอนที่ 1 ว่าด้วย Network &amp;amp; Security ไปแล้ว ตอนนี้มาพูดถึงอีกหัวข้อที่ต่างกันมากกว่านั้นอีก ซึ่งก็คือ IAM (Network ยังมีความคล้าย แต่ IAM นี่โครงสร้างไปคนละแนวทางกันเลย)&lt;/p&gt;

&lt;p&gt;IAM หรือ Identity and Access Management เป็นระบบที่ควบคุมว่า &lt;em&gt;"ใคร"&lt;/em&gt; (who) สามารถ &lt;em&gt;"ทำอะไร"&lt;/em&gt; (what) กับ &lt;em&gt;"ทรัพยากรไหน"&lt;/em&gt; (which) บนระบบ&lt;/p&gt;

&lt;h1&gt;
  
  
  AWS
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Account vs User
&lt;/h3&gt;

&lt;p&gt;AWS แยกคำว่า Account ออกจาก User&lt;/p&gt;

&lt;p&gt;Account หมายถึง AWS Account (หรือเรียกว่า Root Account) เป็นเจ้าของทรัพยากรทั้งหมด และเป็นตัวตนที่ใช้ในการจ่ายเงิน&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ในเคสปกติ ล็อกอินโดยใช้อีเมลที่สมัคร (ยกเว้นกรณี multi-account)&lt;/li&gt;
&lt;li&gt;ในเคสปกติ มีสิทธิ์สูงสุด ทำได้ทุกอย่าง (ยกเว้นถูกบล็อกด้วย Service Control Policy ซึ่งเป็นนโยบายที่ใช้ควบคุมสิทธิ์ของบัญชี)&lt;/li&gt;
&lt;li&gt;ใช้สร้าง admin คนแรก (หลังจากนั้นปล่อยให้ admin จัดการสร้าง user คนถัดๆ ไปเอง) และจัดการเรื่อง billing เท่านั้น ไม่แนะนำให้ใช้ในงานทั่วไป เพราะมีสิทธิ์สูงเกินไป&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ส่วน User หมายถึง IAM User คือตัวตนที่ถูกสร้างขึ้นมาภายใต้บัญชี เพื่อใช้ทำงานในระบบ&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ล็อกอินโดยใช้ credentials ที่ admin สร้างให้&lt;/li&gt;
&lt;li&gt;มีสิทธิ์แค่ตามที่ admin กำหนดให้เท่านั้น&lt;/li&gt;
&lt;li&gt;ใช้ทำงานต่างๆ นอกเหนือจากการสร้าง user&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AWS IAM Entities
&lt;/h3&gt;

&lt;p&gt;IAM User ที่กล่าวถึงไป ก็คือหนึ่งใน IAM Entities (สิ่งที่ถูกสร้างและจัดการในระบบ IAM) ซึ่งเป็นประเภทหนึ่งของ Principal (ใครหรืออะไรที่พยายามเข้าถึงทรัพยากร) ที่เป็นตัวแทนคำว่า &lt;em&gt;"ใคร"&lt;/em&gt; ใน IAM นั่นเอง&lt;/p&gt;

&lt;p&gt;IAM Entities จะมี 3 แบบหลักๆ ตามนี้&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAM User&lt;/strong&gt;: เป็นตัวตนที่ใช้แทนคน มีข้อมูลยืนยันตัวตนแบบถาวร (permanent credentials) เช่น username/password หรือ access keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Group&lt;/strong&gt;: กลุ่มของ User ใช้สำหรับจัดชุดสิทธิ์ (Policy) ให้หลายคนพร้อมกัน (อันนี้ไม่สามารถล็อกอินได้)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Role&lt;/strong&gt;: เป็นตัวตนแบบชั่วคราว ไม่มีรหัสผ่านถาวร เหมือนการขอใบรับรองชั่วคราวเพื่อใช้ทำงาน ให้สิทธิ์เข้าถึงทรัพยาการ โดยอาศัยกระบวนการ Assume Role&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;มาถึงตรงนี้ก็อาจจะยังไม่เห็นภาพว่า IAM Role (ต่อไปนี้จะเรียกแค่ Role) มีไว้ทำไม แต่เจ้า Role เนี่ยแหละที่ใช้เยอะมาก ตัวอย่างเช่น&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;ใช้สำหรับบริการ AWS (Service Role)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;อันนี้เป็นกรณีที่ใช้กันบ่อยสุด คือการให้สิทธิ์กับบริการของ AWS เพื่อไปทำงานบางอย่าง&lt;/p&gt;

&lt;p&gt;เนื่องจากว่าแต่ละบริการของ AWS ไม่ได้เรียกกันเองได้โดยอัตโนมัติ เช่น มีเครื่อง EC2 อยากจะอ่านไฟล์ใน S3 ถ้าเราไม่ทำอะไรเลยมันจะไม่มีสิทธิ์คุยกัน เราต้องสร้าง Role ที่ให้สิทธิ์อ่านไฟล์จาก S3 แล้วเอาไปผูกไว้กับ EC2 เพื่อมันสามารถทำงานของมันได้&lt;/p&gt;

&lt;p&gt;2) &lt;strong&gt;ใช้สำหรับข้าม Account (Cross-Account Access)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;กรณีที่มีหลายบัญชี (multi-account) เราสามารถทำให้ user จาก Account A เข้าไปทำอะไรใน Account B ได้ โดยการสร้าง Role ใน Account B โดยกำหนดให้อนุญาตให้คนจาก Account A เข้ามาได้&lt;/p&gt;

&lt;p&gt;3) &lt;strong&gt;ใช้สำหรับล็อกอินจากระบบภายนอก&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เช่น การล็อกอินด้วย Google, Microsoft 365 หรือบริการอื่น เพื่อเข้ามาทำงานใน AWS&lt;br&gt;
ระบบจะไม่มีได้สร้าง User ให้ใน AWS แต่จะใช้การผูก Role ให้หลังจากการยืนยันตัวตนจากข้างนอก&lt;/p&gt;

&lt;p&gt;4) &lt;strong&gt;ใช้สำหรับเชื่อมต่อกับระบบภายนอก&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;สำหรับการเชื่อมต่อการใช้บริการจากระบบภายนอก จำเป็นต้องสร้าง Role ให้กับบริการนั้นๆ&lt;/p&gt;
&lt;h3&gt;
  
  
  AWS IAM Policies
&lt;/h3&gt;

&lt;p&gt;พูดถึง &lt;em&gt;"ใคร"&lt;/em&gt; ในระบบ IAM ไปแล้ว มาถึง &lt;em&gt;"ทำอะไร"&lt;/em&gt; กับ &lt;em&gt;"ทรัพยากรไหน"&lt;/em&gt; กันบ้าง สิ่งที่กำหนดทั้ง 2 อย่างนี้ก็คือ Policy นั่นเอง&lt;/p&gt;

&lt;p&gt;Policy เป็นเอกสารรูปแบบ JSON ที่ระบุว่า อนุญาต (Allow) หรือ ปฏิเสธ (Deny) การเข้าถึงส่วนไหนในระบบ หน้าตาอะไรประมาณนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s3:ListBucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::my-bucket"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;อธิบาย JSON&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Version&lt;/code&gt;: เวอร์ชั่นของภาษา Policy ซึ่งปัจจุบัน AWS บังคับใช้ค่าเดียวคือ &lt;code&gt;2012-10-17&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Effect&lt;/code&gt;: Allow หรือ Deny&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Action&lt;/code&gt;: ทำอะไร&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Resource&lt;/code&gt;: ทรัพยากรไหน&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;หมายเหตุ&lt;/strong&gt;: สังเกตว่าใน 1 Policy กำหนดได้หลายสิทธิ์ (&lt;code&gt;Statement&lt;/code&gt; เป็น list)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  GCP
&lt;/h1&gt;

&lt;p&gt;ทางฝั่ง GCP ก่อนจะพูดถึง IAM จะต้องว่าด้วย "ลำดับชั้นของทรัพยากร" (Resource Hierarchy) กันก่อน&lt;/p&gt;

&lt;h3&gt;
  
  
  GCP Resource Hierarchy
&lt;/h3&gt;

&lt;p&gt;ลำดับชั้นนี้จะมีการสืบทอดนโยบาย (Policy) จากชั้นบนสู่ชั้นล่าง โดยแบ่งลำดับชั้นตามนี้&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;Organization&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เป็นจุดสูงสุดของโครงสร้าง&lt;/li&gt;
&lt;li&gt;เชื่อมต่อกับโดเมนของบริษัท เช่น Google Workspace หรือ Cloud Identity&lt;/li&gt;
&lt;li&gt;ช่วยให้มองเห้นและควบคุมทรัพยากรทั้งหมดในบริษัทได้จากที่เดียว&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) &lt;strong&gt;Folder&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เป็นตัวเลือกเสริม จะมีหรือไม่มีก็ได้&lt;/li&gt;
&lt;li&gt;สามารถมี folders ย่อยซ้อนกันกี่ชั้นก็ได้&lt;/li&gt;
&lt;li&gt;ใช้สำหรับแบ่งกลุ่ม เช่น แบ่งตามแผนก (ฝ่ายไอที, ฝ่ายการตลาด) หรือแบ่งตามสภาพแวดล้อม (development, production)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) &lt;strong&gt;Project&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เป็นหัวใจสำคัญ ทรัพยากรทุกอย่างจะต้องอยู่ภายใต้ Project เสมอ&lt;/li&gt;
&lt;li&gt;เป็นระดับที่ใช้ในการจัดการเรื่อง billing และการเปิดใช้ API ต่างๆ&lt;/li&gt;
&lt;li&gt;Project ID จะต้องไม่ซ้ำกันเลยใน GCP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) &lt;strong&gt;Resource&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;บริการของ GCP ที่เราใช้งานกัน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ข้อดีของลำดับชั้นคือ สามารถใช้ประโยชน์จากการสืบทอดสิทธิ์ ทำให้สามารถจัดการทรัพยากรจำนวนมากได้เป็นระบบระเบียบขึ้น และยังช่วยให้สามารถจัดกลุ่มทรัพยากรเพื่อให้สรุปค่าใช้จ่ายได้ง่ายขึ้และมีประสิทธิภาพ&lt;/p&gt;

&lt;h3&gt;
  
  
  GCP IAM Principals
&lt;/h3&gt;

&lt;p&gt;กลับมาที่ IAM กันต่อ ส่วนแรกของ IAM ก็คือ &lt;em&gt;"ใคร"&lt;/em&gt; สำหรับ GCP จะเรียกว่า Principals หรือ Members&lt;/p&gt;

&lt;p&gt;ประกอบด้วย&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Account&lt;/strong&gt;: อีเมลส่วนตัว (@gmail.com)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service Account&lt;/strong&gt;: บัญชีสำหรับ "แอปพลิเคชั่น" หรือ "เครื่อง" (Machine) เพื่อคุยกันเอง ไม่ต้องมีรหัสผ่าน ใช้กุญแจเข้ารหัส (Cryptographic Keys) ในการยืนยันตัวตน&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Group&lt;/strong&gt;: กลุ่มของ User&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Workspace / Cloud Identity&lt;/strong&gt;: บัญชีขององค์กร&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;หมายเหตุ&lt;/strong&gt;: Account ใน GCP มีอีเมลเสมอ รวมถึง Service Account ด้วย&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  GCP IAM Roles
&lt;/h3&gt;

&lt;p&gt;ส่วนนี้คือตัวแทนของ &lt;em&gt;"ทำอะไร"&lt;/em&gt; ใน IAM&lt;/p&gt;

&lt;p&gt;เป็นชุดของสิทธิ์ในการเข้าถึงทรัพยาการ (อย่าสับสนกับ Roles ของ AWS) ซึ่งใน GCP ไม่ได้แยกเป็น permission แต่ละเรื่องย่อยๆ แต่จะมัดรวมกันอยู่ใน Role&lt;/p&gt;

&lt;p&gt;โดยจะแบ่งออกเป็น 3 ประเภท&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;Basic Roles&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เป็นพวก Role พื้นฐานของ GCP ที่ให้สิทธิ์กว้างๆ มี 3 อย่างคือ &lt;code&gt;Owner&lt;/code&gt;, &lt;code&gt;Editor&lt;/code&gt;, &lt;code&gt;Viewer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;2) &lt;strong&gt;Predefined Roles&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เป็นพวก Role ที่ Google เตรียมไว้ให้ตามหน้าที่งาน เจาะจงไปที่แต่ละบริการ เช่น &lt;code&gt;Storage Admin&lt;/code&gt; หรือ &lt;code&gt;Compute Viewer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;3) &lt;strong&gt;Custom Roles&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เป็น Role ที่ user สร้างขึ้นเอง ใช้เมื่อต้องการสิทธิ์การใช้งานที่เฉพาะเจาะจง&lt;/p&gt;

&lt;h3&gt;
  
  
  GCP IAM Policy
&lt;/h3&gt;

&lt;p&gt;เป็นตัวเชื่อม Principal กับ Role เข้าด้วยกัน โดยจะเป็น resource-based คือเราจะเอาไปผูกไว้กับทรัพยากร (ในขณะที่ใน AWS เราจะเอา Policy ไปผูกไว้ที่ User)&lt;/p&gt;

&lt;p&gt;หน้าตาของ Policy ใน GCP จะเป็นประมาณนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bindings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"roles/storage.admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"members"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"user:storage-admin@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"serviceAccount:my-app@project.iam.gserviceaccount.com"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"roles/viewer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"members"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"group:dev-group@example.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"etag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"xxxx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;หมายเหตุ&lt;/strong&gt;: โดยปกติ GCP IAM Policy คือการ Allow เท่านั้น ถ้าต้องการ Deny จะใช้ Deny Policy เข้ามาช่วย&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  สรุปข้อเปรียบเทียบ
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;โครงสร้าง IAM ของ AWS โดยพื้นฐานเป็นแบบ flat (แต่สามารถสร้างโครงสร้างองค์กรได้จากบริการ AWS Organizations) ในขณะที่ GCP เป็นลำดับชั้นลงมา&lt;/li&gt;
&lt;li&gt;AWS Role คือ "ตัวตน" (Identity) ส่วน GCP Role คือ "ชุดของสิทธิ์" (Permissions)&lt;/li&gt;
&lt;li&gt;การทำงานกับ resource ในระบบ AWS จะใช้ &lt;code&gt;Role&lt;/code&gt; ส่วนใน GCP ใช้ &lt;code&gt;Service Account&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;AWS IAM Policy ใช้กำหนดส่วน &lt;em&gt;"ทำอะไร"&lt;/em&gt; กับ &lt;em&gt;"ทรัพยากรไหน"&lt;/em&gt; แล้วใช้ผูกกับ Entities (Identity-based) ส่วน GCP IAM Policy ใช้เชื่อม &lt;em&gt;"ใคร"&lt;/em&gt; เข้ากับสิทธิ์อนุญาติให้ &lt;em&gt;"ทำอะไร"&lt;/em&gt; แล้วนำไปผูกกับทรัพยากร (Resource-based)&lt;/li&gt;
&lt;li&gt;AWS IAM Policy สามารถกำหนดได้ทั้ง Allow หรือ Deny ส่วน GCP IAM Policy โดยพื้นฐานจะเป็น allow-only&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cloud</category>
      <category>aws</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>AWS vs GCP ตอนที่ 1: Network &amp; Security</title>
      <dc:creator>Perajit</dc:creator>
      <pubDate>Tue, 21 Apr 2026 14:31:30 +0000</pubDate>
      <link>https://dev.to/perajit/aws-vs-gcp-tnthii-1-network-security-43l2</link>
      <guid>https://dev.to/perajit/aws-vs-gcp-tnthii-1-network-security-43l2</guid>
      <description>&lt;p&gt;บทความนี้เขียนเพื่อพยายามสรุปเปรียบเทียบ AWS และ GCP จะเขียนออกแนวโน้ตส่วนตัว แบบสรุปคร่าวๆ ไม่ลงรายละเอียดเยอะ (เพราะก็ไม่ได้เชี่ยวชาญ 555)&lt;/p&gt;

&lt;p&gt;ที่คิดไว้ก็อยากเขียนหลายเรื่อง แต่ขอเริ่มจากเรื่อง Network ก่อนแล้วกัน&lt;/p&gt;

&lt;h1&gt;
  
  
  Network Architecture
&lt;/h1&gt;

&lt;p&gt;ก่อนอื่นต้องพูดถึงคำศัพท์กันก่อน&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ในแง่ภูมิศาสตร์&lt;/strong&gt;: &lt;em&gt;Global&lt;/em&gt;, &lt;em&gt;Region&lt;/em&gt;, &lt;em&gt;Zone&lt;/em&gt; (สำหรับ AWS เรียก &lt;em&gt;Availability Zone&lt;/em&gt; หรือ &lt;em&gt;AZ&lt;/em&gt;)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ในระบบ cloud แต่ละเจ้าจะแบ่งโครงสร้างพื้นฐานในเชิงภูมิศาสตร์ตามบริเวณที่มี data center&lt;/li&gt;
&lt;li&gt;Global คือทั้งโลก ประกอบด้วยหลายๆ Region ซึ่งเป็นที่ตั้งที่อยู่ห่างกันมาก&lt;/li&gt;
&lt;li&gt;แต่ละ Region จะประกอบด้วยหลาย Zone (สำหรับ AWS เรียก availability zone หรือ AZ) อีกที&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;ในแง่เครือข่าย&lt;/strong&gt;: &lt;em&gt;VPC&lt;/em&gt;, &lt;em&gt;Subnet&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPC ที่เป็นวงเครือข่ายที่ resource ภายในสามารถมองเห็นกันได้แต่ภายนอกไม่สามารถมองเห็นเข้ามาได้ถ้าไม่ได้รับอนุญาต&lt;/li&gt;
&lt;li&gt;Subnet ส่วนย่อยของ VPC&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;ความแตกต่างที่ชัดเจนคือ&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;สำหรับ AWS&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPC span เป็น Regional ไม่สามารถ span ข้าม Region ได้&lt;/li&gt;
&lt;li&gt;Subnet อยู่เฉพาะใน AZ เดียวเท่านั้น ไม่สามารถ span ข้าม AZ ได้&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;ในขณะที่ GCP&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VPC span เป็น Global ครอบคลุมทั่วโลก&lt;/li&gt;
&lt;li&gt;Subnet เป็น Regional คือ span ครอบคลุมได้หลาย Zone
(เวลาวาง VM ยังต้องเลือก Zone แต่ IP Address มาจาก pool เดียวกันทั้ง Region)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ความแตกต่างนี้ทำให้ GCP สามารถคุยข้าม Region ได้เลยโดยไม่ต้องทำอะไรเพิ่ม ขณะที่ AWS ต้องมีขั้นตอนเพิ่มเติม (เช่น ทำ Peering) เพื่อให้คุยข้าม Region ได้&lt;/p&gt;

&lt;h1&gt;
  
  
  Network Routing
&lt;/h1&gt;

&lt;p&gt;การคุม traffic ใน Network จะใช้ Route Table (AWS) หรือ Routes (GCP)โดยมีความแตกต่างกันตามนี้&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Route Table&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;อยู่ในเลเวล Subnet คือแต่ละ VPC จะมีได้หลาย Route Table และควบคุม traffic ของแต่ละ Subnet แยกกันด้วยการสร้าง Route Table ที่ต่างกันไปผูกไว้&lt;/li&gt;
&lt;li&gt;ถ้าไม่มีการผูก Route Table ที่สร้างแยกไว้ Subnet จะใช้ Main Route Table ซึ่งเป็นตารางกลางสำหรับทุก Subnet ซึ่งกำหนดให้ component ทุกตัวคุยกันผ่าน internal IP ได้ภายใน VPC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GCP Routes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;มี Routes ตารางเดียวเป็นระดับ Global แชร์กันทั้งโลก&lt;/li&gt;
&lt;li&gt;สามารถสร้าง Route ให้มีผลเฉพาะสำหรับ VM บางตัวได้โดยการใส่ Network Tags&lt;/li&gt;
&lt;li&gt;เมื่อมีการสร้าง Subnet ใหม่ ระบบจะสร้าง Route สำหรับการติดต่อภายใน (local) ให้แบบ Global โดยอัตโนมัติ หมายความว่า component จากคนละ Region จะคุยกันผ่าน internal IP ได้เลย&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Network Security
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Network Security in AWS
&lt;/h3&gt;

&lt;p&gt;AWS แบ่งตัวควบคุม security เป็น 2 เลเยอร์ คือ ในระดับ Subnet และระดับ Instance&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Group&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ควบคุมระดับ Instance&lt;/li&gt;
&lt;li&gt;เป็นการควบคุมแบบ Stateful หมายความว่า มีการจำเส้นทางเข้าออกของ traffic ถ้าขาเข้าผ่านได้ ขาออกก็จะผ่านได้โดยอัตโนมัติ&lt;/li&gt;
&lt;li&gt;ควบคุมแค่ Allow เท่านั้น (white-list)&lt;/li&gt;
&lt;li&gt;ค่า default คือ Deny ขาเข้าทั้งหมด แต่ Allow ขาออกทั้งหมด ทำให้ instance สามารถเรียกออกไปข้างนอกได้เพื่อ install package ต่างๆ ได้&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Network ACL (NACL)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ควบคุมระดับ Subnet&lt;/li&gt;
&lt;li&gt;เป็นการควบคุมแบบ Stateless หมายความว่า ไม่มีการจำการเส้นทางเข้าออกของ traffic ต่อให้ขาเข้าผ่านเข้ามาได้แล้วมันก็ไม่ได้ยอมให้ขาออกผ่านได้เอง ต้องกำหนดกฎของ "ขาเข้า" (ingress) และ "ขาออก" (egress) แยกกัน&lt;/li&gt;
&lt;li&gt;ควบคุมได้ทั้ง Allow และ Deny (สามารถทำ black-list ได้)&lt;/li&gt;
&lt;li&gt;โดย default จะ allow ทั้งหมด ทั้งขาเข้าและขาออก&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;ใน AWS มักจะใช้ Security Group เป็นหลัก และใช้ NACL แค่เป็นด่านเสริมเพื่อ black-list IP ไม่พึงประสงค์&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Network Security in GCP
&lt;/h3&gt;

&lt;p&gt;มีตัวควบคุม security หลักๆ คือ Firewall Rules เป็นการควบคุมแบบ Stateful โดยมีค่า default เหมือนกับ Security Group (stateful เหมือนกัน) คืออนุญาตให้ทุกอย่างวิ่งออกได้ทั้งหมด แต่บล็อกทุกอย่างที่วิ่งเข้า&lt;/p&gt;

&lt;p&gt;ควบคุมได้ทั้ง Allow และ Deny ทำให้เราสามารถใช้ทำ black-list กับ white-list ได้โดยอาศัยการตั้งค่า Deny/Allow, Priority&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ทำ Deny (black-list)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;สร้างกฎโดยใช้ &lt;code&gt;Action: Deny&lt;/code&gt;, &lt;code&gt;Priority: 100&lt;/code&gt; &lt;em&gt;(ตัวเลขน้อยกว่ากฎ Allow หมายถึงสำคัญมากกว่า)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;ระบุ Source IP ที่ต้องการบล็อก&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;ทำ Allow (white-list)&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;สร้างกฎโดยใช้ &lt;code&gt;Action: Allow&lt;/code&gt;, &lt;code&gt;Priority: 1000&lt;/code&gt; &lt;em&gt;(ตัวเลขมากกว่ากฎ Deny หมายถึงสำคัญน้อยกว่า)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;ระบุ Source IP ที่ต้องการให้ผ่าน&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Firewall Rules ทำงานที่ระดับ VPC แต่สามารถเซ็ตค่าให้ทำงานในระดับ Instance (เหมือน Security Group) หรือ Subnet (เหมือน NACL) ได้&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ควบคุมระดับ Instance (เทียบกับ Security Group)&lt;/strong&gt;: อาศัย Target Tags ระบุเป้าหมายกลุ่มของ VM ที่ต้องการบังคับใช้กฎ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ควบคุมระดับ Subnet (เทียบกับ NACL)&lt;/strong&gt;: อาศัยการกำหนด Destination IP เป็น IP range ของ Subnet ที่ต้องการควบคุม&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cloud</category>
      <category>network</category>
      <category>aws</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>Go กับ Interface ฉบับรวบยอด</title>
      <dc:creator>Perajit</dc:creator>
      <pubDate>Tue, 21 Apr 2026 01:27:11 +0000</pubDate>
      <link>https://dev.to/perajit/interface-in-go-chbabrwbyd-1c3e</link>
      <guid>https://dev.to/perajit/interface-in-go-chbabrwbyd-1c3e</guid>
      <description>&lt;h1&gt;
  
  
  Interface กับ OOP
&lt;/h1&gt;

&lt;p&gt;จากบทความที่แล้ว เราทำ OOP ด้วยการผูก method ให้ struct ผ่าน receiver ทีนี้ถ้าเราอยากจัดการกับ struct หลายประเภทที่มีพฤติกรรมเหมือนกันในที่เดียวจะทำยังไง?&lt;/p&gt;

&lt;p&gt;ปกติในภาษาอื่นเราก็จะสร้าง interface ขึ้นมา แล้วให้แต่ละ class implement interface นั้นใช่มั้ย? ใน Go ก็มี interface เหมือนกัน แต่สิ่งที่ต่างคือ ใน Go เป็น implicit interface ไม่ต้องมีการเขียนคีย์เวิร์ด implement เลย&lt;/p&gt;

&lt;p&gt;เช่น เรามี &lt;code&gt;Cat&lt;/code&gt; และ &lt;code&gt;Dog&lt;/code&gt; ซึ่งทั้งคู่มี method ที่มี method signature เหมือนกันคือ &lt;code&gt;Speaker()&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Cat&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Speak&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Meow!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Dog&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="n"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Speak&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Woof!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เราสามารถสร้าง interface ได้แบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Speaker&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Speak&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;โดย &lt;code&gt;Cat&lt;/code&gt; และ &lt;code&gt;Dog&lt;/code&gt; ของเราจะยังเหมือนเดิม ไม่มีแก้โค้ดใดๆ แต่ Go จะรู้เองว่ามันคือ &lt;code&gt;Speaker&lt;/code&gt; เพราะว่า "satisfy interface"&lt;/p&gt;

&lt;p&gt;และเราก็สามารถประกาศ slice ของ &lt;code&gt;Speaker&lt;/code&gt; แล้วเก็บค่า &lt;code&gt;Cat&lt;/code&gt; กับ &lt;code&gt;Dog&lt;/code&gt; ไว้ด้วยกัน รวมถึงสามารถเรียก method ของ &lt;code&gt;Speaker&lt;/code&gt; จาก slice ได้เลย&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Kitty"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;dog&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Dog&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Bo"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// slice ของ Speaker เก็บได้ทั้ง cat และ dog ทันที&lt;/span&gt;
&lt;span class="n"&gt;animals&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Speaker&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dog&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;animals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// เรียก Speak() ของ Speaker ได้เลย&lt;/span&gt;
  &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Speak&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ไอเดียคือใช้การประกาศ interface จากฝั่ง consumer ตรงข้ามกับใน classic OOP ที่เวลาจะใช้ interface ต้องมีการผูกกันตั้งแต่ต้น ซึ่งการทำ interface แบบ Go มีข้อดีหลายอย่าง&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ทำให้เราโฟกัสที่ behavior มากกว่า type&lt;/li&gt;
&lt;li&gt;ยืดหยุ่น สามารถเอา library สองตัวจากคนละที่มาทำงานร่วมกันได้ทันทีถ้า method signature ตรงกัน&lt;/li&gt;
&lt;li&gt;ไม่ผูกติดกับโค้ดต้นทาง ถ้าสนใจ method ไหนก็ประกาศ interface แค่นั้นพอ ซึ่งข้อนี้ทำให้การ mock (เช่น สำหรับ unit test) ง่ายขึ้นมาก&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  โครงสร้าง Interface
&lt;/h1&gt;

&lt;p&gt;ภายใต้ตัวแปร interface จะเก็บค่า 2 อย่างเสมอ คือ type (ชนิดของข้อมูล) และ value (ค่าของข้อมูล) เพราะฉะนั้น interface จะเป็น &lt;code&gt;nil&lt;/code&gt; ก็ต่อเมื่อทั้ง type และ value เป็น &lt;code&gt;nil&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;เช่นจากตัวอย่างเดิม เรามี struct &lt;code&gt;Cat&lt;/code&gt; และ interface &lt;code&gt;Speaker&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Cat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="n"&gt;Speaker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;

&lt;span class="c"&gt;// ตรวจสอบว่า i เป็น nil รึเปล่า&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จะได้ผลลัพธ์เป็น &lt;code&gt;false&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Empty Interface
&lt;/h1&gt;

&lt;p&gt;interface ว่าง &lt;code&gt;interface{}&lt;/code&gt; คือ interface ที่ไม่มีการกำหนด method อะไรไว้ ซึ่งหมายความว่าสามารถเป็น type อะไรก็ได้ (หรือก็คือ &lt;code&gt;any&lt;/code&gt; ในเวอร์ชั่นใหม่ๆ ของ Go)&lt;/p&gt;

&lt;p&gt;เรามักจะใช้ในกรณีที่ต้องการ Dynamic Type เช่น ใช้กับฟังก์ชั่นที่รับพารามิเตอร์อะไรก็ได้ หรือใช้กับโครงสร้างข้อมูลที่ dynamic เช่น &lt;code&gt;map[string]interface{}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;แต่ข้อควรระวังคือ เราจะไม่สามารถใช้ความสามารถของ type เดิมได้ ต้องแปลงค่ากลับมาก่อน เช่น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เราไม่สามารถสั่งแบบนี้ได้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="c"&gt;// จะ error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เนื่องจากตอนนี้ x เป็น &lt;code&gt;interface{}&lt;/code&gt; ไม่ใช่ &lt;code&gt;int&lt;/code&gt; ต้องแปลงกลับเป็น &lt;code&gt;int&lt;/code&gt; ก่อนถึงจะทำ operation ของ &lt;code&gt;int&lt;/code&gt; ได้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;ปัจจุบัน Go มี Generics ให้ใช้แล้ว แนะนำให้ใช้ Generics แทน interface{}&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>programming</category>
      <category>go</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Go กับ OOP ฉบับเข้าใจง่าย (มั้ง)</title>
      <dc:creator>Perajit</dc:creator>
      <pubDate>Tue, 21 Apr 2026 00:17:37 +0000</pubDate>
      <link>https://dev.to/perajit/oop-ain-go-chbabekhaaaicchngaay-mang-3j2l</link>
      <guid>https://dev.to/perajit/oop-ain-go-chbabekhaaaicchngaay-mang-3j2l</guid>
      <description>&lt;p&gt;ออกตัวก่อนว่าไม่ได้เชี่ยวชาญ Go อะไรมากมาย แค่นึกไม่ออกว่าเขียนเรื่องอะไรดี แล้วเรื่องนี้ก็ผุดขึ้นมา&lt;/p&gt;

&lt;p&gt;ใน Go สามารถทำ OOP ได้ แต่จะค่อนข้างต่างจากภาษาอื่นที่เคยเจอมา คือไม่มีสิ่งที่เรียกว่า class แต่จะใช้ struct ซึ่งเป็นการ define โครงสร้างข้อมูล เช่น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
  &lt;span class="n"&gt;Weight&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เป็นการประกาศโครงสร้างของข้อมูลที่ประกอบด้วยฟิลด์ชื่อ &lt;code&gt;Name&lt;/code&gt; และ &lt;code&gt;Weight&lt;/code&gt; และเนื่องจากใน Go จะใส่ zero value ให้ค่าต่างๆ เสมอ ดังนั้นถ้าประกาศตัวแปรด้วย type ของ struct แต่ละ field ก็จะมีค่าเริ่มต้นเป็น zero value ของแต่ละชนิดข้อมูลโดยอัตโนมัติ ซึ่งก็คือทำหน้าที่เก็บข้อมูลได้เหมือน data class ในภาษาอื่นนั่นเอง&lt;/p&gt;

&lt;p&gt;จากตัวอย่าง &lt;code&gt;Animal&lt;/code&gt; ถ้าประกาศตัวแปรแบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เราก็จะได้สิ่งที่เหมือนกับ instance ของ data class ที่มีค่า &lt;code&gt;cat.Name = ""&lt;/code&gt; และ &lt;code&gt;cat.Weight = 0&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;หรือจะกำหนดค่าเริ่มต้นลงไปตั้งแต่ตอนประกาศค่าก็ได้ เช่น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Kitty"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Weight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ก็จะได้ค่าตามที่กำหนดคือ &lt;code&gt;cat.Name = "Kitty"&lt;/code&gt; และ &lt;code&gt;cat.Weight = 2&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Class with methods
&lt;/h1&gt;

&lt;p&gt;ตอนนี้เราสามารถสร้างสิ่งที่เหมือนกับ class ที่มี property แล้ว แต่ว่าในโลกของ OOP เนี่ย class ไม่ได้มีแค่ property แต่ยังมี method ด้วยใช่มั้ย?&lt;/p&gt;

&lt;p&gt;ใน Go เราสามารถสร้าง method ให้กับ struct ได้โดยไม่ได้เขียนลงใน struct เลย แต่ใช้การการผูกฟังก์ชั่นเข้าไปโต้งๆ ทีหลัง ซึ่งคำว่า "ผูกโต้งๆ" อาศัยสิ่งที่เรียกว่า receiver&lt;/p&gt;

&lt;p&gt;ตัวอย่างเช่น เราจะเติม method &lt;code&gt;Eat()&lt;/code&gt; ให้กับ &lt;code&gt;Animal&lt;/code&gt; ได้แบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Eat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Weight&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สังเกตว่ารูปแบบการเขียนฟังก์ชั่นจะมีบางอย่างเพิ่มเข้ามา คือ &lt;code&gt;(a *Animal)&lt;/code&gt; สิ่งนี้แหละที่เรียกว่า receiver โดย &lt;code&gt;a&lt;/code&gt; คือ receiver variable ส่วน &lt;code&gt;*Animal&lt;/code&gt; เป็น receiver type สังเกตว่าจะไม่มีการใช้คีย์เวิร์ด this เหมือนภาษาอื่น แต่จะใช้ receiver อ้างอิงไปเลย&lt;/p&gt;

&lt;p&gt;ลองเขียนโค้ดทดสอบดู&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Kitty"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Weight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Eat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Weight: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Weight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จะได้ค่า &lt;code&gt;a.Weight = 3&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;แล้วทีนี้อยากให้มี method อีกสักกี่อันก็แค่เขียนฟังก์ชั่นโดยผูก receiver เข้าไป&lt;/p&gt;

&lt;p&gt;ข้อดีของการเขียนลักษณะนี้คือทำให้สามารถเติม method เข้าไปได้โดย struct ไม่บวม (อยู่คนละไฟล์ได้) และการใช้ชื่อตัวแปร receiver แทน &lt;code&gt;this&lt;/code&gt; ทำให้ refactor โค้ดจาก function ธรรมดามาเป็น method ได้ง่ายๆ โดยไม่ต้องแก้ชื่อตัวแปรเป็น &lt;code&gt;this&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;เสริมอีกนิดว่า receiver มี 2 แบบคือ value receiver กับ pointer receiver สำหรับในตัวอย่างเราใช้ pointer receiver ซึ่งคือแบบนี้ &lt;code&gt;(a *Animal)&lt;/code&gt; เพราะต้องการแก้ไขข้อมูล เวลาใช้ value receiver ซึ่งคือแบบนี้ &lt;code&gt;(a Animal)&lt;/code&gt; เราจะไม่สามารถแก้ไขข้อมูลได้&lt;/p&gt;

&lt;p&gt;เช่น ถ้าแก้โค้ดของ method &lt;code&gt;Eat()&lt;/code&gt; เป็นแบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// การใช้ value receiver จะไม่สามารถแก้ไขข้อมูลได้&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Eat&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Weight&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ผลที่ได้คือคือค่าของ &lt;code&gt;a.Weight&lt;/code&gt; จะไม่เปลี่ยน&lt;/p&gt;

&lt;h1&gt;
  
  
  Inheritance (หรือจริงๆ คือ Composition)
&lt;/h1&gt;

&lt;p&gt;การ "สืบทอด" ใน Go ก็แตกต่างจากภาษาอื่นที่ใช้คีย์เวิร์ด extends แต่ Go ออกแบบมาให้ "Composition over Inheritance" จึงใช้การ "ฝัง" (embedding) struct ซ้อนเข้าไปข้างใน เช่น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Cat&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Animal&lt;/span&gt;
  &lt;span class="n"&gt;SlaveName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Meow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Meow"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เราจะได้ &lt;code&gt;Cat&lt;/code&gt; ที่มี property และ method ของ &lt;code&gt;Animal&lt;/code&gt; ติดมาด้วยทันที&lt;br&gt;
ทดสอบด้วยการเขียนโค้ดตามด้านล่างนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Eat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Name: %v, Weight: %v, SlaveName: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SlaveName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;โค้ดจะไม่พัง และจะได้ผลลัพธ์เป็น &lt;code&gt;Name:, Weight:1, SlaveName:&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Name&lt;/code&gt; เป็นสตริงว่าง จาก zero value&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Weight&lt;/code&gt; เป็น 1 เนื่องจากมีการสั่ง &lt;code&gt;cat.Eat()&lt;/code&gt; ทำการเพิ่มค่าจาก zero value ครั้งนึง&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SlaveName&lt;/code&gt; เป็นสตริงว่างจาก zero value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แล้วถ้าเกิดเราต้องการใส่ค่าเริ่มต้นให้กับ &lt;code&gt;cat&lt;/code&gt; ล่ะ? ในเมื่อเราสามารถเรียก property ที่มาจาก &lt;code&gt;Animal&lt;/code&gt; ได้ตรงๆ งั้นเราลองใส่ &lt;code&gt;Weight&lt;/code&gt; เป็น 2 ให้กับ &lt;code&gt;cat&lt;/code&gt; ดูหน่อย&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Weight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SlaveName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Jack"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ปรากฏว่าเขียนแบบนี้จะเจอ syntax error…&lt;/p&gt;

&lt;p&gt;นี่เป็นเรื่องที่เราเจอตอนลองเขียนครั้งแรก แล้วก็งงว่าจะใส่ค่าเริ่มต้นให้มันได้ยังไง แล้วก็ค้นพบว่า วิธีที่ถูกต้องคือ ต้องระบุชื่อ struct ที่ embed มาด้วย!&lt;/p&gt;

&lt;p&gt;เนื่องจากตอนประกาศ struct เราใส่ &lt;code&gt;Animal&lt;/code&gt; เข้าไป ตอนเซ็ตค่าเริ่มต้นเราก็ต้องระบุ &lt;code&gt;Animal&lt;/code&gt; เหมือนกัน แบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Weight&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;SlaveName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Jack"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ทีนี้ถ้าลองสั่ง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Eat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Name: %v, Weight: %v, SlaveName: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SlaveName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เราจะได้ผลลัพธ์ &lt;code&gt;Name:, Weight:3, SlaveName:Jack&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Name&lt;/code&gt; ยังเป็นสตริงว่างเพราะไม่ได้กำหนดค่า&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Weight&lt;/code&gt; เป็น 3 เนื่องจากค่าเริ่มต้นถูกเซ็ตเป็น 2&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SlaveName&lt;/code&gt; ได้ค่า Jack ที่เซ็ตเข้าไป&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;ตอนที่ดึงค่าออกมาจาก field หรือเรียก method สามารถเรียกได้เลยตรงๆ แต่ตอนประกาศค่าต้องระบุชื่อ struct ที่ embed มาเสมอ&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;สำหรับ OOP ใน Go คร่าวๆ ก็ประมาณนี้ค่ะ&lt;/p&gt;

</description>
      <category>programming</category>
      <category>go</category>
      <category>oop</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
