หลัง ๆ มานี้ ได้สอนงานน้อง ๆ และเพื่อนร่วมงานอยู่หลายครั้ง และค้นพบความจริงว่า คนที่รู้จัก Object-Oriended Programming หรือ OOP แบบถ่องแท้นั้นมีน้อยมาก บ้างก็รู้จัก แต่ออกแบบไม่เป็น บ้างก็แค่พอใช้งานได้แบบมั่ว ๆ ไป ไม่เข้าใจที่มาที่ไปซะทีเดียว
บทความนี้จึงเกิดขึ้นมา จากการที่ได้พูดได้สอน จนได้วิธีการอธิบายให้เข้าใจได้ง่ายขึ้น จากการตกผลึกมาให้แล้ว เพื่อปลดล๊อคความเข้าใจในการเขียนโปรแกรมเชิงวัตถุ หรือ OOP ได้มากขึ้น
Code ที่จะได้เห็นภายในบทความนี้ ไม่ได้ขึ้นอยู่กับภาษาโปรแกรมใด ๆ เพราะ OOP นั้น ไม่ได้เฉพาะเจาะจง แต่จะขอเลือกใช้ Syntax ของภาษา Java เป็นหลัก เพราะหลาย ๆ ภาษา เช่น C# นั้น มี Syntax ใกล้เคียงกัน ส่วนใครที่ถนัดภาษาอื่น ๆ เชื่อว่าไม่น่าจะยากเกินความสามารถนะครับ เพราะเป้าหมายของบทความนี้ อยู่ที่การปลดล๊อคความเข้าใจในเรื่องของ OOP เป็นหลัก
Class ใช้งานไม่ได้
หากใครผ่าน OOP พื้นฐาน หรือเคยเรียน Introduction to OOP มาแล้ว ทุกคนต้องรู้จักกันดีอยู่แล้วว่า Class คือ พิมพ์เขียว…

สีฟ้าชัด ๆ -_-
แล้วเคยได้ยินคำเหล่านี้ไม๊!?
- รันคลาส
- คลาสทำงาน
- คลาสหาย (หมายถึงค่าเป็น null)
หากใครยังใช้คำพวกนี้ แสดงว่าคงแค่รู้จัก OOP แบบฉาบฉวย แต่แทบจะไม่เข้าใจมันเลย…
ย้ำกันอีกที… Class คือพิมพ์เขียว ให้ลองนึกถึงพิมพ์เขียวสำหรับสร้างรถยนต์
พอเอาพิมพ์เขียวไปสร้างเป็นรถยนต์ (Instantiate) ซึ่งรถยนต์ที่ได้มาในแต่ละคัน ก็คือ Instance หรือที่เรียกกันโดยทั่วไปว่า Object นั่นเอง
แล้วถ้าในพิมพ์เขียวบอกว่า รถยนต์ สามารถ Start ได้ รถยนต์ทุกคันที่สร้างมาจากแบบพิมพ์เขียวนี้ ก็จะสามารถ Start ได้
1 2 3 4 5 6 7 8 9 |
class Car { public String color; public int capacity; public void Start() { // Start car engine... } } |
มาถึงตรงนี้ คุณคิดว่า พิมพ์เขียว (Class) หรือ รถยนต์ (Object) สามารถ Start ได้!?
ใครคิดว่า Class ถ้าอย่างงั้น การ Start อยู่ที่ Class แปลว่า ถ้าสร้าง Object รถยนต์ ขึ้นมา 2 คัน แล้ว Start รถยนต์ มัน Start 2 คันเลยรึเปล่า…!? ไม่ใช่เน๊าะ เพราะมันเป็น Object รถยนต์คนละคันกันไง
1 2 3 4 |
Car car1 = new Car(); Car car2 = new Car(); car1.Start(); |
เพราะฉะนั้น Class ใช้งานไม่ได้ แต่ Object ต่างหาก ที่ใช้งานได้ ถูกไม๊!?
แล้วที่บอกว่า รัน Class หรือ Class ทำงาน มันต้องเป็น Object ทำงานรึเปล่า…
แล้วที่บอกว่า Class หาย (ค่าเป็น null) มันต้องเป็น Object เป็น null รึเปล่า…
ของใครของมัน
จากพิมพ์เขียวรถยนต์ในหัวข้อที่แล้ว จะเห็นว่า car1
และ car2
นั้น มันเป็นคนละ Object ถ้ายังไม่ค่อยเข้าใจ ลองดูอีกซักตัวอย่าง คราวนี้เราจะกำหนดให้มีสีคนละสี ดังนี้
1 2 3 4 5 |
Car car1 = New Car(); car1.color = "green"; Car car2 = New Car(); car2.color = "blue"; |
เพราะฉะนั้น car1
จะเป็นสีเขียว และ car2
จะเป็นสีน้ำเงิน ชัดเจนเน๊อะ
ตัวแปรเป็นแค่ตัวอ้างอิง
ลองดู Code ต่อไปนี้
1 2 3 4 5 6 7 8 |
private void StartCar(Car car) { car.Start(); } // ... Car myCar = new Car(); StartCar(myCar); |
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 นี้
1 |
car.color = "orange"; |
ตัวแปร myCar
ก็จะได้สีส้มไปด้วย เพราะการกำหนดสี ถูกกำหนดที่ Object โดยมีตัวแปร car
อ้างอิงนั่นเอง
และจาก Code ด้านบน ตัวแปร myCar
อันที่จริงแล้ว ไม่จำเป็นต้องประกาศเลยก็ได้ เพราะเราสามารถสร้าง Object แล้วใส่เข้าไปให้ Function StartCar()
เลยก็ได้
1 |
StartCar(new Car()); |
ลองดูอีกซักตัวอย่าง…
1 |
(new Car()).Start(); |
ตัวแปรอยู่ไหนเอ่ย!?
หาไม่เจอใช่ไม๊ล่ะครับ แต่ 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 ยังไง ลองดูตัวอย่างนี้
1 2 3 4 5 6 |
int capacity1; int capacity2; capacity1 = 7; capacity2 = capacity1; capacity2 = 10; |
ทุกคนเข้าใจใช่ไม๊ครับ ว่า capacity2
นั้น ถูกกำหนดความจุของรถเป็น 7 ที่นั่ง ด้วยการถ่ายค่าจากตัวแปร capacity1
หลังจากนั้นจึงถูกเปลี่ยนเป็น 10 ที่นั่งในภายหลัง
และรู้ใช่ไม๊ครับว่า capacity1
นั้น ยังมีความจุเป็น 7 ที่นั่ง!?
นั่นแหล่ะ ทั้ง capacity1
และ capacity2
นั้น ต่างเป็นตัวแปรแบบ Value Type เพราะมันเก็บ Value ในตัวมัน
แต่ถ้ากรณีของ Object ดังตัวอย่างนี้
1 2 3 4 5 6 7 |
Car car1; Car car2; car1 = new Car(); car1.capacity = 7; car2 = car1; car2.capacity = 10; |
ลองตอบซิครับว่า car1
และ car2
มีความจุผู้โดยสารเป็นเท่าไหร่!?
ตรงนี้ล่ะ ไคลแมค!! สังเกตุว่า car1
ถูกกำหนดให้มีความจุเป็น 7 ที่นั่งถูกไม๊ครับ แล้ว car2
ถูกกำหนดให้ความจุเป็น 10 ที่นั่ง
ถึงตรงนี้ ถ้าใครตอบว่า car1
และ car2
มีความจุผู้โดยสารต่างกัน ผิดนะครับ!! อย่าลืมว่า car1
และ car2
นั้น อ้างอิงไปที่ Object เดียวกัน ที่บรรทัดที่ 6 ไง
ตัวแปร car1
และ car2
จึงเป็นตัวแปรแบบ Reference Type เพราะมันเก็บแค่การอ้างอิง
ถึงตรงนี้แล้ว ถ้าใครยังไม่ค่อยเข้าใจ แนะนำให้ลองเขียนตาม แล้วรันดูครับ จะได้รู้ว่าผมโกหกหรือเปล่า!?
เพื่อความเข้าใจที่มากขึ้น ผมจะเพิ่ม Code ไปอีก 2 บรรทัด
1 2 |
car1 = null; car2.Start(); |
บรรทัดแรก กำหนดให้ car1
เป็น null
ไปแล้ว ก็คือตัวแปร car1
เลิกอ้างอิง Object แต่อย่าลืมว่า car2
ยังอ้างอิงอยู่ จึงยังสามารถเรียก Method Start()
ได้อยู่นั่นเอง ไม่ใช่ว่า car2
จะเป็น null
ไปด้วย
อ้างอิงยังไง
ถึงตรงนี้ ทุกคนเข้าใจแล้วว่า OOP นั้น ตัวแปรที่สร้างขึ้น จะเป็นแบบ Reference Type ก็คือมีการอ้างอิงไปที่ Object
จากในตัวอย่างที่แล้ว
1 2 |
car1 = new Car(); car2 = car1; |
ลองวาดรูปออกมาเป็น Step ก็จะได้ดังนี้
คือเริ่มต้นจาก มี Class
Car
แล้วก็สร้าง Object ขึ้นมาด้วยคำสั่ง new Car()
หลังจากนั้นก็ให้ตัวแปร car1
อ้างอิง
สำหรับตัวแปร car2
ลองดูภาพด้านล่างนี้นะครับ ว่าที่ถูกต้องเป็นแบบไหน
แบบแรก car2
อ้างอิง car1
แบบที่สอง
car2
อ้างอิง Object เดียวกับ car1
มันควรเป็นอย่างหลังใช่ไม๊ครับ ถ้าไม่เข้าใจ ลองกลับไปอ่านหัวข้อ Reference Type vs Value Type ดูใหม่นะ
จบแล้ว… เรื่องพื้นฐานมาก ๆ ของ OOP หวังว่าผู้ที่ได้อ่านจนจบ จะเข้าใจมากขึ้นนะครับ และถ้ามีคำถาม ก็เขียน Comment มาถามได้เลยนะ
Permalink
Permalink
car2 = car1
ใช้ประโยชน์ในกรณีใดครับ เพราะผมมองว่ามันแทบ ไม่มีประโยชน์เลย
นอกเสียจากว่า
car1 = new car();
car2 = new car();
car2 ก็จะนำไปใช้ประโยชน์ได้
แต่ถ้า car2 = car1 งั้นเอา car1 ไปใช้เลย
จะไม่ลด mem aloc ดีกว่ารึ
Permalink
ลองเปลี่ยน car2 เป็น parameter ของ function ดูครับ จะเห็นว่า มีค่าเท่ากัน
private void someOne(Car car2) {
...
}
Car car1 = new Car();
someOne(car1);
มันก็เท่ากับ
car2 = car1
ใช่มั๊ยล่ะครับ ^_^