Skip to content

Instantly share code, notes, and snippets.

@greenantdotcom
Created July 2, 2015 23:45
Show Gist options
  • Save greenantdotcom/d539b130d7efe7f51682 to your computer and use it in GitHub Desktop.
Save greenantdotcom/d539b130d7efe7f51682 to your computer and use it in GitHub Desktop.
Java example for caching and composition

Purpose

I understand why we would want to have the database be queried if we have, say, a cache Miss/Mrs./Ms. or a cache-hard-down, but I would suggest a more Interface-oriented/Compositional approach, like the below which may end up being more testable and with a consistent interface for cients.

// Assume we had a series of adapters which had this simple interface
public interface CacheReadingInterface{
public boolean writeValueToHash(String hashName, String hashKey, Object value);
}
// And assume you had a Redis cache that implemented it
// Further assume RedisConnectionExcepion and RedisProtocolException…
public class MyCacheClass implements CacheReadingInterface{
protected Jredis client;
public MyCacheClass(Jredis _client){
client = _client;
}
protected boolean checkHashNameAndKey(String hashName, String hashKey) throws InvalidArgumentException{
String tmpName = hashName.trim(),
tmpKey = hashKey.trim();
if(tmpName != hashName){
throw new InvalidArgumentException("Unexpected whitespace in hash name");
}
else if(tmpName.length() == 0){
throw new InvalidArgumentException("Hash name is empty");
}
if(tmpKey != hashKey){
throw new InvalidArgumentException("Unexpected whitespace in hash key");
}
else if(tmpKey.length() == 0){
throw new InvalidArgumentException("Hash key is empty");
}
return true;
}
public boolean writeValueToHash(String hashName, String hashKey, Object value) throws
RedisConnectionExcepion,
RedisProtocolException,
InvalidArgumentException
{
checkHashNameAndKey(hashName,hashKey);
String valueAsString = value.toString();
// Assume that calling the client here would throw one of the Redis exceptions
return client.hset(hashName, hashKey, value.toString());
}
public Object getValueFromHash(String hashName, String hashKey) throws
RedisConnectionExcepion,
RedisProtocolException,
InvalidArgumentException {
checkHashNameAndKey(hashName,hashKey);
// Assume that calling the client here would throw one of the Redis exceptions
return client.hget(hashName, hashKey);
}
}
// And assume you had a DB touching adapter class that did roughly the same thing
public class MyDBAccessClass implements CacheReadingInterface{
Connection db;
PreparedStmt productToPlatformStmt;
public MyDBAccessClass(Connection _db){
db = _db;
productToPlatformStmt = db.prepare('SELECT * FROM … WHERE platform_id = ?');
}
public Object getValueFromHash(String hashName, String hashKey) throws DBException, InvalidException, blah, blah, blah{
// And excuse my using a switch() - was the quickest way to show it off…
switch(hashName){
case 'productToPlatform':
return productToPlatformStmt.execute(hashKey).fetchOne();
default:
throw new InvalidArgumentException("Unknown lookup type <" + hashName + ">");
}
}
}
// Then we can create a Composite object that also implements the interface
// that makes us not care where the data is coming from
public class DataAcquirer implements CacheReadingInterface{
protected ArrayList<CacheReadingInterface> datasources;
public function DataAcquirer(ArrayList<CacheReadingInterface> _datasources){
datasources = _datasources;
}
public Object getValueFromHash(String hashName, String hashKey) throws Exception {
// Then we can loop through the datasources in order
for(CacheReadingInterface source : datasources){
try{
// If they can return, they will…
return source.getValueFromHash(hashName,hashKey);
}
catch(Exception e){
// Log me
}
}
// And if we exhaust all sources, throw a specific exception to note this
throw new DataNotFoundException("Exhausted datasources - no value found");
}
}
// This works, right?
import *.*;
Jredis redis = …;
Connection connection = …;
ArrayList<CacheReadingInterface> lookupClient = new ArrayList(
new MyCacheClass(jredis),
new MyDBAccessClass(connection)
);
try{
Object value = lookupClient.getValueFromHash('productToPlatform','w');
}
catch(Exception e){
// Ohs nos…
}
@egwall
Copy link

egwall commented Jul 2, 2015

Slick! I like this!

@ebrandell-2
Copy link

On line 13 of runtime.java, don't you need to get an item from the ArrayList first before calling getValueFromHash?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment