[Tut 7 - Design Pattern] Composite

Composite Pattern là gì ?

Composite là một mẫu thiết kế thuộc nhóm cấu trúc, cho phép thực hiện các tương tác với tất cả đối tượng trong mẫu tương tự nhau.
Một ví dụ thực tế có thể áp dụng cho mẫu này đó là việc thiết kế một mạch điện. Với một mạch điện đơn giản có thể chứa các thiết bị đơn lẻ, mắc nối tiếp hoặc song song với nhau, và cả những mạch điện phức tạp trong đó là các mạch con được kết hợp với nhau thay vì các thiết bị đơn lẻ. Cho dù mạch này được cấu tạo như thế nào, thì việc tính điện trở của mạch chỉ nằm trong 2 trường hợp:

  • Mắc nối tiếp: R = R1 + R2 + … + Rn.
  • Mắc song song: 1/R = 1/R1 + 1/R2 + … + 1/Rn.

(Nếu có thắc mắc về các công thức này bạn có thể tìm hiểu thêm sách vật lý phổ thông).

Với công thức này, việc lập trình theo cách suy nghĩ đơn giản nhất có thể sẽ khiến cho việc hiện thực hóa trở nên khó khăn hơn rất nhiều, trong đó chúng ta có một cách tiếp cận khá đơn giản mà phức tạp như:
  • Rnối tiếp = Tính điện trở của tất cả thiết bị đơn () + Tính điện trở của các mạch ()
Vấn đề này có thể được giải quyết bằng mẫu composite khá đơn giản:
  • Rnối tiếp = Điện trở phần 1 + điện trở phần 2 + … + Điện trở phần cuối.
  • Các phần này có thể là 1 thiết bị đơn hoặc 1 mạch tích hợp (song song / nối tiếp).
  • Điện trở của từng phần sẽ được tự phần đó tính toán, các phần đều có chung một giao diện hàm tính toán này, như thế mạch cha sẽ không cần quan tâm lạoi chính xác của mạch con được tích hợp vào nó.
Hiện thực hóa cho ví dụ này bao gồm các thành phần:
  • Equipment: Một interface quy định các hàm chung cần phải có cho tất cả các thành phần tham gia vào mẫu này
  • Light, Fan: Thiết bị đơn tham gia vào mạch
  • Circuit: Mạch tích hợp, ngoài giao tiếp hàm kế thừa từ Element, Circuit sẽ có thêm các hàm phục vụ cho việc quản lý các thiết bị được tích hợp vào nó
  • ParallelCircuit, SerialCircuit: Mạch song song và nội tiếp, là các lớp hiện thực cho Circuit.
Equipment.java

public interface Equipment {
 public double getResistance();
 
 public String getName();
}

Fan.java
public class Fan implements Equipment {

    private static final int FAN_RESISTANCE = 20;
    private static final String FAN_NAME = "The fan";

    @Override
    public double getResistance() {
        return FAN_RESISTANCE;
    }

    @Override
    public String getName() {
        return FAN_NAME;
    }
}

Light.java
public class Light implements Equipment {

    private static final int LIGHT_RESISTANCE = 20;
    private static final String LIGHT_NAME = "The light";

    @Override
    public double getResistance() {
        return LIGHT_RESISTANCE;
    }

    @Override
    public String getName() {
        return LIGHT_NAME;
    }
}

Circuit.java
public abstract class Circuit implements Equipment {

    protected List equipments = new LinkedList();

    public void addEquipment(Equipment equipment) {
        if (equipment != null) {
            equipments.add(equipment);
        }
    }
}

Lớp Circuit được hiện thực như một abstract class, trong ví dụ này, hàm addEquipment được thêm vào nhằm cho phép thêm các đối tượng khác, hàm này có thể nhận vào bất kì một đối tượng nào thuộc loại Equipment.

ParallelCircuit.java
public class ParallelCircuit extends Circuit {

    private static final String CIRCUIT_NAME = "Parallel Circuit";

    @Override
    public double getResistance() {
        double temp = 0;
        for (Equipment e : equipments) {
            temp += 1.0 / e.getResistance();
        }
        return 1.0 / temp;
    }

    @Override
    public String getName() {
        return CIRCUIT_NAME;
    }
}

SerialCircuit.java
public class SerialCircuit extends Circuit {

    private static final String CIRCUIT_NAME = "Serial Circuit";

    @Override
    public double getResistance() {
        double temp = 0;
        for (Equipment e : equipments) {
            temp += e.getResistance();
        }
        return temp;
    }

    @Override
    public String getName() {
        return CIRCUIT_NAME;
    }
}

Chạy thử mẫu này với hàm main:

Main.java
public class Main {

    public static void main(String[] args) {
        /*
          Sơ đồ mạch điện
         /---light--------fan-----------light---fan---/
                     |----fan----| 
         */
        SerialCircuit rootCircuit = new SerialCircuit();
        rootCircuit.addEquipment(new Light());
  
        ParallelCircuit childCircuit = new ParallelCircuit();
        childCircuit.addEquipment(new Fan());
        childCircuit.addEquipment(new Fan());
  
        SerialCircuit childOfChildCircuit = new SerialCircuit();
        childOfChildCircuit.addEquipment(new Light());
        childOfChildCircuit.addEquipment(new Fan());
  
        childCircuit.addEquipment(childOfChildCircuit);
  
        rootCircuit.addEquipment(childCircuit);
  
        System.out.println("Total resistance = " + rootCircuit.getResistance());
    }
}

Với mạch điện được lắp như trong hình, chúng ta sẽ có kết quả:
Total resistance = 17.5

Nhận xét

Bài đăng phổ biến từ blog này

Base64 image – Lợi hay hại?

Hàm "tap" trong Laravel Collection