Encapsulation

Encapsulation คืออะไร

ภาพจากหนังสือ Object-Oriented Analysis and Design with Applications ถ้าจำไม่ผิด

ช่วงโม้ (ไม่มีสาระ ข้ามได้ไม่ต้องอ่าน)

มีเพื่อนร่วมงานคนหนึ่งมาถามว่า Encapsulation ใช่อันนี้ป่ะแล้วก็อธิบายให้ผมฟัง ผมก็งงๆไม่รู้จะตอบยังไงว่าอะไรคือ Encapsulation จริงๆมันก็อธิบายยากนะ ผมไม่แปลกใจที่คนส่วนใหญ่เกลียด OOP (เคยมีคนเกลียด JAVA เพราะมันเป็น OOP) ตอนเริ่มเขียนผมโปรแกรมผมก็งงๆเหมือนกัน แต่ด้วยความอยากรู้อยากเก่งให้ทันเพื่อนก็เลยลงไปนั่ง Sit in ในคาบวิชา JAVA เพื่อเรียน แต่ตอนเรียน JAVA มันไม่ได้เกี่ยวกับ OOP เลยนะ แค่ประกาศ public private package ต่างกันยังไง import ยังไง ฟังก์ชัน Build in พื้นฐานมีอะไรบ้าง แค่นั้นจริงๆนะ ดังนั้นใครเกลียด JAVA เพราะ OOP ผมว่าไม่เกี่ยวกันนะ

แล้วอะไรคือ Encapsulation ล่ะ

จริงๆ wiki ก็มีอธิบายไว้นะ https://en.wikipedia.org/wiki/Encapsulation_(computer_programming) น่าจะน่าเชื่อถือกว่าที่ผมกำลังจะอธิบาย ถ้าให้ผมอธิบายก็อธิบายคล้ายๆ wiki แหละมันเป็นกระบวนการซ่อนข้อมูล ซ่อนการกระทำไม่ให้คนนอกเห็น อย่างประกาศ private ที่ attribute และ function เออความหมายมันแค่นี้แหละ จบละ

เดี๋ยวๆๆ จริงๆมันมีอะไรมากกว่านั้น

ถ้าถามว่ามันคืออะไรตอบแค่นั้นก็พอนะ แต่จริงๆเราควรเข้าใจว่าทำไมมันถึงต้องทำ อันนี้คือเรื่องที่จำเป็นนะ ถ้าเราอยากเข้าใจมันอย่าทำตัวเป็นนกแก้วนกขุนทองแถวบ้านผมที่ท่องว่า “ขายไข่หน่อย” โดยที่ัมันไม่รู้ว่าคืออะไร

ทำไมต้องทำ Encapsulation ทำแล้วดียังไง ถ้าพูดกันตามตรงตอนเขียนโปรแกรมใหม่ๆแม่งทำทำไมวะโคตรไร้สาระเลยนะครับ ประมาณว่าอย่างงี้

1
2
3
4
5
6
7
8
9
10
public class People {
private String name;

public void setName(String name) {
return this.name = name;
}
public String getName() {
return this.name;
}
}

คือมันเป็น private ก็ต้องมานั่งทำ Get Set ค่าซึ่งมันน่ารำคาญมากเพราะ เฮ้ย อะไรกันนักกันหนาหนอ แต่พอเริ่มหาหนังสือ OOP มาอ่านกับได้เรียน OOAD กับอาจารย์จบใหม่จากต่างประเทศที่ไฟแรง เลยค่อยๆเริ่มเข้าใจว่าทำไอ้ Encapsulation ไปทำไม

คนนอกรู้มาก ยิ่งยุ่งยาก

อันนี้เหมือนจะแปลกๆ รู้มากแล้วไม่ดีตรงไหน จริงๆคือมันไม่ดีในทางการเขียนโปรแกรมที่ต้องเชื่อมต่อกับกับหลายส่วนครับ กล่าวคือเมื่อคุณเริ่มรู้ว่า Class ที่คุณทำงานด้วยอะไร ข้างในมันมีอะไรบ้าง คุณก็เริ่มจะไปยุ่งวุ่นวายกับพวกนั้นมากขึ้น ตัวอย่างเช่น ผมซื้อแอร์มาจากนั้น ด้วยความอยากรู้อยากเห็นผมไป Search และรู้ว่ามันทำงานแบบดูดความร้อน (ผมก็ไม่รู้ว่าแอร์ทำงานยังไงเลยขอมั่วว่ามันดูดอากาศร้อนออก) ผมคิดว่าแอร์มันเย็นไม่พอผมเลยไปซื้ออุปกรณ์เพิ่มความเย็นมาซึ่งมันทำงานได้กับแอร์ที่ทำงานแบบดูดความร้อน แอร์เย็นขึ้นมากๆเลย จนวันหนึ่งมีแอร์ตัวใหม่ผลิตออกมาพร้อมบอกว่าใช้ไฟลดลง 90 เปอร์เซ็น (คุณพระ) แถมราคาถูกกว่าของเก่า 95 เปอร์เซ็น ในหัวผมตอนนั้นนี่ไปซื้อแอร์มาเลย เพราะคิดว่ายังไงก็ดี แถมเอามาต่อกับไออุปกรณ์เพิ่มความเย็นตัวเก่ามันจะต้องดีแน่ พอได้แอร์มาติดตั้งเรียบร้อย ผมเอาไอตัวอุปกรณ์พิเศษจากแอร์ตัวเก่า รีบเอามาติดกับแอร์ตัวใหม่ปรากฏว่า “ติดไม่ได้” เอ้าทำไมไม่ได้วะ แอร์ส่วนใหญ่มันก็ระบบดูดความร้อนหมดนี่ (ในความเข้าใจของคนนอกวงการ) พอผมไปแกะดูถึงรู้ว่า แอร์ตัวนี้ทำงานด้วยระบบ ไสยศาสตร์ “เอาผีมาใส่ในแอร์เพื่อให้เกิดความหนาวแบบขนหัวลุกขึ้นมา” ราคามันเลยถูก

ภาพประกอบจากหนังเรื่อง ผีช่องแอร์

คุณเริ่มเห็นอะไรในนั้นไหม เพราะผมรู้ว่า แอร์ทำงานแบบ ดูดความร้อน ผมเลยเริ่มไปหาอุปกรณ์ที่ทำงานกับแอร์แบบดูดความร้อนมาเพื่อติดตั้ง แล้วผมก็มโนเอาเองว่า แอร์มีแต่แบบที่ดูดความร้อน คราวนี้พอผมไปเจอแอร์แบบไสยศาสตร์ที่กินไฟน้อยกว่า ผมก็ไม่สามารถเอาอุปกรณ์นี้ไปต่อพ่วงได้ เพราะมันไม่ได้ทำงานแบบเดียวกัน ถ้าลองเอาตัวอย่างนี้ไปเทียบกับ Code มันจะเป็นแบบนี้

แอร์แบบดูดความร้อน

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AirCondition {
public int compressorPower = 50;
public void start() {
doCoolWithCompressorPower(this.compressorPower);
}
}

public static void main(String argv[]) {

AirCondition air = new AirCondition();
// เร่งการทำงานของระบบแอร์ที่เป็นแบบดูความร้อน
air.compressorPower = 100;
air.start();
}

แอร์แบบไสยศาสตร์

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public class AirCondition {
public float ghostPower = 1.414;
public void start() {
doCoolWithGhostPower(this.ghostPower);
}
}

public static void main(String argv[]) {

AirCondition air = new AirCondition();
// เร่งการทำงานของระบบแอร์ที่เป็นแบบดูความร้อน
air.compressorPower = 100;
air.start();
// Error ไปสิจ๊ะ
}

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

โถ่แก้นิดแก้บ่อยทำบ่น

เคยมีคนบอกผมแบบนี้เหมือนกัน จริงครับตัวอย่างข้างบนมันแก้นิดแก้หน่อย แต่ถ้าโปรแกรมมันมีเรียกใช้แบบนี้สัก 1000 ที่ในโปรแกรม จะเป็นยังไง คุณจะตามแก้มันหมดเหรอ ใช่คุณอาจใช้ Search all แล้ว Replace แต่ถ้ามนันต้องไล่แก้กระบวนการเพิ่มความเย็นให้เป็นแอร์ไสยศาสตร์ล่ะจะทำไง แล้วการคำนวณค่าพลังงานผีมันเป็นต้องตามไปแก้การคำนวณที่ compressorPower ด้วยไหม อะไรมันก็ chain ไปหมด

รองรับการเปลี่ยนแปลงที่เกิดขึ้นได้เสมอ

จากปัญหายิ่งรู้มาก ยิ่งยุ่งยาก จะเห็นผมกระทบและอะไรหลายๆอย่าง จนผู้เชี่ยวชาญเริ่มคิดหลักการอะไรต่างๆขึ้นมาไม่ว่าจะเป็น Abstraction, Dependecy inversion (ผมเคยเขียนไว้แล้วแต่ภาษาจะแปลกๆหน่อย), Law of Demeter ซึ่งการที่จะทำให้ ชิ้นส่วนชิ้นส่วนหนึ่งซ่อนจากภายนอกได้ในระดับที่ห้ามรู้ห้ามยุ่งเลยนั้น(จริงๆแค่บอกให้เข้าใจก็พอทำได้นะ แต่มันคงวุ่นวายว่า เฮ้ย อันไหนตัวแปรที่ไม่ควรยุ่ง) เขาจึงต้องทำ Encapsulation ขึ้นมา แบบประกาศ Private ที่ Attribute หรือ Method แล้วข้างนอกก็จะไม่เห็นแน่นอน พอทำแบบนี้ชีวิตก็จะยากขึ้นเวลาอยากจะทำอะไรเกี่ยวกับการ Class นั้น เราจะต้องทำอะไรอ้อมๆแบบ ต้องเรียกผ่านฟังก์ชัน ต้องส่ง component เข้ามาใน class แต่ไอสิ่งที่ยุ่งยากนี้มันแลกมาด้วยการที่เราสามารถ แก้ปัญหาไอแอร์ไสยศาสตร์ข้างบนได้ในระดับที่ดีมากระบบจะยืดหยุ่น แทนที่จะแก้ Code 1000 ที่ อาจจะแก้แค่ 2 ที่ เวลาเทสก็ไม่ต้องเทสทั้งระบบ เพราะ Code แก้แค่ 2 ส่วน Cost ก็จะลด เงินก็จะเพิ่ม พนักงานจะได้นอนนานขึ้น ไปหาความรู้ได้มากขึ้น

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// เพิ่มช่องทางการเพิ่มความเย็น ให้กับ Air ด้วยฟังก์ชัน addCoolPower ส่วนตัวแปรที่เกี่ยวกับกลไกด้านในเราซ่อนไว้ คนใช้ก็จะเห็นแค่ว่า
// ให้เพิ่ม addCoolPower ได้อย่างเดียว

// แอร์แบบผี
public class AirCondition {
private float ghostPower = 1.414;
public void addCoolPower(int percentCool) {
this.ghosPower = this.ghostPower * percentCool;
}
public void start() {
doCoolWithGhostPower(this.ghostPower);
}
}


// แอร์แบบดูดความร้อน
public class AirCondition {
private int compressorPower = 50;
public void addCoolPower(int percentCool) {
this.compressorPower = percentCool;
}
public void start() {
doCoolWithCompressorPower(this.compressorPower);
}
}



public static void main(String argv[]) {

AirCondition air = new AirCondition();
// เร่งการทำงานของแอร์ แบบไม่เรียกตรงๆ แต่เรียกอ้อมๆผ่าน function จะแอร์แบบผี แอร์แบบดูดความร้อนก็ทำงานได้
air.addCoolPower(100);
air.start();
}

จากตัวอย่างจะเห็นว่า ผมซ่อนพวก compressorPower , ghostPower ไว้ไม่ให้ใครรู้ว่ามีอยู่ คนที่มาเรียกใช้ AIR ทำได้แค่รู้ว่าถ้าต้องการเพิ่มประสิทธิภาพการให้ความเย็นแอร์จะต้องเรียกผ่าน addCoolPower เท่านั้น มันอาจยาก แต่ถ้ามีการเปลี่ยน code ภายในจากแอร์ผีเป็นแอร์ดูดความร้อน หรือ แอร์ดูคดความร้อนเป็นแอร์ผี ผมก็ไม่ได้รับกระทบอะไรเพราะยังเพิ่มประสิทธิภาพ AIR ได้ตามปกติเหมือนเดิม ดังนั้นคุณคงเริ่มเห็นข้อดีของการ ซ่อนพวกการกระทำต่างๆไม่ให้ข้างนอกรู้แล้วใช่ไหมครับ แนวติดของการทำ Encapsulation มีแค่นี้เอง

ตัดจบละกัน

จริงๆมันต่อไปได้อีกหลายเรื่องเลยนะเรื่อง SOLID, Design pattern, Polymorphism, การออกแบบโปรแกรม ทุกเรื่องมันเชื่อมโยงกันหมด แต่เดี๋ยวมันจะยาวไปจนมันเริ่มน่าเบื่อ เลยขอเขียนน้อยๆแต่บ่อยๆดีกว่า (จริงๆยังมีอีกหลายเรื่องที่ผมยังไม่เข้าใจ) สำหรับใครที่อยากอ่านต่อนั้นผมแนะนำหนังสือหลายเล่มเลย

  • Object-Oriented Analysis and Design with Applications

เล่มนี้สนุกดีมีรูปอธิบายตลกดี ใช้เรียนตอนปี 4
Object-Oriented Analysis and Design with Applications

  • Head First Object-Oriented Analysis and Design

เล่มนี้รูปเยอะ มีคำถาม คำตอบระหว่างเล่ม เหมาะกับคนอ่านที่ไม่ชอบรูป (ผมอ่านอันนี้อยู่เหมือนกัน แต่อ่านไม่จบสักที)
Head First Object-Oriented Analysis and Design

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