รูปแบบการออกแบบกลยุทธ์

Dec 01 2022
สรุปสิ่งที่แตกต่างกันไป
สมมติว่าเรามีแอปพลิเคชันที่ให้ลูกค้าชำระค่าสินค้าหรือรับบริการ แอปพลิเคชันมีวิธีการชำระเงินมากกว่า 1 วิธี ลูกค้าสามารถใช้เงินสด บัตรวีซ่า หรือกระเป๋าเงิน เราสามารถทำได้ดังนี้ ไม่จริง เรามีการชำระเงินสามประเภท และในแต่ละกรณีมีการใช้งานที่แตกต่างกัน เราจึงมีพฤติกรรมเดียว “จ่ายเงิน” ด้วยวิธีต่างๆ กัน ดังนั้นจะเกิดอะไรขึ้นหากเรานำสิ่งที่แตกต่างไปจัดแยกประเภทและใช้อินเทอร์เฟซทั่วไปสำหรับพฤติกรรมทั่วไป

สมมติว่าเรามีแอปพลิเคชันที่ให้ลูกค้าชำระค่าสินค้าหรือรับบริการ

แอปพลิเคชันมีวิธีการชำระเงินมากกว่า 1 วิธี ลูกค้าสามารถใช้เงินสด บัตรวีซ่า หรือกระเป๋าเงิน เราสามารถทำได้ดังนี้

public class Customer {

  public void pay(String paymentType) {
    if (StringUtils.isEmpty(paymentType)) {
      throw new RuntimeException("please add payment method");
    }
      // pay according to payment type
    if (paymentType.equalsIgnoreCase("cash")) {
      // pay in cash steps ( Algorithm )
    } else if (paymentType.equalsIgnoreCase("visa")) {
      // use the visa for payment steps ( Algorithm )
    } else if (paymentType.equalsIgnoreCase("wallet")) {
      // use the wallet for payment steps ( Algorithm )
    } else {
      throw new RuntimeException(paymentType + "payment type is not available");
    }
  }
}

public class Client {
    public static void main(String[] args) {
        Customer customer = new Customer();
        customer.pay("cash");
    }
}

ไม่จริง

  1. หากคุณต้องการเพิ่มวิธีการชำระเงินใหม่ คุณต้องเปลี่ยนคลาสนี้ และทำให้การออกแบบของคุณไม่ยืดหยุ่นสำหรับการเปลี่ยนแปลงใหม่ใดๆ การใช้งานนี้ถือว่าละเมิดหลักการออกแบบ SOLID ข้อหนึ่งซึ่งก็คือ: “ เอนทิตีซอฟต์แวร์ควรเปิดอยู่ เพื่อขยายแต่ปิดเพื่อแก้ไข
  2. ตรรกะส่วนใหญ่ในคลาสนี้ไม่ได้ใช้ เมื่อลูกค้าเลือกประเภทการชำระเงินแล้ว ดังนั้นหากลูกค้าต้องการชำระเป็นเงินสด รหัสที่ใช้จัดการวีซ่าและกระเป๋าเงินจะไม่ถูกใช้
  3. ตามที่คุณเห็นในการออกแบบนี้ คุณจะต้องจัดการกับอินพุตประเภทการชำระเงินที่เป็นไปได้ทั้งหมด แม้ว่าจะไม่สมเหตุสมผล เช่น ประเภทการชำระเงินที่เป็นโมฆะ หรือประเภทการชำระเงินที่ว่างเปล่า และคุณสามารถจำกัดให้ลูกค้าเลือกประเภทการชำระเงินที่ถูกต้องได้อย่างง่ายดาย โดยใช้รูปแบบกลยุทธ์

เรามีประเภทการชำระเงินสามประเภท และในแต่ละกรณีมีการใช้งานที่แตกต่างกัน ดังนั้นเราจึงมีพฤติกรรมแบบหนึ่ง"จ่าย"ด้วยวิธีการที่แตกต่างกัน ดังนั้นจะเกิดอะไรขึ้นหากเรานำสิ่งที่แตกต่างออกไปและจัดไว้ในคลาสที่แยกจากกันและใช้อินเทอร์เฟซทั่วไปสำหรับพฤติกรรมทั่วไป .

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

public interface Payment {
    void pay();
}
public class CashPayment implements Payment {
    @Override
    public void pay() {
        // pay in cash steps ( Algorithm )
    }
}
public class VisaPayment implements Payment {
    @Override
    public void pay() {
        // use the visa for payment steps ( Algorithm )
    }
}
public class WalletPayment implements Payment {
    @Override
    public void pay() {
        // use the wallet for payment steps ( Algorithm )
    }
}

public class Customer {
    private Payment payment;

    public Customer(Payment payment) {
        this.payment = payment;
    }

    public void setPaymentType(Payment payment) {
        this.payment = payment;
    }

    public void pay() {
        payment.pay();
    }
}

public class Client {
    public static void main(String[] args) {
        Customer customer = new Customer(new VisaPayment());
        customer.pay();
        // you can change the payment type at runtime
        customer.setPaymentType(new CashPayment());
        customer.pay();
    }
}

ไดอะแกรม UML สำหรับตัวอย่างการชำระเงินของลูกค้า

หลักการออกแบบ

จากการสนทนาก่อนหน้านี้ เราสามารถสรุปหลักการออกแบบได้สี่ประการ:

  1. ออกแบบสำหรับส่วนต่อประสานไม่ใช่เพื่อการใช้งาน
  2. สรุปสิ่งที่แตกต่างออกไป
  3. เอนทิตีซอฟต์แวร์ควรเปิดเพื่อขยาย แต่ปิดเพื่อแก้ไข
  4. ใช้องค์ประกอบเหนือการสืบทอด

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

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