通过粘性仙人掌基元进行延迟加载和缓存
您顯然知道什么是延遲加載 ,對嗎? 而且您無疑知道緩存 。 據我所知,Java中沒有一種優雅的方法來實現它們中的任何一個。 這是我在Cactoos原語的幫助下為自己找到的。
Matteo Garrone的《 Reality(2012)》
假設我們需要一個可以加密某些文本的對象。 以一種更加面向對象的方式講,它將封裝文本并成為其加密形式。 這是我們將如何使用它(讓我們先創建測試 ):
interface Encrypted {String asString() throws IOException; } Encrypted enc = new EncryptedX("Hello, world!"); System.out.println(enc.asString());現在,讓我們以一個非常原始的方式用一個主要的構造函數來實現它。 加密機制只會在傳入數據中的每個字節上加上+1 ,并且會假定加密不會破壞任何內容(一個非常愚蠢的假設,但是對于本示例而言,它將起作用):
class Encrypted1 implements Encrypted {private final String text;Encrypted1(String txt) {this.data = txt;}@Overridepublic String asString() {final byte in = this.text.getBytes();final byte[] out = new byte[in.length];for (int i = 0; i < in.length; ++i) {out[i] = (byte) (in[i] + 1);}return new String(out);} }到目前為止看起來正確嗎? 我對其進行了測試,并且可以正常工作。 如果輸入是"Hello, world!" ,輸出將為"Ifmmp-!xpsme\"" 。
接下來,假設我們希望我們的類接受InputStream和String 。 我們想這樣稱呼它,例如:
Encrypted enc = new Encrypted2(new FileInputStream("/tmp/hello.txt") ); System.out.println(enc.toString());這是最明顯的實現,具有兩個主要的構造函數(同樣,實現是原始的,但是可以工作):
class Encrypted2 implements Encrypted {private final String text;Encrypted2(InputStream input) throws IOException {ByteArrayOutputStream baos =new ByteArrayOutputStream();while (true) {int one = input.read();if (one < 0) {break;}baos.write(one);}this.data = new String(baos.toByteArray());}Encrypted2(String txt) {this.text = txt;}// asString() is exactly the same as in Encrypted1 }從技術上講,它是可行的,但流讀取是在構造函數內部進行的,這是一種不好的做法 。 主要構造函數只能執行屬性分配,而次要構造函數只能創建新對象。
讓我們嘗試重構并引入延遲加載:
class Encrypted3 {private String text;private final InputStream input;Encrypted3(InputStream stream) {this.text = null;this.input = stream;}Encrypted3(String txt) {this.text = txt;this.input = null;}@Overridepublic String asString() throws IOException {if (this.text == null) {ByteArrayOutputStream baos =new ByteArrayOutputStream();while (true) {int one = input.read();if (one < 0) {break;}baos.write(one);}this.text = new String(baos.toByteArray());}final byte in = this.text.getBytes();final byte[] out = new byte[in.length];for (int i = 0; i < in.length; ++i) {out[i] = (byte) (in[i] + 1);}return new String(out);} }效果不錯,但看起來很丑。 最丑陋的部分當然是這兩行:
this.text = null; this.input = null;它們使對象可變,并且使用NULL 。 丑陋,相信我。 不幸的是,延遲加載和NULL引用總是在經典示例中并存 。 但是,有一種更好的方法來實現它。 讓我們重構類,這次使用Cactoos的 Scalar :
class Encrypted4 implements Encrypted {private final IoCheckedScalar<String> text;Encrypted4(InputStream stream) {this(() -> {ByteArrayOutputStream baos =new ByteArrayOutputStream();while (true) {int one = stream.read();if (one < 0) {break;}baos.write(one);}return new String(baos.toByteArray());});}Encrypted4(String txt) {this(() -> txt);}Encrypted4(Scalar<String> source) {this.text = new IoCheckedScalar<>(source);}@Overridepublic String asString() throws IOException {final byte[] in = this.text.value().getBytes();final byte[] out = new byte[in.length];for (int i = 0; i < in.length; ++i) {out[i] = (byte) (in[i] + 1);}return new String(out);}現在看起來好多了。 首先,只有一個主要構造函數和兩個次要構造函數。 其次,對象是不可變的 。 第三,還有很多改進的余地:我們可以添加更多的構造函數來接受其他數據源,例如File或byte數組。
簡而言之,應該以“惰性”方式加載的屬性在對象內部表示為“功能”(Java 8中的lambda表達式 )。 在我們觸摸該屬性之前,不會加載該屬性。 一旦需要使用它,函數便會執行并得到結果。
這段代碼有一個問題。 每當我們調用asString() ,它將讀取輸入流,這顯然是行不通的,因為只有第一次流才會有數據。 在每個后續調用中,流都將為空。 因此,我們需要確保this.text.value()僅執行一次封裝的Scalar 。 所有以后的調用都必須返回先前計算的值。 因此,我們需要對其進行緩存 。 方法如下:
class Encrypted5 implements Encrypted {private final IoCheckedScalar<String> text;// same as above in Encrypted4Encrypted5(Scalar<String> source) {this.data = new IoCheckedScalar<>(new StickyScalar<>(source));}// same as above in Encrypted4此StickyScalar將確保僅對其方法value()的第一次調用將傳遞給封裝的Scalar 。 所有其他呼叫將接收第一個呼叫的結果。
要解決的最后一個問題是關于并發性。 我們上面的代碼不是線程安全的。 如果創建Encrypted5的實例并將其傳遞給同時調用asString()兩個線程,則結果將是不可預測的,這僅僅是因為StickyScalar不是線程安全的。 不過,還有另一種可以幫助我們的原語,稱為SyncScalar :
class Encrypted5 implements Encrypted {private final IoCheckedScalar<String> text;// same as above in Encrypted4Encrypted5(Scalar<String> source) {this.data = new IoCheckedScalar<>(new SyncScalar<>(new StickyScalar<>(source)));}// same as above in Encrypted4現在我們很安全,設計優雅。 它包括延遲加載和緩存。
我現在在許多項目中都使用這種方法,它看起來很方便,清晰且面向對象。
您可能還會發現這些相關的帖子很有趣: 為什么InputStream設計錯誤 ; 嘗試。 最后。 如果。 不。 空值。 ; 每個私有靜態方法都是新類的候選人 ; 我將如何重新設計equals() ; 對象行為不可配置 ;
翻譯自: https://www.javacodegeeks.com/2017/10/lazy-loading-caching-via-sticky-cactoos-primitives.html
總結
以上是生活随笔為你收集整理的通过粘性仙人掌基元进行延迟加载和缓存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 吴姓名字大全男孩子(吴姓男孩好听名字推荐
- 下一篇: 电脑快捷键录制不了(电脑录屏快捷键打不开