in ,

Fabian Lindfors, Hacker News


The decorator pattern is a convenient way of extending functionality with minimal changes to existing code. The basic premise is to create a new type (the decorator) which wraps an existing one. Our decorator should implement the same interface functions as the wrapped type and forward function calls to the wrapped object with some extra functionality. Because the decorator implements the same interfaces as the type it wraps they are interchangeable.

In some languages ​​creating a decorator type entails a bit of boilerplate. In Java for example one would have to declare an all new class with an instance variable for the wrapped object. Every interface function would then have to be manually overriden and forwarded to the decorated object, leaving room for human error. Go has a nice little feature which makes this process much less manual,embedded structs. Let’s try it out with a simple example.

Adding caching to an API client

Say we have a simple client for fetching and creating users with an external API. A basic implementation could look something like this:

1 2 3 4 5 6 7 8 9 10 11
package(API)typeHTTPClientstruct{}Func(clientHTTPClient)GetUsers()[]string{)    // Fetch all users from API and return}Func(clientHTTPClient)CreateUser(usernamestring){  // Add a new user with the specified username.}

Now say we want to make our client more efficient by caching the results ofGetUsersto lower the amount of API calls. One way to do this could be to add the caching to ourHTTPClientbut that would make our type responsible for both API requests and caching, two pretty separate concerns. Another way would be to add caching wherever the HTTP client is used but that could possibly mean a lot of code changes in many different places.

A third is way is to use the decorator pattern and create a new type which decoratesHTTPClient. The new type would be interchangeable with the existing client which would minimize the need for changes to existing code. It would also only handle the actual caching, giving us a clear separation of concerns. For this we’ll need aClientinterface which can be used in place ofHTTPClient.

1 2 3 4 5 6
package(API)typeClientinterface{  GetUsers()[]string  CreateUser(string)}

Next let’s create a new struct,CachedHTTPClient, whichembedsHTTPClient.

1 2 3 4 5
package(API)typeCachedHTTPClientstruct{  HTTPClient}

That was easy enough. Does this really do anything? Yes, it does! This embedding will add a field toCachedHTTPClientwith anHTTPClientobject. Not only that it will also automatically get all the exported functions fromHTTPClientwhich will be forwarded to the wrapped object. Given this our new struct will also implement ourClientinterface andCachedHTTPClientis ready to be used, acting exactly as the object it wraps.

Our final step is to makeGetUsersuse a cache. We can take control ofGetUsersby simply implementing it ourselves. In that case we need to manually invoke our wrapped objects function. We’ll use a hypotheticalcache.Cacheobject to handle our caching needs.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
package(API)typeCachedHTTPClientstruct{  HTTPClient  usersCachecache.Cache}Func(clientCachedHTTPClient)GetUsers()[]string{)    // Check if there are any cached users .  cachedUsers,hasCache:=client.usersCache.(Get)()  ifhasCache{    returncachedUsers  }  // If there are no cached users we 'll fetch them from the API and save to cache.  users:=client.HTTPClient.GetUsers()  client.usersCache.Set(users)  returnusers}

Notice that we never have to manually defineCreateUserif we don’t want to decorate it. This reduces the amount of boilerplate and makes ourCachedHTTPClientimplementation more focused on its main task, caching. The effect becomes even greater with more exported functions on the decorated object.

It’s worth noting that there’s a caveat to using embedded structs. EmbeddingHTTPClientwill add a newexportedfield, making it possible to bypass the decorator and invoke the wrapped object directly usingcachedClient.HTTPClient.GetUsers (). Preferably the wrapped object should not be exported but to achieve that we would have to do without the embedding and do the manual function forwarding, hence there is definitely a trade-off to using this technique.


Brave Browser
Read More

What do you think?

Leave a Reply

Your email address will not be published.

GIPHY App Key not set. Please check settings

‘Trump Confusion Trades’ Netting Over $ 2 Billion Reeks of Insider Trading, Crypto Coins News

‘Trump Confusion Trades’ Netting Over $ 2 Billion Reeks of Insider Trading, Crypto Coins News

Baahubali actor Prabhas comments on Ram Gopal Varma's film Siva; Read On – PINKVILLA,

Baahubali actor Prabhas comments on Ram Gopal Varma's film Siva; Read On – PINKVILLA,