Skip to content

Programming Paradigms

แต่ละกระบวนทัศน์การเขียนโปรแกรม (Programming Paradigm) มีจุดแข็งและจุดอ่อนที่แตกต่างกันไป ตารางด้านล่างแสดงการเปรียบเทียบระหว่างกระบวนทัศน์หลักๆ ที่นิยมใช้ในปัจจุบัน

กระบวนทัศน์ImperativeFunctionalObject-Oriented
แนวคิดหลักเน้นการสั่งคอมพิวเตอร์ว่า "ทำอย่างไร" โดยใช้คำสั่งที่เปลี่ยนแปลงสถานะของโปรแกรมเน้นการคำนวณผ่านการประเมินฟังก์ชันโดยไม่เปลี่ยนแปลงสถานะมองทุกอย่างเป็นวัตถุที่มีข้อมูลและพฤติกรรม
จุดแข็ง- เข้าใจง่าย
- ประสิทธิภาพสูง
- ควบคุมการทำงานได้ละเอียด
- ทดสอบง่าย
- ลดบั๊กที่เกิดจากสถานะ
- เหมาะกับการประมวลผลแบบขนาน
- เหมาะสำหรับระบบขนาดใหญ่
- ส่งเสริมการนำกลับมาใช้ใหม่
- ใกล้เคียงกับโลกจริง
จุดอ่อน- ยากต่อการดีบัก
- ยากต่อการทดสอบ
- มีโอกาสเกิดบั๊กสูง
- โค้ดอาจดูซับซ้อน
- การเรียนรู้ยากกว่า
- อาจมีประสิทธิภาพต่ำในบางกรณี
- อาจมีความซับซ้อนเกินจำเป็น
- การออกแบบที่ดีต้องใช้ประสบการณ์
- มีโอเวอร์เฮดในการทำงาน
ภาษาที่นิยมใช้C, Pascal, BASICHaskell, Clojure, ScalaJava, C++, Python

ตัวอย่างโค้ด

ts
// จุดเด่น: เข้าใจง่าย ตรงไปตรงมา ควบคุมการทำงานได้ละเอียด
// จุดด้อย: ยากต่อการทดสอบเพราะมีการเปลี่ยนแปลงสถานะ (state) ภายใน
function sumEvenNumbers(max: number): number {
  let sum = 0;
  for (let i = 1; i <= max; i++) {
    if (i % 2 === 0) {
      sum += i;
    }
  }
  return sum;
}

// การทดสอบต้องทำโดยเรียกฟังก์ชันและตรวจสอบค่าที่ส่งคืน
const result = sumEvenNumbers(100);
console.log(result === 2550 ? "ผ่าน" : "ไม่ผ่าน"); // ผ่าน
ts
// จุดเด่น: ฟังก์ชันบริสุทธิ์ (pure functions) ทดสอบง่าย คาดเดาผลลัพธ์ได้แม่นยำ
// จุดด้อย: อาจซับซ้อนสำหรับผู้เริ่มต้น และอาจมีประสิทธิภาพต่ำกว่าในบางกรณี
const generateRange = (start: number, end: number): number[] =>
  Array.from({ length: end - start + 1 }, (_, i) => start + i);

const isEven = (num: number): boolean => num % 2 === 0;

const sum = (numbers: number[]): number =>
  numbers.reduce((acc, val) => acc + val, 0);

// ฟังก์ชันย่อยแต่ละส่วนสามารถทดสอบแยกกันได้อย่างอิสระ
const sumOfEvens = (max: number): number =>
  sum(generateRange(1, max).filter(isEven));

// ทดสอบแต่ละฟังก์ชันแยกกันได้
console.log(isEven(2) === true ? "ผ่าน" : "ไม่ผ่าน"); // ผ่าน
console.log(sum([2, 4, 6]) === 12 ? "ผ่าน" : "ไม่ผ่าน"); // ผ่าน
console.log(sumOfEvens(100) === 2550 ? "ผ่าน" : "ไม่ผ่าน"); // ผ่าน
ts
// จุดเด่น: แยกข้อมูลและพฤติกรรมเป็นหมวดหมู่ ขยายฟังก์ชันได้ง่ายผ่านการสืบทอด
// จุดด้อย: อาจมีความซับซ้อนเกินจำเป็นสำหรับงานง่ายๆ ทดสอบยากกว่าเพราะต้องสร้าง objects

// สร้าง interface เพื่อง่ายต่อการทดสอบด้วย mock objects
interface NumberCollection {
  getNumbers(): number[];
}

class NumberRange implements NumberCollection {
  constructor(private start: number, private end: number) {}

  getNumbers(): number[] {
    return Array.from(
      { length: this.end - this.start + 1 },
      (_, i) => this.start + i,
    );
  }
}

class NumberCalculator {
  constructor(private collection: NumberCollection) {}

  getSumOfEvens(): number {
    return this.collection.getNumbers()
      .filter(num => num % 2 === 0)
      .reduce((acc, val) => acc + val, 0);
  }
}

// ทดสอบโดยใช้ mock object
const mockCollection: NumberCollection = {
  getNumbers: () => [1, 2, 3, 4, 5, 6],
};
const calculator = new NumberCalculator(mockCollection);
console.log(calculator.getSumOfEvens() === 12 ? "ผ่าน" : "ไม่ผ่าน"); // ผ่าน

// ทดสอบกับข้อมูลจริง
const realCalculator = new NumberCalculator(new NumberRange(1, 100));
console.log(realCalculator.getSumOfEvens() === 2550 ? "ผ่าน" : "ไม่ผ่าน"); // ผ่าน

Comparison

Testing

ts
// จุดเด่น: เข้าใจง่าย ตรงไปตรงมา ควบคุมการทำงานได้ละเอียด
// จุดด้อย: ยากต่อการทดสอบเพราะมีการเปลี่ยนแปลงสถานะ (state) ภายใน
function sumEvenNumbers(max: number): number {
  let sum = 0;
  for (let i = 1; i <= max; i++) {
    if (i % 2 === 0) {
      sum += i;
    }
  }
  return sum;
}

// การทดสอบต้องทำโดยเรียกฟังก์ชันและตรวจสอบค่าที่ส่งคืน
const result = sumEvenNumbers(100);
console.log(result === 2550 ? "ผ่าน" : "ไม่ผ่าน"); // ผ่าน
ts
// จุดเด่น: ฟังก์ชันบริสุทธิ์ (pure functions) ทดสอบง่าย คาดเดาผลลัพธ์ได้แม่นยำ
// จุดด้อย: อาจซับซ้อนสำหรับผู้เริ่มต้น และอาจมีประสิทธิภาพต่ำกว่าในบางกรณี
const generateRange = (start: number, end: number): number[] =>
  Array.from({ length: end - start + 1 }, (_, i) => start + i);

const isEven = (num: number): boolean => num % 2 === 0;

const sum = (numbers: number[]): number =>
  numbers.reduce((acc, val) => acc + val, 0);

// ฟังก์ชันย่อยแต่ละส่วนสามารถทดสอบแยกกันได้อย่างอิสระ
const sumOfEvens = (max: number): number =>
  sum(generateRange(1, max).filter(isEven));

// ทดสอบแต่ละฟังก์ชันแยกกันได้
console.log(isEven(2) === true ? "ผ่าน" : "ไม่ผ่าน"); // ผ่าน
console.log(sum([2, 4, 6]) === 12 ? "ผ่าน" : "ไม่ผ่าน"); // ผ่าน
console.log(sumOfEvens(100) === 2550 ? "ผ่าน" : "ไม่ผ่าน"); // ผ่าน
ts
// จุดเด่น: แยกข้อมูลและพฤติกรรมเป็นหมวดหมู่ ขยายฟังก์ชันได้ง่ายผ่านการสืบทอด
// จุดด้อย: อาจมีความซับซ้อนเกินจำเป็นสำหรับงานง่ายๆ ทดสอบยากกว่าเพราะต้องสร้าง objects

// สร้าง interface เพื่อง่ายต่อการทดสอบด้วย mock objects
interface NumberCollection {
  getNumbers(): number[];
}

class NumberRange implements NumberCollection {
  constructor(private start: number, private end: number) {}

  getNumbers(): number[] {
    return Array.from(
      { length: this.end - this.start + 1 },
      (_, i) => this.start + i,
    );
  }
}

class NumberCalculator {
  constructor(private collection: NumberCollection) {}

  getSumOfEvens(): number {
    return this.collection.getNumbers()
      .filter(num => num % 2 === 0)
      .reduce((acc, val) => acc + val, 0);
  }
}

// ทดสอบโดยใช้ mock object
const mockCollection: NumberCollection = {
  getNumbers: () => [1, 2, 3, 4, 5, 6],
};
const calculator = new NumberCalculator(mockCollection);
console.log(calculator.getSumOfEvens() === 12 ? "ผ่าน" : "ไม่ผ่าน"); // ผ่าน

// ทดสอบกับข้อมูลจริง
const realCalculator = new NumberCalculator(new NumberRange(1, 100));
console.log(realCalculator.getSumOfEvens() === 2550 ? "ผ่าน" : "ไม่ผ่าน"); // ผ่าน

Debugging

ts
// จุดเด่น: ง่ายต่อการเพิ่ม log เพื่อติดตามการทำงานในแต่ละขั้นตอน
// จุดด้อย: ต้องเพิ่ม log หลายจุด และอาจทำให้โค้ดรกเมื่อต้องการดีบัก
function sumEvenNumbers(max: number): number {
  let sum = 0;
  console.log(`เริ่มต้นคำนวณผลรวมเลขคู่จาก 1 ถึง ${max}`);

  for (let i = 1; i <= max; i++) {
    console.log(`กำลังตรวจสอบ i = ${i}`);

    if (i % 2 === 0) {
      const oldSum = sum;
      sum += i;
      console.log(`${i} เป็นเลขคู่: ${oldSum} + ${i} = ${sum}`);
    } else {
      console.log(`${i} เป็นเลขคี่: ข้าม`);
    }
  }

  console.log(`ผลรวมสุดท้าย: ${sum}`);
  return sum;
}

sumEvenNumbers(10); // ลองกับตัวเลขเล็กๆ ก่อนเพื่อดีบัก
ts
// จุดเด่น: สามารถตรวจสอบข้อมูลโดยไม่เปลี่ยนแปลงโครงสร้างโค้ด ใช้ฟังก์ชัน debug helper
// จุดด้อย: อาจต้องสร้างฟังก์ชัน helper เพิ่มเติมสำหรับการดีบัก

// ฟังก์ชัน debug helper ที่ไม่เปลี่ยนแปลงข้อมูล
const debug = <T>(label: string, value: T): T => {
  console.log(`${label}:`, value);
  return value;
};

const sumOfEvens = (max: number): number =>
  debug('ผลลัพธ์สุดท้าย',
    Array.from({ length: max }, (_, i) => i + 1)
      |> debug('สร้างช่วงตัวเลข', #)
      |> #.filter(num => 
          debug(`ตรวจสอบ ${num}`, num % 2 === 0 ? `${num} เป็นเลขคู่` : `${num} เป็นเลขคี่`)
          && num % 2 === 0
        )
      |> debug('กรองเฉพาะเลขคู่', #)
      |> #.reduce((acc, val) => 
          debug('การรวม', `${acc} + ${val} = ${acc + val}`), 0
        )
  );

sumOfEvens(10); // ลองกับตัวเลขเล็กๆ ก่อนเพื่อดีบัก