แม้ว่าโปรแกรมเมอร์

รูปแบบการออกแบบ: รูปแบบโครงสร้างของคลาสการออกแบบและวัตถุ

อะแดปเตอร์, มัณฑนากร, พร็อกซี, ผู้เชี่ยวชาญด้านข้อมูล, คอมโพสิต, บริดจ์, คัปปลิ้งต่ำ, ฟลายเวท, รูปแบบที่ได้รับการป้องกัน และซุ้ม

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

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

รูปแบบการออกแบบโครงสร้างมี 10 ประเภทดังนี้

  • รูปแบบอะแดปเตอร์
  • ลวดลายสะพาน
  • รูปแบบคอมโพสิต
  • แพทเทิร์นมัณฑนากร
  • คลัปต่ำ
  • รูปแบบฟลายเวท
  • รูปแบบซุ้ม
  • รูปแบบพร็อกซี
  • ผู้เชี่ยวชาญด้านข้อมูล
  • รูปแบบที่ได้รับการคุ้มครอง

ABCD (อะแดปเตอร์, บริดจ์, คอมโพสิต, มัณฑนากร)

รูปแบบอะแดปเตอร์

เจตนา

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

วิธีแก้ปัญหา

ใช้อินเทอร์เฟซที่ไคลเอ็นต์รู้จักและให้สิทธิ์เข้าถึงอินสแตนซ์ของคลาสที่ไคลเอ็นต์ไม่รู้จัก

  • AdapterClient: ไคลเอ็นต์โค้ด
  • อะแดปเตอร์: คลาสอะแดปเตอร์ที่ส่งต่อการเรียกไปยังอะแดปเตอร์
  • Adaptee: จำเป็นต้องดัดแปลงโค้ดเก่า
  • เป้าหมาย: อินเทอร์เฟซใหม่ที่จะสนับสนุน

ตัวอย่างจากโลกแห่งความเป็นจริง

กำลังไฟเดิมคือ 220 แรงดันไฟฟ้า และต้องปรับเป็น 100 แรงดันไฟฟ้าจึงจะใช้งานได้

รหัสต่อไปนี้ช่วยแก้ปัญหานี้ได้ โดยกำหนด HighVoltagePlug (อะแดปเตอร์), อินเทอร์เฟซ Plug (เป้าหมาย), AdapterPlug (อะแดปเตอร์)

เป้าหมาย: Plug.java

public interface Plug {
    public int recharge();
}

อะแดปเตอร์: HighVoltagePlug.java

public class HighVoltagePlug{
    public int recharge() {
        //Power is 220 Voltage
        return 220; 
    }
}

อะแดปเตอร์: AdapterPlug.java

public class AdapterPlug implements Plug {
    @Override
    public int recharge() {
        HighVoltagePlug bigplug = new HighVoltagePlug();
        int v = bigplug.recharge();
        v = v - 120;
        return v;
    }
}

AdapterClient: AdapterClient.java

public class AdapterClient {
    public static void main(String[] args) {
        HighVoltagePlug oldPlug = new HighVoltagePlug();
        System.out.println(plug.recharge() + " too much voltage");
        
        Plug newPlug = new AdapterPlug();
        System.out.println("Adapter into " + plug.recharge() + " voltage");
    }
}

กรณีการใช้งาน

เมื่อคุณต้องการใช้คลาสที่มีอยู่และอินเทอร์เฟซไม่ตรงกับอินเทอร์เฟซที่คุณต้องการ

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

โดยที่ต้องมีการแปลอินเทอร์เฟซระหว่างหลายแหล่ง

ลวดลายสะพาน

เจตนา

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

วิธีแก้ปัญหา

แผนภาพต่อไปนี้แสดงการใช้งานบริดจ์ที่เป็นไปได้

  • นามธรรม: นี่คือองค์ประกอบที่เป็นนามธรรม
  • ตัวดำเนินการ: นี่คือการนำไปใช้เชิงนามธรรม
  • RefinedAbstraction: นี่คือองค์ประกอบที่ได้รับการปรับปรุง
  • ConcreateImplementors: สิ่งเหล่านี้คือการใช้งานที่เป็นรูปธรรม

ตัวอย่างจากโลกแห่งความเป็นจริง

ผู้คนต่างสามารถสวมใส่เสื้อผ้าที่แตกต่างกัน เช่น ผู้ชาย ผู้หญิง เด็กผู้ชาย และเด็กผู้หญิง

AbstractImpl: Person.java

public abstract class Person {

    protected String name;
    protected Clothing cloth;

    public Person(String name) {
        super();
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    public Clothing getCloth() {
        return cloth;
    }

    public void setCloth(Clothing cloth) {
        this.cloth = cloth;
    }
}

ผู้ดำเนินการ: Clothing.java

public abstract class Clothing {

    protected String name;
    
    public Clothing(String name) {
        super();
        this.name = name;
    }
    public String getName() {
        return name;
    }

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

ConcreateImplementor: Jacket.java

public class Jacket extends Clothing {

    public Jacket(String name) {
        super(name);
    }
}

RefinedAbstraction: Woman.java

public class Woman extends Person {
    
    public Woman(String name) {
        super(name);
    }

    @Override
    public void dress() {
        System.out.println(name + " wear " + cloth.getName());
    }
}

ลูกค้า: BridgeClient.java

public class BridgeClient {
    public static void main(String[] args) {

        Person woman = new Woman("Woman");

        Clothing jacket = new Jacket("Jacket");

        // a woman wear jacket
        woman.setCloth(jacket); 
        woman.dress();
    }
}

กรณีการใช้งาน

หลีกเลี่ยงการผูกมัดอย่างถาวรระหว่างสิ่งที่เป็นนามธรรมและการนำไปปฏิบัติ

ทั้งนามธรรมและการใช้งานควรขยายได้โดยใช้คลาสย่อย

การเปลี่ยนแปลงในการใช้งานนามธรรมไม่ควรมีผลกระทบต่อลูกค้า นั่นคือคุณไม่ควรต้องคอมไพล์โค้ดใหม่

รูปแบบคอมโพสิต

เจตนา

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

วิธีแก้ปัญหา

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

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

ตัวอย่างจากโลกแห่งความเป็นจริง

ในองค์กร มีผู้จัดการทั่วไป และภายใต้ผู้จัดการทั่วไปอาจมีผู้จัดการ และภายใต้ผู้จัดการก็สามารถเป็นนักพัฒนาได้ ตอนนี้คุณสามารถตั้งค่าโครงสร้างแบบต้นไม้และขอให้แต่ละโหนดดำเนินการทั่วไป เช่น printStructures()

ส่วนประกอบ: IEmployee.java

interface IEmployee {
    
    void printStructures();
    int getEmployeeCount();

}

คอมโพสิต: CompositeEmployee.java

class CompositeEmployee implements IEmployee {

    private int employeeCount=0;
    private String name;
    private String dept;

    //The container for child objects
    private List<IEmployee> controls;

    public CompositeEmployee(String name, String dept){

        this.name = name;
        this.dept = dept;
        controls = new ArrayList<IEmployee>();

    }

    public void addEmployee(IEmployee e){

        controls.add(e);

    }

    public void removeEmployee(IEmployee e){

        controls.remove(e);

    }

    @Override
    public void printStructures(){

        System.out.println("\t" + this.name + " works in  " + this.dept);

        for(IEmployee e: controls){
            e.printStructures();
        }

    }

    @Override
    public int getEmployeeCount(){
        employeeCount=controls.size();
        for(IEmployee e: controls){
            employeeCount+=e.getEmployeeCount();
        }
        return employeeCount;
    }

}

ใบไม้: Employee.java

class Employee implements IEmployee{

    private String name;
    private String dept;
    private int employeeCount=0;

    public Employee(String name, String dept){

        this.name = name;
        his.dept = dept;

    }

    @Override
    public void printStructures(){
        System.out.println("\t\t"+this.name + " works in  " + this.dept);
    }

    @Override
    public int getEmployeeCount(){
        return employeeCount;

    }

}

กรณีการใช้งาน

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

คุณสามารถใช้รูปแบบนี้กับโครงสร้างที่สามารถมีความซับซ้อนในระดับใดก็ได้

แพทเทิร์นมัณฑนากร

เจตนา

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

วิธีแก้ปัญหา

มันเปลี่ยนการทำงานของออบเจ็กต์ในลักษณะที่โปร่งใสต่อไคลเอนต์โดยใช้อินสแตนซ์ของคลาสย่อยของคลาสดั้งเดิมที่มอบหมายการดำเนินการให้กับออบเจ็กต์ดั้งเดิม

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

ตัวอย่างจากโลกแห่งความเป็นจริง

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

กรณีการใช้งาน

เมื่อเพิ่มความรับผิดชอบให้กับแต่ละวัตถุแบบไดนามิกและโปร่งใสโดยไม่ส่งผลกระทบต่อวัตถุอื่น

เมื่อคุณต้องการเพิ่มความรับผิดชอบให้กับวัตถุที่คุณอาจต้องการเปลี่ยนแปลงในอนาคต

โดยที่การขยายโดยคลาสย่อยแบบคงที่นั้นทำไม่ได้

F2P (ฟลายเวท, เฟซาด, พร็อกซี)

รูปแบบฟลายเวท

เจตนา

รูปแบบฟลายเวทช่วยลดจำนวนวัตถุที่มีรายละเอียดระดับต่ำภายในระบบด้วยการแบ่งปันวัตถุ

วิธีแก้ปัญหา

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

  • ลูกค้า: รหัสลูกค้า
  • FlyweightFactory: สิ่งนี้จะสร้างฟลายเวทหากไม่มีอยู่ และส่งคืนจากพูลหากมีอยู่
  • ฟลายเวท: นามธรรมฟลายเวท
  • ConcreateFlyweight: รุ่นฟลายเวตที่ออกแบบมาให้มีสถานะที่ใช้ร่วมกันกับคู่แข่ง

ตัวอย่างจากโลกแห่งความเป็นจริง

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

กรณีการใช้งาน

คุณควรใช้รูปแบบฟลายเวทเมื่อเงื่อนไขทั้งหมดต่อไปนี้เป็นจริง

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

รูปแบบซุ้ม

เจตนา

รูปแบบ Façade จัดเตรียมอินเทอร์เฟซแบบรวมให้กับกลุ่มของอินเทอร์เฟซในระบบย่อย

วิธีแก้ปัญหา

มันกำหนดอินเทอร์เฟซระดับสูงกว่าที่ทำให้ระบบย่อยใช้งานง่ายขึ้นเนื่องจากคุณมีเพียงอินเทอร์เฟซเดียวเท่านั้น

ตัวอย่างจากโลกแห่งความเป็นจริง

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

กรณีการใช้งาน

เมื่อคุณต้องการจัดเตรียมอินเทอร์เฟซที่เรียบง่ายให้กับระบบย่อยที่ซับซ้อน

ในกรณีที่มีการขึ้นต่อกันมากมายระหว่างไคลเอนต์และคลาสการใช้งานของสิ่งที่เป็นนามธรรม

เมื่อคุณต้องการเลเยอร์ระบบย่อยของคุณ

รูปแบบพร็อกซี

รูปแบบพร็อกซีมีการใช้งานหลายประเภท โดยที่พร็อกซีระยะไกลและพร็อกซีเสมือนเป็นประเภทที่พบบ่อยที่สุด

เจตนา

รูปแบบพร็อกซีจัดเตรียมวัตถุตัวแทนหรือตัวยึดเพื่อควบคุมการเข้าถึงวัตถุต้นฉบับ

วิธีแก้ปัญหา

ตัวอย่างจากโลกแห่งความเป็นจริง

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

กรณีการใช้งาน

คุณต้องมีการอ้างอิงวัตถุที่หลากหลายหรือซับซ้อนมากกว่าตัวชี้แบบธรรมดา

รูปแบบของ GRASP

GRASP ตั้งชื่อและอธิบายหลักการพื้นฐานในการมอบหมายความรับผิดชอบ

ผู้เชี่ยวชาญด้านข้อมูล

เราดูที่รูปแบบผู้เชี่ยวชาญ (หรือรูปแบบผู้เชี่ยวชาญด้านข้อมูล) อันนี้ค่อนข้างเรียบง่ายและสำคัญมาก

เจตนา

หลักการพื้นฐานในการกำหนดความรับผิดชอบให้กับวัตถุคืออะไร?

วิธีแก้ปัญหา

มอบหมายความรับผิดชอบให้ชั้นเรียนซึ่งมีข้อมูลที่จำเป็นต่อการตอบสนอง

ตัวอย่างจากโลกแห่งความเป็นจริง

พิจารณาเกมผูกขาด สมมติว่าวัตถุต้องการอ้างอิง Square โดยใช้ชื่อของมัน ใครเป็นผู้รับผิดชอบในการรู้จักจัตุรัสตามชื่อของมัน?

ผู้สมัครที่มีแนวโน้มมากที่สุดคือคณะกรรมการเนื่องจากประกอบด้วยสี่เหลี่ยมจัตุรัส

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

กรณีการใช้งาน

คิดว่าออบเจ็กต์ในแบบจำลองการออกแบบของคุณเป็นผู้ปฏิบัติงานที่คุณจัดการ หากคุณมีงานที่ต้องมอบหมาย คุณจะมอบหมายงานให้ใคร?

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

รูปแบบที่ได้รับการคุ้มครอง

เจตนา

จะออกแบบออบเจ็กต์ ระบบย่อย และระบบอย่างไรเพื่อให้ความแปรผันหรือความไม่เสถียรในองค์ประกอบเหล่านี้ไม่ส่งผลกระทบที่ไม่พึงประสงค์ต่อองค์ประกอบอื่นๆ

วิธีแก้ปัญหา

ระบุจุดของการแปรผันหรือความไม่แน่นอนที่คาดการณ์ไว้ มอบหมายความรับผิดชอบเพื่อสร้างส่วนต่อประสานที่มีความเสถียรรอบตัว

หลักการ “อย่าพูดคุยกับคนแปลกหน้า” ซึ่งระบุว่าวิธีการของวัตถุควรส่งข้อความ (เช่น การใช้วิธีการ) ของวัตถุที่คุ้นเคยโดยตรงเท่านั้น



ตัวอย่างจากโลกแห่งความเป็นจริง

การห่อหุ้มข้อมูล อินเทอร์เฟซ ความหลากหลาย ทางอ้อม และมาตรฐานได้รับแรงบันดาลใจจากความแปรผันที่ได้รับการคุ้มครอง

กรณีการใช้งาน

Protected Variations เป็นหลักการพื้นฐานที่กระตุ้นให้เกิดกลไกและรูปแบบส่วนใหญ่ในการเขียนโปรแกรมและการออกแบบ เพื่อให้มีความยืดหยุ่นและการป้องกันจากการเปลี่ยนแปลงของข้อมูล พฤติกรรม ฮาร์ดแวร์ ส่วนประกอบซอฟต์แวร์ ระบบปฏิบัติการ และอื่นๆ

บทสรุป

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

ง่ายใช่มั้ย?