Lazy Loading and Caching via Sticky Cactoos Primitives
You obviously know what lazy loading is, right? And you no doubt know about caching. To my knowledge, there is no elegant way in Java to implement either of them. Here is what I found out for myself with the help of Cactoos primitives.
Let’s say we need an object that will encrypt some text. Speaking in a more object-oriented way, it will encapsulate the text and become its encrypted form. Here is how we will use it (let’s create tests first):
Let’s say we need an object that will encrypt some text. Speaking in a more object-oriented way, it will encapsulate the text and become its encrypted form. Here is how we will use it (let’s create tests first):
Now let’s implement it, in a very primitive way, with one primary constructor. The encryption mechanism will just add
to each byte in the incoming data, and will assume that the encryption won’t break anything (a very stupid assumption, but for the sake of this example it will work):
Looks correct so far? I tested it and it works. If the input is , the output will be .
Next, let’s say that we want our class to accept an as well as a . We want to call it like this, for example:
Here is the most obvious implementation, with two primary constructors (again, the implementation is primitive, but works):
Technically it works, but stream reading is right inside the constructor, which is bad practice. Primary constructors must not do anything but attribute assignments, while secondary ones may only create new objects.
Let’s try to refactor and introduce lazy loading:
Works great, but looks ugly. The ugliest part is these two lines of course:
They make the object mutable and they’re using NULL. It’s ugly, trust me. Unfortunately, lazy loading and NULL references always come together in classic examples. However there is a better way to implement it. Let’s refactor our class, this time using from Cactoos:
Now it looks way better. First of all, there is only one primary constructor and two secondary ones. Second, the object is immutable. Third, there is still a lot of room for improvement: we can add more constructors which will accept other sources of data, for example or a byte array.
In a nutshell, the attribute that is supposed to be loaded in a “lazy” way is represented inside an object as a “function” (lambda expression in Java 8). Until we touch that attribute, it’s not loaded. Once we need to work with it, the function gets executed and we have the result.
There is one problem with this code though. It will read the input stream every time we call
, which will obviously not work, since only the first time will the stream have the data. On every subsequent call the stream will simply be empty. Thus, we need to make sure that executes the encapsulated only once. All later calls must return the previously calculated value. So we need to cache it. Here is how:
This will make sure that only the first call to its method will go through to the encapsulated . All other calls will receive the result of the first call.
The last problem to solve is about concurrency. The code we have above is not thread safe. If I create an instance of is not thread-safe. There is another primitive to help us out though, called :
and pass it to two threads, which call simultaneously, the result will be unpredictable, simply because
Now we’re safe and the design is elegant. It includes lazy loading and caching.
I’m using this approach in many projects now and it seems convenient, clear, and object-oriented.
Comments
Post a Comment