Tạo ra theo thể loại

Khi bạn sử dụng một ứng dụng media player, bạn có thể thấy nó có khả năng xem video, ngoài ra nó cũng có khả năng phát được file audio, đây là một trong những ví dụ điển hình của Factory Method design pattern.

MediaFileReader reader = mediaFileReaderFactory.createReader(mediaFile);

Giới thiệu

Cũng giống như Factory Pattern Factory Method là sự kết hợp giữa tính đóng gói và cách thức để tạo ra một đối tượng, từ đó sẽ đơn giản và dễ dàng hơn cho người sử dụng.

Mục tiêu ra đời

Factory Method pattern ra đời với mục tiêu:

  • Che giấu các logic phức tạp để tạo ra một đối tượng, giúp lập trình viên chỉ cần quan tâm đến đối giao diện tạo đối tượng mà thôi tuy nhiên các đối tượng được tạo ra có thể sẽ có cài đặt khác nhau tuỳ thuộc vào dữ liệu đầu vào.
  • Sử dụng 1 giao diện duy nhất để tạo ra đối tượng, ví dụ không cần phải sử dụng VideoMediaFileReaderFactoryAudioMediaFileReaderFactory mà chỉ cần sử dụng duy nhất MediaFileReaderFactory là đủ.

Ví dụ

Chúng ta có thể cài đặt các lớp đọc dữ liệu từ file cho media player thế này:

public interface MediaFileReader {

    void read();
}

@AllArgsConstructor
public static class VideoMediaFileReader implements MediaFileReader {

    private final String filePath;

    @Override
    public void read() {
        System.out.println("read video file: " + filePath);
    }
}

@AllArgsConstructor
public static class AudioMediaFileReader implements MediaFileReader {

    private final String filePath;

    @Override
    public void read() {
        System.out.println("read audio file: " + filePath);
    }
}

public static abstract class MediaFileReaderFactory {

    public MediaFileReader newMediaFileReader(String mediaFile) {
        return mediaFile.endsWith(".mp3")
               ? newAudioMediaFileReader(mediaFile)
               : newVideoMediaFileReader(mediaFile);
    }

    protected abstract MediaFileReader newAudioMediaFileReader(
        String mediaFile
    );

    protected abstract MediaFileReader newVideoMediaFileReader(
        String mediaFile
    );
}

public static class MediaFileReaderFactoryImpl extends MediaFileReaderFactory {

    @Override
    protected MediaFileReader newAudioMediaFileReader(String mediaFile) {
        return new AudioMediaFileReader(mediaFile);
    }

    @Override
    protected MediaFileReader newVideoMediaFileReader(String mediaFile) {
        return new VideoMediaFileReader(mediaFile);
    }
}

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

final MediaFileReaderFactory factory = new MediaFileReaderFactoryImpl();
final MediaFileReader audioReader = factory.newMediaFileReader("hello.mp3");
audioReader.read();
final MediaFileReader videoReader = factory.newMediaFileReader("hello.mp4");
videoReader.read();

Bạn có thể thấy, mặc dù chúng ta có tạo lớp đọc file cho video hay audio thì chúng ta vẫn chỉ cần sử dụng 1 interface duy nhất là MediaFileReaderFactory mà thôi.

Kết luận

Cũng giống như Factory pattern, Factory Method pattern có tính đóng gói rất cao, thậm chí với bao nhiêu kiểu dữ liệu đầu vào thì chúng ta cũng chỉ cẩn sử dụng duy nhất một giao diện để tạo ra đối tượng mà thôi, nó cực kỳ tiện lợi cho những bài toán phức tạp trong thực tế. Nếu chúng ta đi viết thư viện thì việc sử dụng Factory Method pattern cũng sẽ giúp người dùng cảm thấy rõ ràng và đơn giản hơn trong việc sử dụng thư viện do chúng ta tạo ra.