ออบเจ็กต์และโครงสร้างข้อมูลมีบทบาทสำคัญในการพัฒนาซอฟต์แวร์ และการปฏิบัติตามแนวทางปฏิบัติของโค้ดที่สะอาด เราจะสามารถเพิ่มความสามารถในการอ่าน การบำรุงรักษา และความยืดหยุ่นของโค้ดของเราได้ เราจะเจาะลึกเรื่องการห่อหุ้ม, ตัวดัดแปลงการเข้าถึง, วิธี getter และ setter, องค์ประกอบเหนือการสืบทอด, การลดสถานะที่ไม่แน่นอนให้เหลือน้อยที่สุด และ Single Responsibility Principle (SRP) มีเหตุผลที่เราเก็บตัวแปรของเราไว้เป็นส่วนตัว เราไม่ต้องการให้ใครพึ่งพาพวกเขา เราต้องการรักษาเสรีภาพในการเปลี่ยนแปลงประเภทหรือการนำไปใช้โดยไม่ได้ตั้งใจหรือตามแรงกระตุ้น

1. การห่อหุ้มข้อมูล

การห่อหุ้มหมายถึงการรวมข้อมูลและพฤติกรรมที่เกี่ยวข้องเข้าด้วยกันภายในคลาสหรืออินเทอร์เฟซ ช่วยให้เราสามารถควบคุมการเข้าถึงข้อมูลและซ่อนรายละเอียดการใช้งานภายในได้ ด้วยการห่อหุ้มข้อมูล เราสามารถจัดเตรียมอินเทอร์เฟซที่ชัดเจนสำหรับการโต้ตอบกับวัตถุได้

class Car {
  private brand: string;
  private model: string;
  
  constructor(brand: string, model: string) {
    this.brand = brand;
    this.model = model;
  }
  
  public getBrand(): string {
    return this.brand;
  }
  
  public getModel(): string {
    return this.model;
  }
}

ในตัวอย่างนี้ เราสรุปคุณสมบัติ brand และ model ภายในคลาส Car เราใช้ตัวแก้ไขการเข้าถึงส่วนตัวเพื่อจำกัดการเข้าถึงคุณสมบัติเหล่านี้โดยตรง แต่เราจัดเตรียมวิธีการรับสาธารณะ (getBrand() และ getModel()) เพื่อเข้าถึงค่า การห่อหุ้มนี้ช่วยให้แน่ใจว่ารายละเอียดภายในของคลาส Car ถูกซ่อนอยู่ และเข้าถึงข้อมูลผ่านอินเทอร์เฟซที่มีการควบคุม

2. หลีกเลี่ยงวิธีการ Getter และ Setter

เมธอด getter และ setter แบบดั้งเดิมใช้เพื่อเข้าถึงและเปลี่ยนคุณสมบัติของอ็อบเจ็กต์ อย่างไรก็ตาม สิ่งเหล่านี้สามารถนำไปสู่การละเมิด Law of Demeter ส่งผลให้เกิดโค้ดที่เชื่อมโยงกันอย่างแน่นหนา แต่เราสามารถใช้คุณสมบัติซึ่งมีไวยากรณ์ที่สะอาดตาและส่งเสริมให้อ่านง่ายแทน

class Rectangle {
  private width: number;
  private height: number;
  
  constructor(width: number, height: number) {
    this.width = width;
    this.height = height;
  }
  
  public get area(): number {
    return this.width * this.height;
  }
  
  public setDimensions(width: number, height: number): void {
    this.width = width;
    this.height = height;
  }
}

ในตัวอย่างนี้ เราใช้คุณสมบัติ area เพื่อคำนวณพื้นที่ของสี่เหลี่ยมผืนผ้า แทนที่จะเรียกวิธีแยกเช่น calculateArea() เราสามารถเข้าถึงพื้นที่ได้โดยตรงเหมือนกับเป็นทรัพย์สินปกติ นอกจากนี้ เรายังมีเมธอด setDimensions() เพื่ออัปเดตขนาดของสี่เหลี่ยม ซึ่งช่วยให้ไม่จำเป็นต้องใช้เมธอด getter และ setter แยกกัน

3. ชอบองค์ประกอบมากกว่ามรดก

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

class Engine {
  public start(): void {
    console.log('Engine started');
  }
}

class Car {
  private engine: Engine;
  
  constructor() {
    this.engine = new Engine();
  }
  
  public startEngine(): void {
    this.engine.start();
  }
}

ในตัวอย่างนี้ เราให้ความสำคัญกับการเรียบเรียงมากกว่าการสืบทอด แทนที่จะสร้างคลาส Car ที่ขยายคลาส Engine เราสร้างคลาส Engine แยกต่างหากและรวมไว้เป็นคุณสมบัติภายในคลาส Car วิธีการจัดองค์ประกอบนี้ช่วยให้เราสลับการใช้งานเอ็นจิ้นที่แตกต่างกันได้อย่างง่ายดายโดยไม่ส่งผลกระทบต่อคลาส Car

4. การลดสถานะที่ไม่แน่นอนให้เหลือน้อยที่สุด

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

function calculateTotalPrice(prices) {
  let totalPrice = 0;

  for (const price of prices) {
    totalPrice += price;
  }

  return totalPrice;
}

const prices = [10, 20, 30, 40];
const total = calculateTotalPrice(prices);

ในตัวอย่าง JavaScript นี้ เราคำนวณราคารวมโดยการรวมอาร์เรย์ของราคา ตัวแปร totalPrice ถูกประกาศโดยใช้คีย์เวิร์ด let เพื่อให้สามารถเปลี่ยนแปลงภายในลูปได้ อย่างไรก็ตาม อาร์เรย์ prices ยังคงไม่เปลี่ยนรูป ด้วยการลดสถานะที่ไม่แน่นอนให้เหลือน้อยที่สุดและหลีกเลี่ยงการกลายพันธุ์ของตัวแปรที่ไม่จำเป็น เราจึงสร้างโค้ดที่สามารถคาดเดาและบำรุงรักษาได้มากขึ้น

5. การปฏิบัติตามหลักการความรับผิดชอบเดียว (SRP)

หลักการความรับผิดชอบเดี่ยว (SRP) ระบุว่าวัตถุหรือหน้าที่ควรมีความรับผิดชอบเดี่ยวหรือทำสิ่งใดสิ่งหนึ่งได้ดี ด้วยการยึดมั่นใน SRP เราจึงสร้างส่วนประกอบที่มีขนาดเล็กลงและเน้นมากขึ้น ซึ่งง่ายต่อการเข้าใจ ทดสอบ และบำรุงรักษา

class Logger {
  public logError(message: string): void {
    // Log the error message to a file or console
  }
  
  public logMetric(metric: string, value: number): void {
    // Log the metric and its value to a monitoring system
  }
}

class PaymentProcessor {
  private logger: Logger;
  
  constructor(logger: Logger) {
    this.logger = logger;
  }
  
  public processPayment(amount: number): void {
    try {
      // Process the payment logic
    } catch (error) {
      this.logger.logError('Payment processing failed');
    }
  }
}

ในตัวอย่างนี้ เราสาธิต SRP โดยการแยกข้อกังวลออกเป็นสองคลาส: Logger และ PaymentProcessor คลาส Logger มีหน้าที่รับผิดชอบในการบันทึกข้อผิดพลาดและตัวชี้วัด ในขณะที่คลาส PaymentProcessor มุ่งเน้นไปที่การประมวลผลการชำระเงินเพียงอย่างเดียว โดยการแยกความรับผิดชอบ แต่ละชั้นเรียนจะมีความสอดคล้องและเข้าใจได้ง่ายขึ้น

บทสรุป

ออบเจ็กต์และโครงสร้างข้อมูลเป็นองค์ประกอบพื้นฐานของการพัฒนาซอฟต์แวร์ ด้วยการใช้หลักการของโค้ดที่สะอาดกับออบเจ็กต์และโครงสร้างข้อมูล เราสามารถปรับปรุงความสามารถในการอ่าน การบำรุงรักษา และความยืดหยุ่นของโค้ดของเราได้ ตลอดบทนี้ เราได้สำรวจการห่อหุ้มข้อมูล การหลีกเลี่ยงวิธีการ getter และ setter นิยมองค์ประกอบมากกว่าการสืบทอด ลดสถานะที่ไม่แน่นอนให้เหลือน้อยที่สุด และยึดมั่นใน Single Responsibility Principle (SRP) ด้วยการปฏิบัติตามหลักการเหล่านี้และปรับให้เข้ากับข้อกำหนดเฉพาะของโครงการของเรา เราจะสามารถเขียนโค้ดที่สะอาดตาและแข็งแกร่งยิ่งขึ้นได้