1. 程式人生 > >享元模式-Flyweight(Java實現)

享元模式-Flyweight(Java實現)

true 看書 cto 垃圾收集 AC fff 編程 identity back

享元模式-Flyweight

享元模式的主要目的是實現對象的共享,即共享池,當系統中對象多的時候可以減少內存的開銷,通常與工廠模式一起使用。

本文中的例子如下:

使用享元模式: 小明想看編程技術的書, 就到家裏的書架上拿, 如果有就直接看, 沒有就去買一本, 回家看. 看完了就放到家裏的書架上, 以後再想看了直接就在書架上拿, 不需要再去買同樣的這本書了.

不適用享元模式: 小明想看編程技術的書, 就去書店裏買一本回家看, 買完後看完了, 書就不知道丟到了哪裏去了. 下次想看的時候就找不到了, 還是得去書店裏重新買......又得花錢....然而並不長記性, 這回買完了, 看完了, 又丟到一邊...下次還是得再買...

Java中實例化一個對象 vs 小明買一本書

在Java中的實例化一個對象, 就像是在買一本書, Java中的對象會隨著作用域的退出, 對象會失去引用, 過後就會被垃圾收集器標記, 進行回收.

就相當於小明買完了一本書, 書就丟到了一邊, 媽媽收拾屋子就把這本書識別為了垃圾, 就給扔掉了. (被媽媽收走是被動導致的小明每次都重新買一本書, 也有可能小明主動地每次看書都重新買一本, 有錢任性...)

Java中把對象放進一個容器裏進行維護 vs 小明看完書把書放到書架上 (下面的相同顏色表示相同的行為, 可以互相對照)

在Java中: 使用完一個臨時實例化的對象後, 如果以後還想復用, 那麽就可以放到一個容器裏(對象管理器),

或者更直接的說就比如存到一個HashMap裏, 需要用的時候以後從裏面直接出來. 這樣HashMap對實例化的對象持有引用, 就不會被GC了, 這樣該對象就可以常駐內存, 可以復用了, 不用再實例化同樣的一個對象了.

小明: 看完了一本書, 把書放到了書架上, 這樣媽媽就知道這本書是小明需要的東西, 就不會把它當成垃圾來處理. 這樣這本書就會一直在家裏存在, 小明想看的時候, 就到家裏的書架就可以看了, 不用再重新買同樣的一本書了.

BooK接口

書的統一定義.書在本裏子中是享元模式裏被共享的對象. 應該被放到書架上復用, 而不是買次都重新買.

/**
 * 書的統一抽象, 書可以被讀
 */
public interface Book {
    void read();
}

HeadFirstJavaScript類

/**
 * <<HeadFirst JavaScript>>
 */
public class HeadFirstJavaScript implements Book {

    @Override
    public void read() {
        System.out.printf("這是一本<<HeadFirst JavaScript>>. (書的編號是:%s)\n", System.identityHashCode(this));
    }
}

KotlinInAction類

/**
 * <<Kotlin實戰>>
 */
public class KotlinInAction implements Book {

    @Override
    public void read() {
        System.out.printf("這是一本<<Kotlin實戰>>. (書的編號是:%s)\n", System.identityHashCode(this));
    }
}

PythonCookBook類

/**
 * <<Python編程手冊>>
 */
public class PythonCookBook implements Book {
    @Override
    public void read() {
        System.out.printf("這是一本<<Python編程手冊>>. (書的編號是:%s)\n", System.identityHashCode(this));
    }
}

BookFactory類

import java.util.EnumMap;
import java.util.Map;

public class BookFactory {

    public enum BookType {
        PYTHON, JAVASCRIPT, KOTLIN
    }

    private final Map<BookType, Book> shelf;

    public BookFactory() {
        shelf = new EnumMap<>(BookType.class);
    }

    /**
     * 想讀一本書的話就通過這裏來get.
     * 如果書架裏有, 那麽就從書架裏拿
     * 如果書架裏沒有, 那麽就從書店買一本看, 然後放到書架上
     */
    public Book getBook(BookType type) {
        Book book = shelf.get(type);
        if (book == null) {
            switch (type) {
                case PYTHON:
                    book = new PythonCookBook();
                    shelf.put(type, book);
                    break;
                case JAVASCRIPT:
                    book = new HeadFirstJavaScript();
                    shelf.put(type, book);
                    break;
                case KOTLIN:
                    book = new KotlinInAction();
                    shelf.put(type, book);
                    break;
                default:
                    break;
            }
        }
        return book;
    }
}

Main

運行/模擬場景

public class Main {

    public static void main(String[] args) {
        BookFactory bookFactory = new BookFactory();

        bookFactory.getBook(BookFactory.BookType.JAVASCRIPT).read();
        bookFactory.getBook(BookFactory.BookType.JAVASCRIPT).read();

        bookFactory.getBook(BookFactory.BookType.PYTHON).read();
        bookFactory.getBook(BookFactory.BookType.PYTHON).read();

        bookFactory.getBook(BookFactory.BookType.KOTLIN).read();
        bookFactory.getBook(BookFactory.BookType.KOTLIN).read();
        bookFactory.getBook(BookFactory.BookType.KOTLIN).read();

        // 書的編號一樣, 說明書復用了, 而不是每次都買一個新的
    }
}

結果如下 :

技術分享圖片

如果對象不是共享的, 也就是非享元模式, 那麽<<Kotlin實戰>>的三次的書編號都會是不一樣的, 因為每次看這本書, 都是新買的, 最終會導致買三次<<Kotlin實戰>>這本書, 同樣的書買三次多浪費啊. 而本文的例子使用了享元模式, 拿了三次<<Kotlin實戰>>這本書, 每次編號都是1360875712, 說明從頭到尾都是同一本書, 沒有造成浪費.

享元模式-Flyweight(Java實現)