[Dev] เรื่องพื้น ๆ ของ OOP ที่โปรแกรมเมอร์ส่วนใหญ่มักไม่เข้าใจมัน

หลัง ๆ มานี้ ได้สอนงานน้อง ๆ และเพื่อนร่วมงานอยู่หลายครั้ง และค้นพบความจริงว่า คนที่รู้จัก Object-Oriended Programming หรือ OOP แบบถ่องแท้นั้นมีน้อยมาก บ้างก็รู้จัก แต่ออกแบบไม่เป็น บ้างก็แค่พอใช้งานได้แบบมั่ว ๆ ไป ไม่เข้าใจที่มาที่ไปซะทีเดียว

OOP-Cover

บทความนี้จึงเกิดขึ้นมา จากการที่ได้พูดได้สอน จนได้วิธีการอธิบายให้เข้าใจได้ง่ายขึ้น จากการตกผลึกมาให้แล้ว เพื่อปลดล๊อคความเข้าใจในการเขียนโปรแกรมเชิงวัตถุ หรือ OOP ได้มากขึ้น

Code ที่จะได้เห็นภายในบทความนี้ ไม่ได้ขึ้นอยู่กับภาษาโปรแกรมใด ๆ เพราะ OOP นั้น ไม่ได้เฉพาะเจาะจง แต่จะขอเลือกใช้ Syntax ของภาษา Java เป็นหลัก เพราะหลาย ๆ ภาษา เช่น C# นั้น มี Syntax ใกล้เคียงกัน ส่วนใครที่ถนัดภาษาอื่น ๆ เชื่อว่าไม่น่าจะยากเกินความสามารถนะครับ เพราะเป้าหมายของบทความนี้ อยู่ที่การปลดล๊อคความเข้าใจในเรื่องของ OOP เป็นหลัก

 

Class ใช้งานไม่ได้

หากใครผ่าน OOP พื้นฐาน หรือเคยเรียน Introduction to OOP มาแล้ว ทุกคนต้องรู้จักกันดีอยู่แล้วว่า Class คือ พิมพ์เขียว

พิมพ์เขียว
พิมพ์เขียว

รูปจาก http://martyhackl.net/johnvanbergenexhibit/2011/05/10/100-year-old-wooden-chandelier-art-glass-panel-will-be-on-display/

สีฟ้าชัด ๆ -_-

แล้วเคยได้ยินคำเหล่านี้ไม๊!?

  • รันคลาส
  • คลาสทำงาน
  • คลาสหาย (หมายถึงค่าเป็น null)

หากใครยังใช้คำพวกนี้ แสดงว่าคงแค่รู้จัก OOP แบบฉาบฉวย แต่แทบจะไม่เข้าใจมันเลย…

ย้ำกันอีกที… Class คือพิมพ์เขียว ให้ลองนึกถึงพิมพ์เขียวสำหรับสร้างรถยนต์

พอเอาพิมพ์เขียวไปสร้างเป็นรถยนต์ (Instantiate) ซึ่งรถยนต์ที่ได้มาในแต่ละคัน ก็คือ Instance หรือที่เรียกกันโดยทั่วไปว่า Object นั่นเอง

แล้วถ้าในพิมพ์เขียวบอกว่า รถยนต์ สามารถ Start ได้ รถยนต์ทุกคันที่สร้างมาจากแบบพิมพ์เขียวนี้ ก็จะสามารถ Start ได้

มาถึงตรงนี้ คุณคิดว่า พิมพ์เขียว (Class) หรือ รถยนต์ (Object) สามารถ Start ได้!?

ใครคิดว่า Class ถ้าอย่างงั้น การ Start อยู่ที่ Class แปลว่า ถ้าสร้าง Object รถยนต์ ขึ้นมา 2 คัน แล้ว Start รถยนต์ มัน Start 2 คันเลยรึเปล่า…!? ไม่ใช่เน๊าะ เพราะมันเป็น Object รถยนต์คนละคันกันไง

เพราะฉะนั้น Class ใช้งานไม่ได้ แต่ Object ต่างหาก ที่ใช้งานได้ ถูกไม๊!?

แล้วที่บอกว่า รัน Class หรือ Class ทำงาน มันต้องเป็น Object ทำงานรึเปล่า…

แล้วที่บอกว่า Class หาย (ค่าเป็น null) มันต้องเป็น Object เป็น null รึเปล่า…

 

ของใครของมัน

จากพิมพ์เขียวรถยนต์ในหัวข้อที่แล้ว จะเห็นว่า car1 และ car2 นั้น มันเป็นคนละ Object ถ้ายังไม่ค่อยเข้าใจ ลองดูอีกซักตัวอย่าง คราวนี้เราจะกำหนดให้มีสีคนละสี ดังนี้

เพราะฉะนั้น car1 จะเป็นสีเขียว และ car2 จะเป็นสีน้ำเงิน ชัดเจนเน๊อะ

 

ตัวแปรเป็นแค่ตัวอ้างอิง

ลองดู Code ต่อไปนี้

Function StartCar() นั้น มี Parameter 1 ตัวเป็นชนิด Car ซึ่งการใช้งาน เราก็ต้องสร้าง Object Car ขึ้นมาก่อน ด้วยคำสั่ง new Car()

หลังจากนั้น Object ที่ได้ ถูกอ้างอิงด้วยตัวแปร myCar แล้วจึงนำตัวแปรส่งให้กับ Function StartCar()

Parameter car ของ Function StartCar() นั้น ก็จะทำการอ้างอิงไปที่ Object เดียวกับที่ myCar อ้างอิง ก็จะสามารถเรียก Method Start() ของ Object ได้

เพราะฉะนั้น ตอนนี้เรามีตัวแปร myCar และ car อ้างอิงไปที่ Object เดียวกันแล้ว

ตรงนี้ส่วนใหญ่จะเข้าใจผิดว่าเป็นคนละตัวกัน

ถ้าตัวแปร car ถูกกำหนดสี ด้วย Code นี้

ตัวแปร myCar ก็จะได้สีส้มไปด้วย เพราะการกำหนดสี ถูกกำหนดที่ Object โดยมีตัวแปร car อ้างอิงนั่นเอง

และจาก Code ด้านบน ตัวแปร myCar อันที่จริงแล้ว ไม่จำเป็นต้องประกาศเลยก็ได้ เพราะเราสามารถสร้าง Object แล้วใส่เข้าไปให้ Function StartCar() เลยก็ได้

 

ลองดูอีกซักตัวอย่าง…

ตัวแปรอยู่ไหนเอ่ย!?

หาไม่เจอใช่ไม๊ล่ะครับ แต่ Object ได้ถูกสร้างขึ้นมาแล้ว ในคำสั่ง new Car()

พอได้ Object มาแล้ว ก็สั่งให้มัน Start เลย ด้วยการเรียก Method Start() ของ Object

Code สั้น ๆ ข้างบนนี้ มันมี 2 Statement นั่นเอง โดยไม่มีตัวแปรมาอ้างอิง

 

Reference Type vs Value Type

เพราะตัวแปรชนิด Class ต่าง ๆ เป็นแค่ตัวอ้างอิง มันจึงถูกเรียกว่า Reference Type ส่วนตัวแปรทั่วไป เช่น int, long, bool จะเป็น Value Type

เนื่องจากมีคนท้วงมาว่า String เป็น Reference Type ซึ่ง String นั้นถือเป็น Class พิเศษ แต่เพื่อความเข้าใจที่ถูกต้อง จึงขอแก้ไขเนื้อหาในหัวข้อนี้ใหม่

สำหรับ Value Type นั้น Dev ทุกคนต้องรู้จักอยู่แล้วล่ะ แต่มันจะต่างจาก Reference Type ยังไง ลองดูตัวอย่างนี้

ทุกคนเข้าใจใช่ไม๊ครับ ว่า capacity2 นั้น ถูกกำหนดความจุของรถเป็น 7 ที่นั่ง ด้วยการถ่ายค่าจากตัวแปร capacity1

หลังจากนั้นจึงถูกเปลี่ยนเป็น 10 ที่นั่งในภายหลัง

และรู้ใช่ไม๊ครับว่า capacity1 นั้น ยังมีความจุเป็น 7 ที่นั่ง!?

นั่นแหล่ะ ทั้ง capacity1 และ capacity2 นั้น ต่างเป็นตัวแปรแบบ Value Type เพราะมันเก็บ Value ในตัวมัน

แต่ถ้ากรณีของ Object ดังตัวอย่างนี้

ลองตอบซิครับว่า car1 และ car2 มีความจุผู้โดยสารเป็นเท่าไหร่!?

ตรงนี้ล่ะ ไคลแมค!! สังเกตุว่า car1 ถูกกำหนดให้มีความจุเป็น 7 ที่นั่งถูกไม๊ครับ แล้ว car2 ถูกกำหนดให้ความจุเป็น 10 ที่นั่ง

ถึงตรงนี้ ถ้าใครตอบว่า car1 และ car2 มีความจุผู้โดยสารต่างกัน ผิดนะครับ!! อย่าลืมว่า car1 และ car2 นั้น อ้างอิงไปที่ Object เดียวกัน ที่บรรทัดที่ 6 ไง

ตัวแปร car1 และ car2 จึงเป็นตัวแปรแบบ Reference Type เพราะมันเก็บแค่การอ้างอิง

ถึงตรงนี้แล้ว ถ้าใครยังไม่ค่อยเข้าใจ แนะนำให้ลองเขียนตาม แล้วรันดูครับ จะได้รู้ว่าผมโกหกหรือเปล่า!?

เพื่อความเข้าใจที่มากขึ้น ผมจะเพิ่ม Code ไปอีก 2 บรรทัด

บรรทัดแรก กำหนดให้ car1 เป็น null ไปแล้ว ก็คือตัวแปร car1 เลิกอ้างอิง Object แต่อย่าลืมว่า car2 ยังอ้างอิงอยู่ จึงยังสามารถเรียก Method Start() ได้อยู่นั่นเอง ไม่ใช่ว่า car2 จะเป็น null ไปด้วย

 

อ้างอิงยังไง

ถึงตรงนี้ ทุกคนเข้าใจแล้วว่า OOP นั้น ตัวแปรที่สร้างขึ้น จะเป็นแบบ Reference Type ก็คือมีการอ้างอิงไปที่ Object

จากในตัวอย่างที่แล้ว

ลองวาดรูปออกมาเป็น Step ก็จะได้ดังนี้

OOP_Step1คือเริ่มต้นจาก มี Class Car แล้วก็สร้าง Object ขึ้นมาด้วยคำสั่ง new Car() หลังจากนั้นก็ให้ตัวแปร car1 อ้างอิง

สำหรับตัวแปร car2 ลองดูภาพด้านล่างนี้นะครับ ว่าที่ถูกต้องเป็นแบบไหน

แบบแรก car2 อ้างอิง car1

OOP_Step2แบบที่สอง car2 อ้างอิง Object เดียวกับ car1

OOP_Step3มันควรเป็นอย่างหลังใช่ไม๊ครับ ถ้าไม่เข้าใจ ลองกลับไปอ่านหัวข้อ Reference Type vs Value Type ดูใหม่นะ

 

จบแล้ว… เรื่องพื้นฐานมาก ๆ ของ OOP หวังว่าผู้ที่ได้อ่านจนจบ จะเข้าใจมากขึ้นนะครับ และถ้ามีคำถาม ก็เขียน Comment มาถามได้เลยนะ

3 Comments



  1. car2 = car1

    ใช้ประโยชน์ในกรณีใดครับ เพราะผมมองว่ามันแทบ ไม่มีประโยชน์เลย
    นอกเสียจากว่า
    car1 = new car();
    car2 = new car();

    car2 ก็จะนำไปใช้ประโยชน์ได้

    แต่ถ้า car2 = car1 งั้นเอา car1 ไปใช้เลย
    จะไม่ลด mem aloc ดีกว่ารึ

    Reply
    1. gplus-profile-picture

      ลองเปลี่ยน car2 เป็น parameter ของ function ดูครับ จะเห็นว่า มีค่าเท่ากัน

      private void someOne(Car car2) {
      ...
      }

      Car car1 = new Car();
      someOne(car1);

      มันก็เท่ากับ car2 = car1 ใช่มั๊ยล่ะครับ ^_^

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *