Cầu nối tương lai!

Đã bao giờ bạn thắc mắc các thư viện lập trình như java persistence, hay spring data hoạt động như thế nào chưa? Chiếc cầu nào đã kết nối tầng ứng dụng của chúng ta đến các tầng thấp hơn ở bên dưới? Đó chính là nhờ có Bridge design pattern.

Vấn đề thực tế

Chắc hẳn chúng ta ai cũng đã biết đến database rồi, và chắc chắn là tất cả các anh em làm Back-End là phải thường xuyên đọc ghi với database rồi. Câu hỏi đặt ra là làm thế nào để chúng ta có thể giao tiếp với database một cách mềm dẻo, và thậm chí chúng ta có thể thay thế database trong tương lai nếu nó không còn phù hợp nữa? Câu trả lời là chúng ta cần chia source code của chúng ta thành các tầng khác nhau.

Thường khi chúng ta lập trình với Spring thì source code thường được chia thành 5 tầng:

Các tầng trên cùng:

  1. Tầng controller: là tầng sẽ nhận request và phản hồi response cho client
  2. Tầng service: là tầng sẽ xử lý nghiệp vụ
  3. Tầng Repository: là tầng sẽ giao tiếp với với càng tầng thấp hơp để đọc ghi dữ liệu vào database. Khi lập trình với các framework như spring-data hay ezydata-jpa thông thường chúng ta chỉ cần quan tâm đến tầng này là đủ

Các tầng ở dưới:

  1. Tầng pesistence: Đây là tầng sẽ giao tiếp với database driver (ví dụ mysql driver) để đọc ghi dữ liệu vào database và map dữ liệu thành dạng đối tượng cho chúng ta, 2 framework mà chúng ta hay dùng nhất ở tầng này là hibernate và mybatis
  2. Tầng driver: là tầng mà các nhà cung cấp database sẽ cài đặt các interface của jdbc và cung cấp cho thư viện mà chúng ta sẽ gọi nó là connector hoặc driver

Mục tiêu ra đời

Với rất nhiều tầng lớp như vậy, thì Bridge design pattern đã ra đời với các mục tiêu:

  1. Kết nối các tầng xử lý dữ liệu với nhau, nó xa hơn thì là kết nối các hệ thống với nhau
  2. Cho phép chúng ta dễ dàng thay việc cài đặt của các tầng mà không ảnh hưởng đến các tầng sử dụng. Ví dụ chúng ta có thể sử dụng MySQL hay Oracle, nhưng lớp Repository của chúng ta vẫn được giữ nguyên, chúng ta chỉ cần thay đổi một chút cấu hình là xong

Giải quyết vấn đề

Nào bây giờ chúng ta sẽ đi viết tằng Persistence và tạo ra một thư viện nhỏ giống kiểu Hibernate. Đầu tiên hãy thiết kế lớp một chút.

Sơ đồ lớp
  1. PersistenceImplementor: là interface cầu nối (Bridge)
  2. OraclePersistenceImplementor: là lớp cầu nối gọi đến driver của Oracle
  3. MySQLPersistenceImplementor: là lớp cầu nối gọi đến driver của MySQL
  4. Persistence: là interface mà các lớp của tầng Service sẽ sử dụng như một cầu nối để gọi đến cơ sở dữ liệu
  5. PersistenceImpl: là lớp cài đặt của interface Persistence, lớp này sẽ sử dụng PersistenceImplementor để truy xuất cơ sở dữ liệu. Lớp này sẽ tuỳ thuộc vào việc chúng ta cấu hình lựa chọn database nào để khởi tạo PersistenceImplementor tương ứng.

Dự theo thiết kế này thì rõ ràng các lớp sử dụng interface Persistence sẽ không cần phải quan tâm đến các lớp cài đặt phức tạp ở dưới, vậy việc chúng ta thay đổi database là tương đối dễ dàng, chỉ cần thay đổi cấu hình, thế là xong

Source code

Vì thiết kế khá nhiều lớp nên source sẽ hơi phức tạp một chút.

interface Persistence {

    public void persist(Entity entity);

    public Entity findById(String id);

    public void deleteById(String id);
}

// bridge interface
interface PersistenceImplementor {

    public void saveEntity(Entity entity);

    public void deleteEntity(String entityId);

    public Entity getEntity(String entityId);
}

class PersistenceImp implements Persistence {

    // bridge object
    private PersistenceImplementor implementor;

    public PersistenceImp(String databaseType) {
        this.implementor = databaseType.equals("MySQL")
                ? new MySQLPersistenceImplementor()
                : new OraclePersistenceImplementor();
    }

    @Override
    public void persist(Entity entity) {
        implementor.saveEntity(entity);
    }

    @Override
    public Entity findById(String id) {
        return implementor.getEntity(id);
    }

    @Override
    public void deleteById(String id) {
        implementor.deleteEntity(id);
    }

}

class MySQLPersistenceImplementor implements PersistenceImplementor {

    private final Map<String, Entity> entities = new HashMap<>();

    @Override
    public void saveEntity(Entity entity) {
        entities.put(entity.getId(), entity);
    }

    @Override
    public void deleteEntity(String entityId) {
        entities.remove(entityId);
    }

    @Override
    public Entity getEntity(String entityId) {
        return entities.get(entityId);
    }
}

class OraclePersistenceImplementor implements PersistenceImplementor {
    private final Map<String, Entity> entities = new HashMap<>();

    @Override
    public void saveEntity(Entity entity) {
        entities.put(entity.getId(), entity);
    }

    @Override
    public void deleteEntity(String entityId) {
        entities.remove(entityId);
    }

    @Override
    public Entity getEntity(String entityId) {
        return entities.get(entityId);
    }
}

Và khi sử dụng sẽ thế này:

// create persistence API
Persistence persistenceAPI = new PersistenceImp(databaseType);

// save an entity
persistenceAPI.persist(new UserEntity("foo", "Mr.Foo"));
UserEntity user = (UserEntity)

// find an entity
persistenceAPI.findById("foo");

// delete an entity
persistenceAPI.deleteById("123456");

Tổng kết

Bridge rõ ràng là một design pattern vô cùng quan trọng, thứ mà đa phần các framework (đặc biệt là spring) rất hay sử dụng, nó giúp chúng ta có thể dễ dàng chia source code của mình thành nhiều tầng khác nhau, từ đó tăng được sử mềm dẻo và khả năng mở rộng dễ dàng về sau này. Hãy nắm chắc nó và các thư viện sẽ nằm trong lòng bàn tay chúng ta.

Tham khảo

  1. wiki
  2. oodesign
  3. Ví dụ