diff --git a/core/lib/Drupal/Component/Plugin/Discovery/CachedDiscoveryInterface.php b/core/lib/Drupal/Component/Plugin/Discovery/CachedDiscoveryInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..27aa039c47f0d5160b5f0559a356d6d4d49c66cc
--- /dev/null
+++ b/core/lib/Drupal/Component/Plugin/Discovery/CachedDiscoveryInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface.
+ */
+
+namespace Drupal\Component\Plugin\Discovery;
+
+/**
+ * Interface for discovery compenents holding a cache of plugin definitions.
+ */
+interface CachedDiscoveryInterface extends DiscoveryInterface {
+
+  /**
+   * Clears cached plugin definitions.
+   */
+  public function clearCachedDefinitions();
+
+}
diff --git a/core/lib/Drupal/Component/Plugin/PluginManagerBase.php b/core/lib/Drupal/Component/Plugin/PluginManagerBase.php
index e4131fcd805b5a2923050c0bab020962a6c8fa2b..c337f9f7bd84703d9be0374c70d20b6b03dd13b7 100644
--- a/core/lib/Drupal/Component/Plugin/PluginManagerBase.php
+++ b/core/lib/Drupal/Component/Plugin/PluginManagerBase.php
@@ -8,11 +8,12 @@
 namespace Drupal\Component\Plugin;
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
 
 /**
  * Base class for plugin managers.
  */
-abstract class PluginManagerBase implements PluginManagerInterface {
+abstract class PluginManagerBase implements PluginManagerInterface, CachedDiscoveryInterface {
 
   /**
    * The object that discovers plugins managed by this manager.
@@ -67,6 +68,15 @@ public function getDefinitions() {
     return $definitions;
   }
 
+  /**
+   * Implements \Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface::clearCachedDefinitions().
+   */
+  public function clearCachedDefinitions() {
+    if ($this->discovery instanceof CachedDiscoveryInterface) {
+      $this->discovery->clearCachedDefinitions();
+    }
+  }
+
   /**
    * Implements Drupal\Component\Plugin\PluginManagerInterface::createInstance().
    */
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php b/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php
index 7a57395497f64ad25f25b009140b4ee41d342e8d..67d6cabe541181921039a8c2add48e06acd26594 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php
@@ -7,12 +7,13 @@
 
 namespace Drupal\Core\Plugin\Discovery;
 
+use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 
 /**
  * Enables static and persistent caching of discovered plugin definitions.
  */
-class CacheDecorator implements DiscoveryInterface {
+class CacheDecorator implements CachedDiscoveryInterface {
 
   /**
    * The cache key used to store the definition list.
@@ -109,6 +110,16 @@ protected function setCachedDefinitions($definitions) {
     $this->definitions = $definitions;
   }
 
+  /**
+   * Implements \Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface::clearCachedDefinitions().
+   */
+  public function clearCachedDefinitions() {
+    if (isset($this->cacheKey)) {
+      cache($this->cacheBin)->delete($this->cacheKey);
+    }
+    $this->definitions = NULL;
+  }
+
   /**
    * Passes through all unknown calls onto the decorated object.
    */
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php
index 6dc41636cbbc2c838f014427afb266b1bf554550..7f17d344c74bceff59a4c8a3cf9d0e7364841edd 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php
@@ -26,41 +26,18 @@ class FormatterPluginManager extends PluginManagerBase {
     'default_value' => TRUE,
   );
 
-  /**
-   * The cache bin used for plugin definitions.
-   *
-   * @var string
-   */
-  protected $cache_bin = 'field';
-
-  /**
-   * The cache id used for plugin definitions.
-   *
-   * @var string
-   */
-  protected $cache_id = 'field_formatter_types';
-
   /**
    * Constructs a FormatterPluginManager object.
    */
   public function __construct() {
-    $this->baseDiscovery = new AlterDecorator(new FormatterLegacyDiscoveryDecorator(new AnnotatedClassDiscovery('field', 'formatter')), 'field_formatter_info');
-    $this->discovery = new CacheDecorator($this->baseDiscovery, $this->cache_id, $this->cache_bin);
+    $this->discovery = new AnnotatedClassDiscovery('field', 'formatter');
+    $this->discovery = new FormatterLegacyDiscoveryDecorator($this->discovery);
+    $this->discovery = new AlterDecorator($this->discovery, 'field_formatter_info');
+    $this->discovery = new CacheDecorator($this->discovery, 'field_formatter_types', 'field');
 
     $this->factory = new FormatterFactory($this);
   }
 
-  /**
-   * Clears cached definitions.
-   *
-   * @todo Remove when http://drupal.org/node/1764232 is fixed.
-   */
-  public function clearDefinitions() {
-    // Clear 'static' data by creating a new object.
-    $this->discovery = new CacheDecorator($this->baseDiscovery, $this->cache_id, $this->cache_bin);
-    cache($this->cache_bin)->delete($this->cache_id);
-  }
-
   /**
    * Overrides PluginManagerBase::getInstance().
    */
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php
index a6051297dc975e8fa0361019ab6a3435e5a7ed6e..c5407bd1f03b69816ac54c6dc59bd47bee57d667 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php
@@ -27,41 +27,18 @@ class WidgetPluginManager extends PluginManagerBase {
     'default_value' => TRUE,
   );
 
-  /**
-   * The cache bin used for plugin definitions.
-   *
-   * @var string
-   */
-  protected $cache_bin = 'field';
-
-  /**
-   * The cache id used for plugin definitions.
-   *
-   * @var string
-   */
-  protected $cache_id = 'field_widget_types';
-
   /**
    * Constructs a WidgetPluginManager object.
    */
   public function __construct() {
-    $this->baseDiscovery = new AlterDecorator(new WidgetLegacyDiscoveryDecorator(new AnnotatedClassDiscovery('field', 'widget')), 'field_widget_info');
-    $this->discovery = new CacheDecorator($this->baseDiscovery, $this->cache_id, $this->cache_bin);
+    $this->discovery = new AnnotatedClassDiscovery('field', 'widget');
+    $this->discovery = new WidgetLegacyDiscoveryDecorator($this->discovery);
+    $this->discovery = new AlterDecorator($this->discovery, 'field_widget_info');
+    $this->discovery = new CacheDecorator($this->discovery, 'field_widget_types',  'field');
 
     $this->factory = new WidgetFactory($this);
   }
 
-  /**
-   * Clears cached definitions.
-   *
-   * @todo Remove when http://drupal.org/node/1764232 is fixed.
-   */
-  public function clearDefinitions() {
-    // Clear 'static' data by creating a new object.
-    $this->discovery = new CacheDecorator($this->baseDiscovery, $this->cache_id, $this->cache_bin);
-    cache($this->cache_bin)->delete($this->cache_id);
-  }
-
   /**
    * Overrides Drupal\Component\Plugin\PluginManagerBase::getInstance().
    */
diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..327e46e18123aa554462bd3b732b754f4b838dfc
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorTest.php
@@ -0,0 +1,136 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Plugin\CacheDecoratorTest.
+ */
+
+namespace Drupal\system\Tests\Plugin;
+
+use Drupal\system\Tests\Plugin\Discovery\DiscoveryTestBase;
+use Drupal\Component\Plugin\Discovery\StaticDiscovery;
+use Drupal\Core\Plugin\Discovery\CacheDecorator;
+
+/**
+ * Tests \Drupal\Core\Plugin\Discovery\CacheDecorator behavior.
+ */
+class CacheDecoratorTest extends DiscoveryTestBase {
+
+  /**
+   * The cache bin.
+   *
+   * @var string
+   */
+  protected $cacheBin = 'test_cacheDecorator';
+
+  /**
+   * The cache key.
+   *
+   * @var string
+   */
+  protected $cacheKey = 'test_cacheDecorator';
+
+  public static function getInfo() {
+    return array(
+      'name' => 'CacheDecorator',
+      'description' => 'Tests the CacheDecorator.',
+      'group' => 'Plugin API',
+    );
+  }
+
+  public function setUp() {
+    global $conf;
+
+    parent::setUp();
+
+    // Use a non-db cache backend, so that we can use DiscoveryTestBase (which
+    // extends UnitTestBase).
+    $conf['cache_classes'][$this->cacheBin] = 'Drupal\Core\Cache\MemoryBackend';
+
+    // Create discovery objects to test.
+    $this->emptyDiscovery = new StaticDiscovery();
+    $this->emptyDiscovery = new CacheDecorator($this->emptyDiscovery, $this->cacheKey . '_empty', $this->cacheBin);
+
+    $this->discovery = new StaticDiscovery();
+    $this->discovery = new CacheDecorator($this->discovery, $this->cacheKey, $this->cacheBin);
+
+    // Populate sample definitions.
+    $this->expectedDefinitions = array(
+      'apple' => array(
+        'label' => 'Apple',
+        'color' => 'green',
+      ),
+      'cherry' => array(
+        'label' => 'Cherry',
+        'color' => 'red',
+      ),
+      'orange' => array(
+        'label' => 'Orange',
+        'color' => 'orange',
+      ),
+    );
+    foreach ($this->expectedDefinitions as $plugin_id => $definition) {
+      $this->discovery->setDefinition($plugin_id, $definition);
+    }
+  }
+
+  /**
+   * Tests that discovered definitions are properly cached.
+   *
+   * This comes in addition to DiscoveryTestBase::testDiscoveryInterface(),
+   * that test the basic discovery behavior.
+   */
+  public function testCachedDefinitions() {
+    $cache = cache($this->cacheBin);
+
+    // Check that nothing is cached initially.
+    $cached = $cache->get($this->cacheKey);
+    $this->assertIdentical($cached, FALSE, 'Cache is empty.');
+
+    // Get the definitions once, and check that they are present in the cache.
+    $definitions = $this->discovery->getDefinitions();
+    $this->assertIdentical($definitions, $this->expectedDefinitions, 'Definitions are correctly retrieved.');
+    $cached = $cache->get($this->cacheKey);
+    $this->assertIdentical($cached->data, $this->expectedDefinitions, 'Definitions are cached.');
+
+    // Check that the definitions are also cached in memory. Since the
+    // CacheDecorator::definitions property is protected, this is tested "from
+    // the outside" by wiping the cache entry, getting the definitions, and
+    // checking that the cache entry was not regenerated (thus showing that
+    // defintions were not fetched from the decorated discovery).
+    $cache->delete($this->cacheKey);
+    $definitions = $this->discovery->getDefinitions();
+    $cached = $cache->get($this->cacheKey);
+    $this->assertIdentical($cached, FALSE, 'Cache is empty.');
+    $this->assertIdentical($definitions, $this->expectedDefinitions, 'Definitions are cached in memory.');
+  }
+
+  /**
+   * Tests CacheDecorator::clearCachedDefinitions().
+   */
+  public function testClearCachedDefinitions() {
+    $cache = cache($this->cacheBin);
+
+    // Populate the caches by collecting definitions once.
+    $this->discovery->getDefinitions();
+
+    // Add a new definition.
+    $this->expectedDefinitions['banana'] = array(
+      'label' => 'Banana',
+      'color' => 'yellow',
+    );
+    $this->discovery->setDefinition('banana', $this->expectedDefinitions['banana']);
+
+    // Check that the new definition is not found.
+    $definition = $this->discovery->getDefinition('banana');
+    $this->assertNull($definition, 'Newly added definition is not found.');
+
+    // Clear cached definitions, and check that the new definition is found.
+    $this->discovery->clearCachedDefinitions();
+    $cached = $cache->get($this->cacheKey);
+    $this->assertIdentical($cached, FALSE, 'Cache is empty.');
+    $definitions = $this->discovery->getDefinitions();
+    $this->assertIdentical($definitions, $this->expectedDefinitions, 'Newly added definition is found.');
+  }
+
+}