Chia đúng phạm vi

Việc sử dụng interface sẽ giúp code của chúng ta mềm dẻo hơn, tuy nhiên, một interface nếu chứa quá nhiều hàm cũng có thể khó hiểu và gây nhầm lẫn. Vì vậy chúng ta có thể tuân theo nguyên tắc interface segregation bằng cách chia nhỏ các interface tương ứng với chức năng của chúng, từ đó mà người sử dụng có thể lựa chọn các interface phù hợp.

Bài toán thức tế

Có lẽ Repository là khái niệm tương đối quen thuộc với chúng ta. Đối tượng này được dùng để truy cập đến cơ sở dữ liệu, và nó có chứa các hàm cơ bản như Create, Read (hoặc Find), UpdateDelete, ví dụ:

public interface CrudRepository<I, E> {

    E saveOne(E entity);

    void saveMany(List<E> entities);

    E findById(I id);

    List<E> findAll();

    int updateAll(String field, Object value);

    void updateById(I id, String field, Object value);

    void deleteById(I id);

    void deleteAll(List<I> ids);
}

Tuy nhiên không phải lúc nào chúng ta cũng có nhu cầu sử dụng tất cả các hàm này, và thực tế là trong hầu hết các trường hợp chúng ta sẽ sử dụng hàm savefind là nhiều.

Giải pháp

Một trong những điểm thú vị đó là interface cho phép đa thừa kế, chính vì vậy mà chúng ta có thể chia CrudRepository thành nhiều interface nhỏ khác nhau kiểu thế này:

public interface CreateRepository<I, E> {

    E saveOne(E entity);

    void saveMany(List<E> entities);
}

public interface ReadRepository<I, E> {

    E findById(I id);

    List<E> findAll();
}

public interface UpdateRepository<I, E> {

    int updateAll(String field, Object value);

    void updateById(I id, String field, Object value);
}

public interface DeleteRepository<I, E> {

    void deleteById(I id);

    void deleteAll(List<I> ids);
}

CrudRepository interface sẽ trở thành thế này:

public interface CrudRepository<I, E> extends
    CreateRepository<I, E>,
    ReadRepository<I, E>,
    UpdateRepository<I, E>,
    DeleteRepository<I, E> {
}

Và bây giờ khi sử dụng chúng ta có thể sử dụng các interface riêng lẻ như ReadRepository hoặc sử dụng interface gộp chung CrudRepository, ví dụ:

public class UserFetcher {

    private ReadRepository<Long, User> userReadRepository;

    public User getUserById(long userId) {
        return userReadRepository.findById(userId);
    }
}

Tổng kết

Nguyên tắc này tương đối dễ áp dụng, tuy nhiên hiệu quả của nó cũng không phải là cái gì đó quá đột phá, nó đơn thuần sẽ giúp code của chúng ta mềm dẻo và thuận tiện hơn cho người sử dụng mà thôi. Vậy nên bạn cũng không cần quá ép buộc để phải tuân theo nguyên tắc này để tránh làm code của bạn quá phức tạp, khó đọc và không được tự nhiên nhé.