บทนำ: จาก Procedural สู่ Object-Oriented
นักศึกษาที่เรียนมาจากภาษา C จะคุ้นเคยกับการเขียนโปรแกรมแบบ Procedural Programming ซึ่งเน้นที่ ขั้นตอนการทำงาน (Procedures) และ ฟังก์ชัน (Functions) เป็นหลัก ในขณะที่ Java ใช้แนวทาง Object-Oriented Programming (OOP) ที่มองโลกในมุมของ วัตถุ (Objects) ที่มีคุณสมบัติและพฤติกรรม
การเข้าใจความแตกต่างระหว่างสองภาษานี้จะช่วยให้นักศึกษาเปลี่ยนกรอบความคิด (Mindset Shift) จากการคิดแบบขั้นตอนไปสู่การคิดแบบวัตถุ ซึ่งเป็นพื้นฐานสำคัญของการพัฒนาซอฟต์แวร์สมัยใหม่
- บทนำ: จาก Procedural สู่ Object-Oriented
- ความแตกต่างพื้นฐาน: Paradigm
- Procedural Programming (C)
- Object-Oriented Programming (Java)
- เปรียบเทียบโครงสร้างโปรแกรม
- ตัวอย่างที่ 1: โปรแกรมจัดการข้อมูลนักศึกษา
- แบบ C (Procedural)
- แบบ Java (Object-Oriented)
- การจัดการหน่วยความจำ (Memory Management)
- C: Manual Memory Management
- Java: Automatic Memory Management
- ความปลอดภัย (Safety)
- C: ไม่มีการตรวจสอบอัตโนมัติ
- Java: Runtime Safety Checks
- Platform Independence: Write Once, Run Anywhere
- C: Platform Dependent
- Java: Platform Independent
- การจัดการ String
- C: String เป็น Character Array
- Java: String เป็น Object
- สรุปความแตกต่างหลัก
- เมื่อไหร่ควรใช้อะไร?
- ใช้ C เมื่อ:
- ใช้ Java เมื่อ:
- สรุป: Mindset Shift จาก C ไป Java
ความแตกต่างพื้นฐาน: Paradigm
Procedural Programming (C)
Procedural Programming คือการเขียนโปรแกรมที่เน้น “ทำอะไร (What to do)” และ “ทำอย่างไร (How to do)” โดยเรียงลำดับคำสั่งและฟังก์ชันตามขั้นตอนการทำงาน
หลักการ:
- โปรแกรม = ลำดับของคำสั่ง (Sequence of instructions)
- ข้อมูล (Data) แยกออกจาก ฟังก์ชัน (Functions)
- ใช้ฟังก์ชันจัดระเบียบโค้ด
- Top-down design (แบ่งปัญหาใหญ่เป็นปัญหาเล็ก)
ตัวอย่างความคิดแบบ Procedural:
textโปรแกรมคำนวณเงินเดือนพนักงาน:
1. รับข้อมูลพนักงาน (ชื่อ, เงินเดือนพื้นฐาน, ชั่วโมงทำงาน)
2. คำนวณค่าล่วงเวลา
3. คำนวณภาษี
4. คำนวณเงินเดือนสุทธิ
5. พิมพ์สลิปเงินเดือน
Object-Oriented Programming (Java)
Object-Oriented Programming คือการเขียนโปรแกรมที่เน้น “สิ่งของ (Objects)” ที่มีอยู่ในระบบ แต่ละวัตถุมีทั้ง คุณสมบัติ (Attributes) และ พฤติกรรม (Behaviors)
หลักการ:
- โปรแกรม = การสื่อสารระหว่าง Objects
- ข้อมูลและ Method รวมกันอยู่ใน Object
- Objects เป็นตัวแทนของสิ่งในโลกจริง
- Bottom-up design (สร้าง Objects แล้วประกอบเป็นระบบ)
ตัวอย่างความคิดแบบ Object-Oriented:
textระบบเงินเดือนพนักงาน:
Objects ในระบบ:
- Employee (พนักงาน): มีชื่อ, เงินเดือน, ตำแหน่ง
- behaviors: calculateSalary(), getBonus(), getDeduction()
- Payslip (สลิปเงินเดือน): มีเลขที่, วันที่, ยอดเงิน
- behaviors: generate(), print(), sendEmail()
- TaxCalculator (คำนวณภาษี): มีอัตราภาษี, เกณฑ์
- behaviors: calculateTax(), getDeductions()
เปรียบเทียบโครงสร้างโปรแกรม
ตัวอย่างที่ 1: โปรแกรมจัดการข้อมูลนักศึกษา
แบบ C (Procedural)
c#include <stdio.h>
#include <string.h>
// ข้อมูลกระจัดกระจาย - ใช้ global variables หรือ struct
struct Student {
char id[10];
char name[100];
float gpa;
};
// ฟังก์ชันแยกจากข้อมูล
void displayStudent(struct Student s) {
printf("ID: %s\n", s.id);
printf("Name: %s\n", s.name);
printf("GPA: %.2f\n", s.gpa);
}
void updateGPA(struct Student *s, float newGPA) {
s->gpa = newGPA;
printf("GPA updated to %.2f\n", s->gpa);
}
float calculateGrade(float gpa) {
if (gpa >= 3.5) return 4.0;
else if (gpa >= 3.0) return 3.5;
else if (gpa >= 2.5) return 3.0;
else if (gpa >= 2.0) return 2.5;
else return 2.0;
}
int isHonor(struct Student s) {
return s.gpa >= 3.5;
}
int main() {
// สร้างนักศึกษา 2 คน
struct Student student1;
strcpy(student1.id, "6501001");
strcpy(student1.name, "Somchai");
student1.gpa = 3.75;
struct Student student2;
strcpy(student2.id, "6501002");
strcpy(student2.name, "Somsri");
student2.gpa = 3.25;
// แสดงข้อมูล - ต้องเรียกฟังก์ชัน
displayStudent(student1);
printf("Honor Student: %s\n", isHonor(student1) ? "Yes" : "No");
printf("\n");
displayStudent(student2);
printf("Honor Student: %s\n", isHonor(student2) ? "Yes" : "No");
// แก้ไข GPA
updateGPA(&student1, 3.85);
return 0;
}
ปัญหาของแนวทาง C:
- ข้อมูลและฟังก์ชันแยกกัน
struct Studentเก็บเฉพาะข้อมูล- ฟังก์ชันอยู่นอก struct
- ยากต่อการจัดการเมื่อโปรแกรมใหญ่ขึ้น
- ไม่มีการซ่อนข้อมูล (No Encapsulation)c
student1.gpa = -5.0; // แก้ไขได้โดยตรง แม้จะผิดเงื่อนไข! - ต้องส่ง struct ให้ฟังก์ชันทุกครั้งc
displayStudent(student1); // ส่ง student isHonor(student1); // ส่ง student updateGPA(&student1, 3.85); // ส่ง pointer ของ student - ไม่มีความสัมพันธ์ที่ชัดเจน
- ฟังก์ชัน
displayStudent()อาจถูกเรียกใช้กับ struct อื่นได้ - ไม่มีการบังคับว่าฟังก์ชันไหนใช้กับ struct ไหน
- ฟังก์ชัน
แบบ Java (Object-Oriented)
java// Class เป็นแบบพิมพ์เขียว (Blueprint) ของ Object
public class Student {
// Attributes (คุณสมบัติ) - ข้อมูลภายใน
private String id;
private String name;
private double gpa;
// Constructor - สร้าง Object
public Student(String id, String name, double gpa) {
this.id = id;
this.name = name;
setGPA(gpa); // ใช้ method เพื่อ validate
}
// Methods (พฤติกรรม) - รวมอยู่กับข้อมูล
// Getter - อ่านข้อมูล
public String getId() {
return id;
}
public String getName() {
return name;
}
public double getGPA() {
return gpa;
}
// Setter - แก้ไขข้อมูล (มี validation)
public void setGPA(double newGPA) {
if (newGPA >= 0.0 && newGPA <= 4.0) {
this.gpa = newGPA;
System.out.println("GPA updated to " + newGPA);
} else {
System.out.println("Invalid GPA! Must be between 0.0 and 4.0");
}
}
// Business logic methods
public boolean isHonor() {
return gpa >= 3.5;
}
public String getGradeLevel() {
if (gpa >= 3.5) return "Excellent";
else if (gpa >= 3.0) return "Very Good";
else if (gpa >= 2.5) return "Good";
else if (gpa >= 2.0) return "Fair";
else return "Poor";
}
public void displayInfo() {
System.out.println("=== Student Information ===");
System.out.println("ID: " + id);
System.out.println("Name: " + name);
System.out.println("GPA: " + gpa);
System.out.println("Grade Level: " + getGradeLevel());
System.out.println("Honor Student: " + (isHonor() ? "Yes" : "No"));
System.out.println("===========================");
}
}
// การใช้งาน
public class StudentDemo {
public static void main(String[] args) {
// สร้าง Objects - แต่ละ object เป็นตัวแทนของนักศึกษา 1 คน
Student student1 = new Student("6501001", "Somchai", 3.75);
Student student2 = new Student("6501002", "Somsri", 3.25);
// Objects มี behavior ของตัวเอง - ไม่ต้องส่ง parameter
student1.displayInfo();
System.out.println();
student2.displayInfo();
System.out.println();
// แก้ไข GPA - object ดูแลข้อมูลของตัวเอง
student1.setGPA(3.85);
// ลองแก้ไขค่าผิด - จะถูกป้องกัน
student1.setGPA(-5.0); // Invalid! แสดง error message
// ไม่สามารถแก้ไข gpa โดยตรง
// student1.gpa = -5.0; // ERROR! gpa เป็น private
}
}
ข้อดีของแนวทาง Java:
- Encapsulation (การห่อหุ้มข้อมูล)
- ข้อมูลเป็น
privateไม่สามารถแก้ไขโดยตรง - ต้องผ่าน methods (
setGPA()) ที่มี validation - ป้องกันข้อมูลผิดพลาด
- ข้อมูลเป็น
- Data + Behavior รวมกัน
- ข้อมูลและ methods อยู่ใน class เดียวกัน
- เรียกใช้ได้ง่าย:
student1.displayInfo() - ไม่ต้องส่ง object เป็น parameter
- Self-contained Objects
- แต่ละ object ดูแลข้อมูลของตัวเอง
student1และstudent2เป็นอิสระจากกัน
- ความชัดเจนของความสัมพันธ์
- Methods เป็นของ class
- เห็นได้ชัดว่า method ไหนทำงานกับ object ไหน
การจัดการหน่วยความจำ (Memory Management)
C: Manual Memory Management
ใน C โปรแกรมเมอร์ต้องจัดการหน่วยความจำเอง ด้วย malloc() และ free()
c#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Product {
char name[100];
double price;
int quantity;
};
int main() {
// จองหน่วยความจำด้วยตัวเอง
struct Product *product = (struct Product*) malloc(sizeof(struct Product));
if (product == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// ใช้งาน
strcpy(product->name, "Laptop");
product->price = 25000.0;
product->quantity = 10;
printf("Product: %s\n", product->name);
printf("Price: %.2f\n", product->price);
// ต้อง free memory เอง ไม่งั้น memory leak!
free(product);
product = NULL;
return 0;
}
ปัญหาที่พบบ่อยใน C:
- Memory Leak – ลืม
free()cstruct Product *p = malloc(sizeof(struct Product)); // ใช้งาน... // ลืม free(p); → Memory leak! - Dangling Pointer – ใช้ pointer หลัง freec
struct Product *p = malloc(sizeof(struct Product)); free(p); strcpy(p->name, "Test"); // ERROR! ใช้ pointer ที่ free แล้ว - Double Free – free ซ้ำc
free(p); free(p); // ERROR! free ซ้ำ - Buffer Overflow – เข้าถึง memory เกินc
int arr[5]; arr[10] = 100; // Buffer overflow! อันตราย
Java: Automatic Memory Management
Java มี Garbage Collector (GC) ทำงานอัตโนมัติ ดูแลจัดการหน่วยความจำให้
javapublic class Product {
private String name;
private double price;
private int quantity;
public Product(String name, double price, int quantity) {
this.name = name;
this.price = price;
this.quantity = quantity;
}
public void displayInfo() {
System.out.println("Product: " + name);
System.out.println("Price: " + price);
System.out.println("Quantity: " + quantity);
}
}
public class MemoryDemo {
public static void main(String[] args) {
// สร้าง object - memory จัดการอัตโนมัติ
Product product = new Product("Laptop", 25000.0, 10);
product.displayInfo();
// ไม่ต้อง free memory เอง
// เมื่อไม่มีการอ้างอิง (reference) ไป product
// Garbage Collector จะลบทิ้งอัตโนมัติ
product = null; // ตัดการอ้างอิง
// GC จะลบ object ทิ้งเอง
// ไม่มี memory leak, dangling pointer, double free
}
}
การทำงานของ Garbage Collector:
javapublic class GCDemo {
public static void main(String[] args) {
// สร้าง objects มากมาย
for (int i = 0; i < 1000000; i++) {
Product p = new Product("Item" + i, 100.0, 1);
// เมื่อจบ loop แต่ละรอบ p จะไม่มีใครอ้างอิง
// GC จะเก็บกวาด (collect) objects เก่าทิ้ง
}
// Memory ไม่ล้น เพราะ GC ทำงานอัตโนมัติ
System.out.println("Program completed successfully!");
}
}
ข้อดี Automatic Memory Management:
- ไม่มี Memory Leak – GC ลบ objects ที่ไม่ใช้แล้วอัตโนมัติ
- ไม่มี Dangling Pointer – Java ป้องกันการเข้าถึง object ที่ถูกลบแล้ว
- ไม่มี Double Free – ไม่มีคำสั่ง
free()ให้ผิดพลาด - ปลอดภัยกว่า – ลดโอกาส bugs ที่เกี่ยวกับ memory
ข้อเสีย:
- Performance Overhead – GC ใช้ CPU ในการเก็บขยะ
- GC Pause – บางครั้งโปรแกรมหยุดชั่วครู่เพื่อให้ GC ทำงาน (แต่ Java modern มี GC ที่มีประสิทธิภาพสูง)
ความปลอดภัย (Safety)
C: ไม่มีการตรวจสอบอัตโนมัติ
c#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
// C ไม่ตรวจสอบ array bounds
printf("%d\n", arr[0]); // OK
printf("%d\n", arr[10]); // ไม่ error แต่อันตราย! (Undefined behavior)
// อาจจะ:
// - แสดงค่าขยะ
// - Crash โปรแกรม
// - เข้าถึง memory ของโปรแกรมอื่น (Security vulnerability!)
// Buffer overflow attack ทำได้จาก vulnerability แบบนี้
return 0;
}
ตัวอย่างปัญหาจริง:
c// Vulnerable code
void processInput(char *input) {
char buffer[10];
strcpy(buffer, input); // ไม่ตรวจสอบขนาด!
// ถ้า input ยาวกว่า 10 → Buffer overflow!
}
int main() {
char userInput[100];
scanf("%s", userInput);
processInput(userInput); // อันตราย!
return 0;
}
Java: Runtime Safety Checks
javapublic class SafetyDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
// Java ตรวจสอบ array bounds อัตโนมัติ
System.out.println(arr[0]); // OK
try {
System.out.println(arr[10]); // ArrayIndexOutOfBoundsException!
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Error: Index out of bounds!");
System.out.println("Array length: " + arr.length);
}
// โปรแกรมไม่ crash แต่จัดการ error ได้
System.out.println("Program continues safely...");
}
}
ข้อดีของ Runtime Checks:
- ป้องกัน Buffer Overflowjava
int[] arr = new int[5]; arr[10] = 100; // Exception! ไม่ให้เกิด overflow - ป้องกัน Null Pointerjava
String s = null; System.out.println(s.length()); // NullPointerException! // แสดง error ชัดเจน ไม่ crash แบบลึกลับ - Type Safetyjava
Object obj = "Hello"; Integer num = (Integer) obj; // ClassCastException! // Java ตรวจสอบ type อัตโนมัติ
Platform Independence: Write Once, Run Anywhere
C: Platform Dependent
โค้ด C ต้อง compile ใหม่สำหรับแต่ละ platform
c// program.c - เขียนครั้งเดียว
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
แต่ต้อง compile หลายครั้ง:
bash# Windows
gcc program.c -o program.exe
# Linux
gcc program.c -o program
# macOS
gcc program.c -o program
# ARM (Raspberry Pi)
arm-linux-gnueabihf-gcc program.c -o program
# แต่ละ platform ได้ไฟล์ executable คนละอัน!
ปัญหา:
- Binary ใช้ไม่ได้ข้าม platform
program.exe(Windows) รันบน Linux ไม่ได้- ต้อง compile ใหม่ทุก platform
- Platform-specific codec
#ifdef _WIN32 // Windows code system("cls"); #elif __linux__ // Linux code system("clear"); #elif __APPLE__ // macOS code system("clear"); #endif - Library dependencies
- Windows:
.dllfiles - Linux:
.sofiles - macOS:
.dylibfiles - ต้องจัดการแยกกัน
- Windows:
Java: Platform Independent
Java compile ครั้งเดียว รันได้ทุก platform
java// HelloWorld.java - เขียนครั้งเดียว
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Compile ครั้งเดียว:
bashjavac HelloWorld.java
# สร้างไฟล์ HelloWorld.class (bytecode)
รันได้ทุก platform:
bash# Windows
java HelloWorld
# Linux
java HelloWorld
# macOS
java HelloWorld
# Raspberry Pi (ARM)
java HelloWorld
# ใช้ไฟล์ .class เดียวกัน!
ทำไมถึงได้?
textJava Source Code (.java)
↓
[javac] Compile
↓
Bytecode (.class) ← Platform-independent
↓
┌───────────────────────────────────┐
│ Java Virtual Machine (JVM) │ ← Platform-specific
├───────────────────────────────────┤
│ Windows JVM | Linux JVM | Mac JVM │
└───────────────────────────────────┘
↓ ↓ ↓
Windows OS | Linux OS | macOS
JVM ทำหน้าที่:
- อ่าน bytecode (เหมือนกันทุก platform)
- แปลเป็นคำสั่งเครื่อง (native code) ของแต่ละ OS
- รันโปรแกรม
ตัวอย่างการใช้งานจริง:
javapublic class FileDemo {
public static void main(String[] args) {
// Java จัดการ path separator อัตโนมัติ
String path = "data/config.txt";
// บน Windows: data\config.txt
// บน Linux/Mac: data/config.txt
// Java แปลให้อัตโนมัติ!
File file = new File(path);
System.out.println("File exists: " + file.exists());
System.out.println("Absolute path: " + file.getAbsolutePath());
}
}
การจัดการ String
C: String เป็น Character Array
c#include <stdio.h>
#include <string.h>
int main() {
// String คือ array of characters ลงท้ายด้วย '\0'
char str1[20] = "Hello";
char str2[20] = "World";
char result[40];
// ต้องใช้ functions จาก string.h
strcpy(result, str1); // Copy
strcat(result, " "); // Concatenate
strcat(result, str2);
printf("%s\n", result); // Hello World
printf("Length: %lu\n", strlen(result));
// เปรียบเทียบ String - ต้องใช้ strcmp()
if (strcmp(str1, "Hello") == 0) {
printf("Strings are equal\n");
}
// ปัญหา: Buffer overflow ได้ง่าย
char small[5] = "Hi";
strcat(small, " There"); // Overflow! อันตราย!
return 0;
}
ปัญหาของ String ใน C:
- ต้องจัดการขนาดเองc
char name[50]; // ถ้าชื่อยาวกว่า 49 ตัว → Overflow! - ไม่มี bounds checkingc
char buffer[10]; strcpy(buffer, "This is a very long string"); // Overflow! - Syntax ยุ่งยากc
strcmp(s1, s2); // เปรียบเทียบ strcpy(dest, src); // คัดลอก strcat(dest, src); // ต่อ String
Java: String เป็น Object
javapublic class StringDemo {
public static void main(String[] args) {
// String เป็น object - จัดการอัตโนมัติ
String str1 = "Hello";
String str2 = "World";
// ต่อ String ง่าย - ใช้ + operator
String result = str1 + " " + str2;
System.out.println(result); // Hello World
// Length เป็น method
System.out.println("Length: " + result.length());
// เปรียบเทียบ String
if (str1.equals("Hello")) {
System.out.println("Strings are equal");
}
// String methods มากมาย
System.out.println(result.toLowerCase()); // hello world
System.out.println(result.toUpperCase()); // HELLO WORLD
System.out.println(result.substring(0, 5)); // Hello
System.out.println(result.replace("World", "Java")); // Hello Java
System.out.println(result.contains("World")); // true
System.out.println(result.startsWith("Hello")); // true
// Split string
String[] words = result.split(" ");
for (String word : words) {
System.out.println(word);
}
// ไม่มี buffer overflow - String จัดการขนาดอัตโนมัติ
String longText = "This is a very very very long string " +
"and Java handles it automatically!";
System.out.println(longText);
}
}
ข้อดี String ใน Java:
- ขนาดปรับอัตโนมัติ – ไม่ต้องกังวล buffer overflow
- Immutable – String ไม่สามารถแก้ไขได้ (ปลอดภัย, thread-safe)
- Rich API – methods มากมายใช้งานง่าย
- Operator overloading – ใช้
+ต่อ String ได้
สรุปความแตกต่างหลัก
| ด้าน | C (Procedural) | Java (Object-Oriented) |
|---|---|---|
| Paradigm | Procedural – เน้นฟังก์ชันและขั้นตอน | Object-Oriented – เน้น objects และความสัมพันธ์ |
| Code Organization | ข้อมูลแยกจากฟังก์ชัน | ข้อมูลและ methods รวมใน class |
| Memory Management | Manual (malloc/free) | Automatic (Garbage Collector) |
| Safety | ไม่มี bounds checking | Runtime checks ป้องกัน errors |
| Platform | Compile ต่าง platform ต่างไฟล์ | Compile ครั้งเดียว รันได้ทุก platform |
| String | Character array + functions | Object พร้อม methods |
| Complexity | เหมาะกับโปรแกรมเล็ก-กลาง | เหมาะกับระบบขนาดใหญ่ |
| Performance | เร็วกว่า (native code) | ช้ากว่าเล็กน้อย (JVM overhead) |
| Development Speed | ช้ากว่า (เขียนโค้ดมากขึ้น) | เร็วกว่า (framework, libraries) |
| Use Cases | Embedded, OS, Drivers | Enterprise, Mobile, Web |
เมื่อไหร่ควรใช้อะไร?
ใช้ C เมื่อ:
- Performance เป็นสิ่งสำคัญที่สุด
- Real-time systems
- Embedded systems (MCU, IoT devices ที่มี resource น้อย)
- Device drivers
- ต้องเข้าถึง Hardware โดยตรง
- Operating systems
- Firmware
- Hardware control
- Resource จำกัด
- Memory น้อย (< 1MB RAM)
- Storage น้อย
- Battery-powered devices
ตัวอย่าง:
- Linux Kernel
- Arduino firmware
- Microcontroller programming
- Device drivers
ใช้ Java เมื่อ:
- พัฒนาระบบขนาดใหญ่
- Enterprise applications
- Web services / APIs
- Microservices
- ต้องการ Portability
- รันได้หลาย platforms
- Cloud-native applications
- Development speed สำคัญ
- มี frameworks สำเร็จ (Spring, Hibernate)
- มี libraries มากมาย
- Safety features ลด bugs
- ทีมใหญ่ทำงานร่วมกัน
- Code maintainability
- OOP principles ช่วยจัดระเบียบ
ตัวอย่าง:
- E-Commerce platforms (Shopee, Lazada)
- Banking systems (SCB, Kbank)
- Android applications
- Big Data processing (Hadoop, Spark)
สรุป: Mindset Shift จาก C ไป Java
จาก:
- คิดแบบ “ขั้นตอน” (Step-by-step)
- ข้อมูลแยกจากฟังก์ชัน
- จัดการ memory เอง
- ใส่ใจ performance ทุก byte
ไปสู่:
- คิดแบบ “วัตถุ” (Objects)
- ข้อมูลและพฤติกรรมรวมกัน
- ให้ JVM จัดการ memory
- ใส่ใจ design, maintainability, team collaboration
การเรียน Java หลังจาก C ทำให้นักศึกษา:
✅ เข้าใจพื้นฐานการทำงานของคอมพิวเตอร์จาก C
✅ เรียนรู้วิธีคิดแบบ Object-Oriented ที่ใช้ในอุตสาหกรรมจริง
✅ พร้อมพัฒนาซอฟต์แวร์ระดับองค์กร
✅ เข้าใจ trade-offs ระหว่างภาษาต่างๆ และเลือกใช้ได้อย่างเหมาะสม
