纹理和基元_通过粘性仙人掌基元进行延迟加载和缓存
紋理和基元
您顯然知道什么是延遲加載 ,對嗎? 而且您無疑知道緩存 。 據(jù)我所知,Java中沒有一種優(yōu)雅的方法來實現(xiàn)它們中的任何一個。 這是我在Cactoos原語的幫助下為自己找到的。
Matteo Garrone的《 Reality》(2012年)
假設我們需要一個可以加密某些文本的對象。 以一種更加面向對象的方式講,它將封裝文本并成為其加密形式。 這是我們將如何使用它( 首先創(chuàng)建測試 ):
interface Encrypted {String asString() throws IOException; } Encrypted enc = new EncryptedX("Hello, world!"); System.out.println(enc.asString());現(xiàn)在,讓我們以一種非常原始的方式用一個主要的構造函數(shù)來實現(xiàn)它。 加密機制只會在傳入數(shù)據(jù)中的每個字節(jié)上加上+1 ,并且會假設加密不會破壞任何內(nèi)容(一個非常愚蠢的假設,但對于本示例而言,它將起作用):
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());這是最明顯的實現(xiàn),具有兩個主要的構造函數(shù)(同樣,實現(xiàn)是原始的,但是可以工作):
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 }從技術上講,它是可行的,但是流讀取是在構造函數(shù)內(nèi)部進行的,這是不正確的做法 。 主要構造函數(shù)只能執(zhí)行屬性分配,而次要構造函數(shù)只能創(chuàng)建新對象。
讓我們嘗試重構并引入延遲加載:
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引用總是在經(jīng)典示例中并存 。 但是,有一種更好的方法來實現(xiàn)它。 讓我們重構類,這次使用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);}現(xiàn)在看起來好多了。 首先,只有一個主要構造函數(shù)和兩個次要構造函數(shù)。 其次,對象是不可變的 。 第三,還有很多改進的余地:我們可以添加更多的構造函數(shù)來接受其他數(shù)據(jù)源,例如File或byte數(shù)組。
簡而言之,應該以“惰性”方式加載的屬性在對象內(nèi)部表示為“功能”(Java 8中的lambda表達式 )。 在我們觸摸該屬性之前,不會加載該屬性。 一旦我們需要使用它,函數(shù)就會被執(zhí)行并得到結果。
這段代碼有一個問題。 每當我們調用asString() ,它將讀取輸入流,這顯然是行不通的,因為只有第一次流才會有數(shù)據(jù)。 在每個后續(xù)調用中,流都將為空。 因此,我們需要確保this.text.value()僅執(zhí)行一次封裝的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 。 所有其他呼叫將接收第一個呼叫的結果。
要解決的最后一個問題是關于并發(fā)性。 我們上面的代碼不是線程安全的。 如果我創(chuàng)建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現(xiàn)在我們很安全,設計優(yōu)雅。 它包括延遲加載和緩存。
我現(xiàn)在在許多項目中都使用這種方法,而且看起來很方便,清晰且面向對象。
您可能還會發(fā)現(xiàn)這些相關的帖子很有趣: 為什么InputStream設計錯誤 ; 嘗試。 最后。 如果。 不。 空值。 ; 每個私有靜態(tài)方法都是新類的候選人 ; 我將如何重新設計equals() ; 對象行為不可配置 ;
翻譯自: https://www.javacodegeeks.com/2017/10/lazy-loading-caching-via-sticky-cactoos-primitives.html
紋理和基元
總結
以上是生活随笔為你收集整理的纹理和基元_通过粘性仙人掌基元进行延迟加载和缓存的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓恢复图标怎么恢复(安卓恢复图标)
- 下一篇: react核心虚拟dom_使用虚拟时间测