Các mẫu thiết kế cấu trúc trong lập trình hướng đối tượng

1. Mẫu thiết kế Adapter (Bộ chuyển đổi)

Mẫu thiết kế Adapter đóng vai trò như một cầu nối giữa hai giao diện (interface) không tương thích. Nó cho phép các lớp vốn không thể làm việc cùng nhau do khác biệt về giao diện có thể phối hợp nhịp nhàng. Adapter thường được chia thành ba loại chính:

1.1. Class Adapter (Adapter cấp lớp)

Sử dụng cơ chế kế thừa để kết nối interface của lớp cũ với interface mục tiêu. Giả sử chúng ta có một hệ thống cũ cung cấp dữ liệu qua lớp OldSystem và cần tích hợp vào NewProcessor.

// Lớp cũ cần thích nghi
public class OldSystem {
    public void legacyRequest() {
        System.out.println("Đang thực hiện yêu cầu từ hệ thống cũ.");
    }
}

// Interface mục tiêu
public interface ITarget {
    void process();
}

// Adapter sử dụng kế thừa
public class ClassAdapter extends OldSystem implements ITarget {
    @Override
    public void process() {
        super.legacyRequest();
    }
}

1.2. Object Adapter (Adapter cấp đối tượng)

Thay vì kế thừa, Adapter này chứa một instance của lớp cần thích nghi (composition). Cách tiếp cận này linh hoạt hơn vì nó có thể làm việc với bất kỳ lớp con nào của đối tượng được bao bọc.

public class ObjectAdapter implements ITarget {
    private OldSystem adaptee;

    public ObjectAdapter(OldSystem system) {
        this.adaptee = system;
    }

    @Override
    public void process() {
        adaptee.legacyRequest();
    }
}

1.3. Interface Adapter (Default Adapter)

Khi một interface có quá nhiều phương thức nhưng bạn chỉ muốn triển khai một vài trong số đó, một lớp abstract trung gian sẽ triển khai mặc định (thường là rỗng) tất cả các phương thức. Bạn chỉ cần kế thừa lớp abstract này.

public interface MultiTask {
    void taskA();
    void taskB();
    void taskC();
}

public abstract class TaskDefaultAdapter implements MultiTask {
    public void taskA() {}
    public void taskB() {}
    public void taskC() {}
}

public class MySpecialTask extends TaskDefaultAdapter {
    @Override
    public void taskA() {
        System.out.println("Chỉ thực hiện nhiệm vụ A.");
    }
}

2. Mẫu thiết kế Decorator (Trang trí)

Decorator cho phép bổ sung trách nhiệm hoặc hành vi cho một đối tượng một cách động mà không làm ảnh hưởng đến các đối tượng khác cùng lớp. Đây là giải pháp thay thế linh hoạt cho việc kế thừa để mở rộng chức năng.

public interface IService {
    void execute();
}

public class BasicService implements IService {
    @Override
    public void execute() {
        System.out.println("Thực thi dịch vụ cơ bản.");
    }
}

public abstract class ServiceDecorator implements IService {
    protected IService decoratedService;

    public ServiceDecorator(IService service) {
        this.decoratedService = service;
    }

    public void execute() {
        decoratedService.execute();
    }
}

public class LoggingDecorator extends ServiceDecorator {
    public LoggingDecorator(IService service) {
        super(service);
    }

    @Override
    public void execute() {
        System.out.println("Log: Bắt đầu thực thi.");
        super.execute();
        System.out.println("Log: Kết thúc thực thi.");
    }
}

3. Mẫu thiết kế Proxy (Ủy nhiệm)

Proxy cung cấp một đối tượng đại diện để kiểm soát quyền truy cập vào đối tượng gốc. Nó thường được dùng để trì hoãn việc khởi tạo đối tượng nặng (Lazy Loading), kiểm soát bảo mật hoặc ghi log.

public interface IResource {
    void display();
}

public class RealResource implements IResource {
    public RealResource() {
        System.out.println("Đang khởi tạo tài nguyên nặng từ Disk...");
    }

    @Override
    public void display() {
        System.out.println("Hiển thị tài nguyên.");
    }
}

public class ResourceProxy implements IResource {
    private RealResource realResource;

    @Override
    public void display() {
        if (realResource == null) {
            realResource = new RealResource();
        }
        System.out.println("Kiểm tra quyền truy cập trước khi hiển thị.");
        realResource.display();
    }
}

4. Mẫu thiết kế Facade (Bề ngoài)

Facade cung cấp một giao diện đơn giản hóa cho một hệ thống con phức tạp gồm nhiều lớp bên trong. Nó giúp giảm sự phụ thuộc giữa khách hàng (client) và các module phức tạp.

class SubsystemAlpha {
    void init() { System.out.println("Alpha khởi động."); }
}

class SubsystemBeta {
    void load() { System.out.println("Beta đang tải dữ liệu."); }
}

public class SystemFacade {
    private SubsystemAlpha alpha = new SubsystemAlpha();
    private SubsystemBeta beta = new SubsystemBeta();

    public void startSystem() {
        System.out.println("Facade: Bắt đầu quy trình khởi động...");
        alpha.init();
        beta.load();
        System.out.println("Hệ thống đã sẵn sàng.");
    }
}

5. Mẫu thiết kế Bridge (Cầu nối)

Bridge tách rời phần trừu tượng (Abstraction) khỏi phần thực thi (Implementation) để cả hai có thể thay đổi độc lập. Thay vì sử dụng kế thừa đa cấp, ta sử dụng tính bao hàm (composition).

public interface IColor {
    void applyColor();
}

public class RedColor implements IColor {
    public void applyColor() { System.out.println("Màu đỏ."); }
}

public abstract class Shape {
    protected IColor color;
    public Shape(IColor color) { this.color = color; }
    abstract void draw();
}

public class Circle extends Shape {
    public Circle(IColor color) { super(color); }
    @Override
    void draw() {
        System.out.print("Vẽ hình tròn ");
        color.applyColor();
    }
}

6. Mẫu thiết kế Composite (Hợp phần)

Mẫu này cho phép bạn xây dựng các cấu trúc đối tượng theo dạng cây để biểu diễn các mối quan hệ "phần-tổng thể". Composite giúp client xử lý các đối tượng đơn lẻ và các nhóm đối tượng một cách thống nhất.

import java.util.ArrayList;
import java.util.List;

public class FileNode {
    private String name;
    private List<FileNode> children = new ArrayList<>();

    public FileNode(String name) { this.name = name; }

    public void add(FileNode node) { children.add(node); }

    public void showStructure(String indent) {
        System.out.println(indent + "+ " + name);
        for (FileNode child : children) {
            child.showStructure(indent + "  ");
        }
    }
}

7. Mẫu thiết kế Flyweight (Chia sẻ)

Flyweight tập trung vào việc tối ưu hóa bộ nhớ bằng cách chia sẻ càng nhiều dữ liệu càng tốt với các đối tượng tương tự nhau. Nó thường được kết hợp với một Factory để quản lý kho đối tượng dùng chung.

import java.util.HashMap;
import java.util.Map;

public class TreeType {
    private String name;
    private String texture;

    public TreeType(String name, String texture) {
        this.name = name;
        this.texture = texture;
    }

    public void plant(int x, int y) {
        System.out.println("Trồng cây " + name + " tại (" + x + "," + y + ")");
    }
}

public class TreeFactory {
    private static Map<String, TreeType> types = new HashMap<>();

    public static TreeType getTreeType(String name, String texture) {
        TreeType result = types.get(name);
        if (result == null) {
            result = new TreeType(name, texture);
            types.put(name, result);
        }
        return result;
    }
}

Thẻ: design-patterns Java oop structural-patterns software-architecture

Đăng vào ngày 29 tháng 7 lúc 23:59