From 53e34a79f2825a789b7350c0dce48cd04af59c26 Mon Sep 17 00:00:00 2001
From: songzhipeng <songzhipeng@loopin.group>
Date: Thu, 27 Jul 2023 13:20:23 +0800
Subject: [PATCH] refactor: optimize cache code architeture

---
 .../cache/decorators/BlockingCache.java       | 27 +++---------
 .../ibatis/cache/decorators/FifoCache.java    | 32 +++-----------
 .../ibatis/cache/decorators/LoggingCache.java | 43 ++-----------------
 .../ibatis/cache/decorators/LruCache.java     | 29 +++----------
 .../cache/decorators/ScheduledCache.java      | 31 ++++---------
 .../cache/decorators/SerializedCache.java     | 41 +++---------------
 .../ibatis/cache/decorators/SoftCache.java    | 25 +++++------
 .../cache/decorators/SynchronizedCache.java   | 32 ++++----------
 .../cache/decorators/TransactionalCache.java  | 26 ++++-------
 .../ibatis/cache/decorators/WeakCache.java    | 25 +++++------
 .../ibatis/cache/impl/DelegateCache.java      | 42 ++++++++++++++++++
 11 files changed, 114 insertions(+), 239 deletions(-)
 create mode 100644 src/main/java/org/apache/ibatis/cache/impl/DelegateCache.java

diff --git a/src/main/java/org/apache/ibatis/cache/decorators/BlockingCache.java b/src/main/java/org/apache/ibatis/cache/decorators/BlockingCache.java
index b70fc0c61f8..62542367987 100644
--- a/src/main/java/org/apache/ibatis/cache/decorators/BlockingCache.java
+++ b/src/main/java/org/apache/ibatis/cache/decorators/BlockingCache.java
@@ -21,6 +21,7 @@
 
 import org.apache.ibatis.cache.Cache;
 import org.apache.ibatis.cache.CacheException;
+import org.apache.ibatis.cache.impl.DelegateCache;
 
 /**
  * <p>
@@ -34,31 +35,20 @@
  *
  * @author Eduardo Macarron
  */
-public class BlockingCache implements Cache {
+public class BlockingCache extends DelegateCache {
 
   private long timeout;
-  private final Cache delegate;
   private final ConcurrentHashMap<Object, CountDownLatch> locks;
 
   public BlockingCache(Cache delegate) {
-    this.delegate = delegate;
+    super(delegate);
     this.locks = new ConcurrentHashMap<>();
   }
 
-  @Override
-  public String getId() {
-    return delegate.getId();
-  }
-
-  @Override
-  public int getSize() {
-    return delegate.getSize();
-  }
-
   @Override
   public void putObject(Object key, Object value) {
     try {
-      delegate.putObject(key, value);
+      super.putObject(key, value);
     } finally {
       releaseLock(key);
     }
@@ -67,7 +57,7 @@ public void putObject(Object key, Object value) {
   @Override
   public Object getObject(Object key) {
     acquireLock(key);
-    Object value = delegate.getObject(key);
+    Object value = super.getObject(key);
     if (value != null) {
       releaseLock(key);
     }
@@ -81,11 +71,6 @@ public Object removeObject(Object key) {
     return null;
   }
 
-  @Override
-  public void clear() {
-    delegate.clear();
-  }
-
   private void acquireLock(Object key) {
     CountDownLatch newLatch = new CountDownLatch(1);
     while (true) {
@@ -98,7 +83,7 @@ private void acquireLock(Object key) {
           boolean acquired = latch.await(timeout, TimeUnit.MILLISECONDS);
           if (!acquired) {
             throw new CacheException(
-                "Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
+                "Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + super.getId());
           }
         } else {
           latch.await();
diff --git a/src/main/java/org/apache/ibatis/cache/decorators/FifoCache.java b/src/main/java/org/apache/ibatis/cache/decorators/FifoCache.java
index f9550c22726..935e5b65917 100644
--- a/src/main/java/org/apache/ibatis/cache/decorators/FifoCache.java
+++ b/src/main/java/org/apache/ibatis/cache/decorators/FifoCache.java
@@ -19,34 +19,24 @@
 import java.util.LinkedList;
 
 import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.cache.impl.DelegateCache;
 
 /**
  * FIFO (first in, first out) cache decorator.
  *
  * @author Clinton Begin
  */
-public class FifoCache implements Cache {
+public class FifoCache extends DelegateCache {
 
-  private final Cache delegate;
   private final Deque<Object> keyList;
   private int size;
 
   public FifoCache(Cache delegate) {
-    this.delegate = delegate;
+    super(delegate);
     this.keyList = new LinkedList<>();
     this.size = 1024;
   }
 
-  @Override
-  public String getId() {
-    return delegate.getId();
-  }
-
-  @Override
-  public int getSize() {
-    return delegate.getSize();
-  }
-
   public void setSize(int size) {
     this.size = size;
   }
@@ -54,22 +44,12 @@ public void setSize(int size) {
   @Override
   public void putObject(Object key, Object value) {
     cycleKeyList(key);
-    delegate.putObject(key, value);
-  }
-
-  @Override
-  public Object getObject(Object key) {
-    return delegate.getObject(key);
-  }
-
-  @Override
-  public Object removeObject(Object key) {
-    return delegate.removeObject(key);
+    super.putObject(key, value);
   }
 
   @Override
   public void clear() {
-    delegate.clear();
+    super.clear();
     keyList.clear();
   }
 
@@ -77,7 +57,7 @@ private void cycleKeyList(Object key) {
     keyList.addLast(key);
     if (keyList.size() > size) {
       Object oldestKey = keyList.removeFirst();
-      delegate.removeObject(oldestKey);
+      super.removeObject(oldestKey);
     }
   }
 
diff --git a/src/main/java/org/apache/ibatis/cache/decorators/LoggingCache.java b/src/main/java/org/apache/ibatis/cache/decorators/LoggingCache.java
index 6ff00cb2e06..9665b56b378 100644
--- a/src/main/java/org/apache/ibatis/cache/decorators/LoggingCache.java
+++ b/src/main/java/org/apache/ibatis/cache/decorators/LoggingCache.java
@@ -16,43 +16,28 @@
 package org.apache.ibatis.cache.decorators;
 
 import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.cache.impl.DelegateCache;
 import org.apache.ibatis.logging.Log;
 import org.apache.ibatis.logging.LogFactory;
 
 /**
  * @author Clinton Begin
  */
-public class LoggingCache implements Cache {
+public class LoggingCache extends DelegateCache {
 
   private final Log log;
-  private final Cache delegate;
   protected int requests;
   protected int hits;
 
   public LoggingCache(Cache delegate) {
-    this.delegate = delegate;
+    super(delegate);
     this.log = LogFactory.getLog(getId());
   }
 
-  @Override
-  public String getId() {
-    return delegate.getId();
-  }
-
-  @Override
-  public int getSize() {
-    return delegate.getSize();
-  }
-
-  @Override
-  public void putObject(Object key, Object object) {
-    delegate.putObject(key, object);
-  }
-
   @Override
   public Object getObject(Object key) {
     requests++;
-    final Object value = delegate.getObject(key);
+    final Object value = super.getObject(key);
     if (value != null) {
       hits++;
     }
@@ -62,26 +47,6 @@ public Object getObject(Object key) {
     return value;
   }
 
-  @Override
-  public Object removeObject(Object key) {
-    return delegate.removeObject(key);
-  }
-
-  @Override
-  public void clear() {
-    delegate.clear();
-  }
-
-  @Override
-  public int hashCode() {
-    return delegate.hashCode();
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    return delegate.equals(obj);
-  }
-
   private double getHitRatio() {
     return (double) hits / (double) requests;
   }
diff --git a/src/main/java/org/apache/ibatis/cache/decorators/LruCache.java b/src/main/java/org/apache/ibatis/cache/decorators/LruCache.java
index 1c593499471..e6057d6c23e 100644
--- a/src/main/java/org/apache/ibatis/cache/decorators/LruCache.java
+++ b/src/main/java/org/apache/ibatis/cache/decorators/LruCache.java
@@ -19,33 +19,23 @@
 import java.util.Map;
 
 import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.cache.impl.DelegateCache;
 
 /**
  * Lru (least recently used) cache decorator.
  *
  * @author Clinton Begin
  */
-public class LruCache implements Cache {
+public class LruCache extends DelegateCache {
 
-  private final Cache delegate;
   private Map<Object, Object> keyMap;
   private Object eldestKey;
 
   public LruCache(Cache delegate) {
-    this.delegate = delegate;
+    super(delegate);
     setSize(1024);
   }
 
-  @Override
-  public String getId() {
-    return delegate.getId();
-  }
-
-  @Override
-  public int getSize() {
-    return delegate.getSize();
-  }
-
   public void setSize(final int size) {
     keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
       private static final long serialVersionUID = 4267176411845948333L;
@@ -63,31 +53,26 @@ protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
 
   @Override
   public void putObject(Object key, Object value) {
-    delegate.putObject(key, value);
+    super.putObject(key, value);
     cycleKeyList(key);
   }
 
   @Override
   public Object getObject(Object key) {
     keyMap.get(key); // touch
-    return delegate.getObject(key);
-  }
-
-  @Override
-  public Object removeObject(Object key) {
-    return delegate.removeObject(key);
+    return super.getObject(key);
   }
 
   @Override
   public void clear() {
-    delegate.clear();
+    super.clear();
     keyMap.clear();
   }
 
   private void cycleKeyList(Object key) {
     keyMap.put(key, key);
     if (eldestKey != null) {
-      delegate.removeObject(eldestKey);
+      super.removeObject(eldestKey);
       eldestKey = null;
     }
   }
diff --git a/src/main/java/org/apache/ibatis/cache/decorators/ScheduledCache.java b/src/main/java/org/apache/ibatis/cache/decorators/ScheduledCache.java
index 2fb059da846..3d13dc58c66 100644
--- a/src/main/java/org/apache/ibatis/cache/decorators/ScheduledCache.java
+++ b/src/main/java/org/apache/ibatis/cache/decorators/ScheduledCache.java
@@ -18,18 +18,18 @@
 import java.util.concurrent.TimeUnit;
 
 import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.cache.impl.DelegateCache;
 
 /**
  * @author Clinton Begin
  */
-public class ScheduledCache implements Cache {
+public class ScheduledCache extends DelegateCache {
 
-  private final Cache delegate;
   protected long clearInterval;
   protected long lastClear;
 
   public ScheduledCache(Cache delegate) {
-    this.delegate = delegate;
+    super(delegate);
     this.clearInterval = TimeUnit.HOURS.toMillis(1);
     this.lastClear = System.currentTimeMillis();
   }
@@ -38,48 +38,33 @@ public void setClearInterval(long clearInterval) {
     this.clearInterval = clearInterval;
   }
 
-  @Override
-  public String getId() {
-    return delegate.getId();
-  }
-
   @Override
   public int getSize() {
     clearWhenStale();
-    return delegate.getSize();
+    return super.getSize();
   }
 
   @Override
   public void putObject(Object key, Object object) {
     clearWhenStale();
-    delegate.putObject(key, object);
+    super.putObject(key, object);
   }
 
   @Override
   public Object getObject(Object key) {
-    return clearWhenStale() ? null : delegate.getObject(key);
+    return clearWhenStale() ? null : super.getObject(key);
   }
 
   @Override
   public Object removeObject(Object key) {
     clearWhenStale();
-    return delegate.removeObject(key);
+    return super.removeObject(key);
   }
 
   @Override
   public void clear() {
     lastClear = System.currentTimeMillis();
-    delegate.clear();
-  }
-
-  @Override
-  public int hashCode() {
-    return delegate.hashCode();
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    return delegate.equals(obj);
+    super.clear();
   }
 
   private boolean clearWhenStale() {
diff --git a/src/main/java/org/apache/ibatis/cache/decorators/SerializedCache.java b/src/main/java/org/apache/ibatis/cache/decorators/SerializedCache.java
index 5726c8d2556..b419dd1ad2c 100644
--- a/src/main/java/org/apache/ibatis/cache/decorators/SerializedCache.java
+++ b/src/main/java/org/apache/ibatis/cache/decorators/SerializedCache.java
@@ -26,28 +26,17 @@
 
 import org.apache.ibatis.cache.Cache;
 import org.apache.ibatis.cache.CacheException;
+import org.apache.ibatis.cache.impl.DelegateCache;
 import org.apache.ibatis.io.Resources;
 import org.apache.ibatis.io.SerialFilterChecker;
 
 /**
  * @author Clinton Begin
  */
-public class SerializedCache implements Cache {
-
-  private final Cache delegate;
+public class SerializedCache extends DelegateCache {
 
   public SerializedCache(Cache delegate) {
-    this.delegate = delegate;
-  }
-
-  @Override
-  public String getId() {
-    return delegate.getId();
-  }
-
-  @Override
-  public int getSize() {
-    return delegate.getSize();
+    super(delegate);
   }
 
   @Override
@@ -55,35 +44,15 @@ public void putObject(Object key, Object object) {
     if ((object != null) && !(object instanceof Serializable)) {
       throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);
     }
-    delegate.putObject(key, serialize((Serializable) object));
+    super.putObject(key, serialize((Serializable) object));
   }
 
   @Override
   public Object getObject(Object key) {
-    Object object = delegate.getObject(key);
+    Object object = super.getObject(key);
     return object == null ? null : deserialize((byte[]) object);
   }
 
-  @Override
-  public Object removeObject(Object key) {
-    return delegate.removeObject(key);
-  }
-
-  @Override
-  public void clear() {
-    delegate.clear();
-  }
-
-  @Override
-  public int hashCode() {
-    return delegate.hashCode();
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    return delegate.equals(obj);
-  }
-
   private byte[] serialize(Serializable value) {
     try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
         ObjectOutputStream oos = new ObjectOutputStream(bos)) {
diff --git a/src/main/java/org/apache/ibatis/cache/decorators/SoftCache.java b/src/main/java/org/apache/ibatis/cache/decorators/SoftCache.java
index daef435e890..e1b3f400bcb 100644
--- a/src/main/java/org/apache/ibatis/cache/decorators/SoftCache.java
+++ b/src/main/java/org/apache/ibatis/cache/decorators/SoftCache.java
@@ -21,6 +21,7 @@
 import java.util.LinkedList;
 
 import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.cache.impl.DelegateCache;
 
 /**
  * Soft Reference cache decorator.
@@ -29,28 +30,22 @@
  *
  * @author Clinton Begin
  */
-public class SoftCache implements Cache {
+public class SoftCache extends DelegateCache {
   private final Deque<Object> hardLinksToAvoidGarbageCollection;
   private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
-  private final Cache delegate;
   private int numberOfHardLinks;
 
   public SoftCache(Cache delegate) {
-    this.delegate = delegate;
+    super(delegate);
     this.numberOfHardLinks = 256;
     this.hardLinksToAvoidGarbageCollection = new LinkedList<>();
     this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();
   }
 
-  @Override
-  public String getId() {
-    return delegate.getId();
-  }
-
   @Override
   public int getSize() {
     removeGarbageCollectedItems();
-    return delegate.getSize();
+    return super.getSize();
   }
 
   public void setSize(int size) {
@@ -60,18 +55,18 @@ public void setSize(int size) {
   @Override
   public void putObject(Object key, Object value) {
     removeGarbageCollectedItems();
-    delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));
+    super.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries));
   }
 
   @Override
   public Object getObject(Object key) {
     Object result = null;
     @SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache
-    SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key);
+    SoftReference<Object> softReference = (SoftReference<Object>) super.getObject(key);
     if (softReference != null) {
       result = softReference.get();
       if (result == null) {
-        delegate.removeObject(key);
+        super.removeObject(key);
       } else {
         // See #586 (and #335) modifications need more than a read lock
         synchronized (hardLinksToAvoidGarbageCollection) {
@@ -89,7 +84,7 @@ public Object getObject(Object key) {
   public Object removeObject(Object key) {
     removeGarbageCollectedItems();
     @SuppressWarnings("unchecked")
-    SoftReference<Object> softReference = (SoftReference<Object>) delegate.removeObject(key);
+    SoftReference<Object> softReference = (SoftReference<Object>) super.removeObject(key);
     return softReference == null ? null : softReference.get();
   }
 
@@ -99,13 +94,13 @@ public void clear() {
       hardLinksToAvoidGarbageCollection.clear();
     }
     removeGarbageCollectedItems();
-    delegate.clear();
+    super.clear();
   }
 
   private void removeGarbageCollectedItems() {
     SoftEntry sv;
     while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) {
-      delegate.removeObject(sv.key);
+      super.removeObject(sv.key);
     }
   }
 
diff --git a/src/main/java/org/apache/ibatis/cache/decorators/SynchronizedCache.java b/src/main/java/org/apache/ibatis/cache/decorators/SynchronizedCache.java
index 72f1dbccf85..35c19f14b7f 100644
--- a/src/main/java/org/apache/ibatis/cache/decorators/SynchronizedCache.java
+++ b/src/main/java/org/apache/ibatis/cache/decorators/SynchronizedCache.java
@@ -16,56 +16,40 @@
 package org.apache.ibatis.cache.decorators;
 
 import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.cache.impl.DelegateCache;
 
 /**
  * @author Clinton Begin
  */
-public class SynchronizedCache implements Cache {
-
-  private final Cache delegate;
+public class SynchronizedCache extends DelegateCache {
 
   public SynchronizedCache(Cache delegate) {
-    this.delegate = delegate;
-  }
-
-  @Override
-  public String getId() {
-    return delegate.getId();
+    super(delegate);
   }
 
   @Override
   public synchronized int getSize() {
-    return delegate.getSize();
+    return super.getSize();
   }
 
   @Override
   public synchronized void putObject(Object key, Object object) {
-    delegate.putObject(key, object);
+    super.putObject(key, object);
   }
 
   @Override
   public synchronized Object getObject(Object key) {
-    return delegate.getObject(key);
+    return super.getObject(key);
   }
 
   @Override
   public synchronized Object removeObject(Object key) {
-    return delegate.removeObject(key);
+    return super.removeObject(key);
   }
 
   @Override
   public synchronized void clear() {
-    delegate.clear();
-  }
-
-  @Override
-  public int hashCode() {
-    return delegate.hashCode();
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    return delegate.equals(obj);
+    super.clear();
   }
 
 }
diff --git a/src/main/java/org/apache/ibatis/cache/decorators/TransactionalCache.java b/src/main/java/org/apache/ibatis/cache/decorators/TransactionalCache.java
index cb022484854..b314c8ee8be 100644
--- a/src/main/java/org/apache/ibatis/cache/decorators/TransactionalCache.java
+++ b/src/main/java/org/apache/ibatis/cache/decorators/TransactionalCache.java
@@ -21,6 +21,7 @@
 import java.util.Set;
 
 import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.cache.impl.DelegateCache;
 import org.apache.ibatis.logging.Log;
 import org.apache.ibatis.logging.LogFactory;
 
@@ -35,36 +36,25 @@
  * @author Clinton Begin
  * @author Eduardo Macarron
  */
-public class TransactionalCache implements Cache {
+public class TransactionalCache extends DelegateCache {
 
   private static final Log log = LogFactory.getLog(TransactionalCache.class);
 
-  private final Cache delegate;
   private boolean clearOnCommit;
   private final Map<Object, Object> entriesToAddOnCommit;
   private final Set<Object> entriesMissedInCache;
 
   public TransactionalCache(Cache delegate) {
-    this.delegate = delegate;
+    super(delegate);
     this.clearOnCommit = false;
     this.entriesToAddOnCommit = new HashMap<>();
     this.entriesMissedInCache = new HashSet<>();
   }
 
-  @Override
-  public String getId() {
-    return delegate.getId();
-  }
-
-  @Override
-  public int getSize() {
-    return delegate.getSize();
-  }
-
   @Override
   public Object getObject(Object key) {
     // issue #116
-    Object object = delegate.getObject(key);
+    Object object = super.getObject(key);
     if (object == null) {
       entriesMissedInCache.add(key);
     }
@@ -93,7 +83,7 @@ public void clear() {
 
   public void commit() {
     if (clearOnCommit) {
-      delegate.clear();
+      super.clear();
     }
     flushPendingEntries();
     reset();
@@ -112,11 +102,11 @@ private void reset() {
 
   private void flushPendingEntries() {
     for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
-      delegate.putObject(entry.getKey(), entry.getValue());
+      super.putObject(entry.getKey(), entry.getValue());
     }
     for (Object entry : entriesMissedInCache) {
       if (!entriesToAddOnCommit.containsKey(entry)) {
-        delegate.putObject(entry, null);
+        super.putObject(entry, null);
       }
     }
   }
@@ -124,7 +114,7 @@ private void flushPendingEntries() {
   private void unlockMissedEntries() {
     for (Object entry : entriesMissedInCache) {
       try {
-        delegate.removeObject(entry);
+        super.removeObject(entry);
       } catch (Exception e) {
         log.warn("Unexpected exception while notifying a rollback to the cache adapter. "
             + "Consider upgrading your cache adapter to the latest version. Cause: " + e);
diff --git a/src/main/java/org/apache/ibatis/cache/decorators/WeakCache.java b/src/main/java/org/apache/ibatis/cache/decorators/WeakCache.java
index 8af6f1d0b5a..b0c8aafa778 100644
--- a/src/main/java/org/apache/ibatis/cache/decorators/WeakCache.java
+++ b/src/main/java/org/apache/ibatis/cache/decorators/WeakCache.java
@@ -21,6 +21,7 @@
 import java.util.LinkedList;
 
 import org.apache.ibatis.cache.Cache;
+import org.apache.ibatis.cache.impl.DelegateCache;
 
 /**
  * Weak Reference cache decorator.
@@ -29,28 +30,22 @@
  *
  * @author Clinton Begin
  */
-public class WeakCache implements Cache {
+public class WeakCache extends DelegateCache {
   private final Deque<Object> hardLinksToAvoidGarbageCollection;
   private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
-  private final Cache delegate;
   private int numberOfHardLinks;
 
   public WeakCache(Cache delegate) {
-    this.delegate = delegate;
+    super(delegate);
     this.numberOfHardLinks = 256;
     this.hardLinksToAvoidGarbageCollection = new LinkedList<>();
     this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();
   }
 
-  @Override
-  public String getId() {
-    return delegate.getId();
-  }
-
   @Override
   public int getSize() {
     removeGarbageCollectedItems();
-    return delegate.getSize();
+    return super.getSize();
   }
 
   public void setSize(int size) {
@@ -60,18 +55,18 @@ public void setSize(int size) {
   @Override
   public void putObject(Object key, Object value) {
     removeGarbageCollectedItems();
-    delegate.putObject(key, new WeakEntry(key, value, queueOfGarbageCollectedEntries));
+    super.putObject(key, new WeakEntry(key, value, queueOfGarbageCollectedEntries));
   }
 
   @Override
   public Object getObject(Object key) {
     Object result = null;
     @SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache
-    WeakReference<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);
+    WeakReference<Object> weakReference = (WeakReference<Object>) super.getObject(key);
     if (weakReference != null) {
       result = weakReference.get();
       if (result == null) {
-        delegate.removeObject(key);
+        super.removeObject(key);
       } else {
         synchronized (hardLinksToAvoidGarbageCollection) {
           hardLinksToAvoidGarbageCollection.addFirst(result);
@@ -88,7 +83,7 @@ public Object getObject(Object key) {
   public Object removeObject(Object key) {
     removeGarbageCollectedItems();
     @SuppressWarnings("unchecked")
-    WeakReference<Object> weakReference = (WeakReference<Object>) delegate.removeObject(key);
+    WeakReference<Object> weakReference = (WeakReference<Object>) super.removeObject(key);
     return weakReference == null ? null : weakReference.get();
   }
 
@@ -98,13 +93,13 @@ public void clear() {
       hardLinksToAvoidGarbageCollection.clear();
     }
     removeGarbageCollectedItems();
-    delegate.clear();
+    super.clear();
   }
 
   private void removeGarbageCollectedItems() {
     WeakEntry sv;
     while ((sv = (WeakEntry) queueOfGarbageCollectedEntries.poll()) != null) {
-      delegate.removeObject(sv.key);
+      super.removeObject(sv.key);
     }
   }
 
diff --git a/src/main/java/org/apache/ibatis/cache/impl/DelegateCache.java b/src/main/java/org/apache/ibatis/cache/impl/DelegateCache.java
new file mode 100644
index 00000000000..0b6cded9d96
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/cache/impl/DelegateCache.java
@@ -0,0 +1,42 @@
+package org.apache.ibatis.cache.impl;
+
+import org.apache.ibatis.cache.Cache;
+
+public abstract class DelegateCache implements Cache {
+
+  private final Cache delegate;
+
+  public DelegateCache(Cache delegate) {
+    this.delegate = delegate;
+  }
+
+  @Override
+  public String getId() {
+    return delegate.getId();
+  }
+
+  @Override
+  public void putObject(Object key, Object value) {
+    delegate.putObject(key, value);
+  }
+
+  @Override
+  public Object getObject(Object key) {
+    return delegate.getObject(key);
+  }
+
+  @Override
+  public Object removeObject(Object key) {
+    return delegate.removeObject(key);
+  }
+
+  @Override
+  public void clear() {
+    delegate.clear();
+  }
+
+  @Override
+  public int getSize() {
+    return delegate.getSize();
+  }
+}