diff --git a/module/VuFind/src/VuFind/Controller/Factory.php b/module/VuFind/src/VuFind/Controller/Factory.php
index a6330d48f0521a7afee16a6ab30e297d4b70c4a7..f15bb99b0399410c6699eb8eaaab5af521f9555c 100644
--- a/module/VuFind/src/VuFind/Controller/Factory.php
+++ b/module/VuFind/src/VuFind/Controller/Factory.php
@@ -40,45 +40,8 @@ use Zend\ServiceManager\ServiceManager;
  *
  * @codeCoverageIgnore
  */
-class Factory
+class Factory extends GenericFactory
 {
-    /**
-     * Construct a generic controller.
-     *
-     * @param string         $name Name of table to construct (fully qualified
-     * class name, or else a class name within the current namespace)
-     * @param ServiceManager $sm   Service manager
-     *
-     * @return object
-     */
-    public static function getGenericController($name, ServiceManager $sm)
-    {
-        // Prepend the current namespace unless we receive a FQCN:
-        $class = (strpos($name, '\\') === false)
-            ? __NAMESPACE__ . '\\' . $name : $name;
-        if (!class_exists($class)) {
-            throw new \Exception('Cannot construct ' . $class);
-        }
-        return new $class($sm->getServiceLocator());
-    }
-
-    /**
-     * Construct a generic controller.
-     *
-     * @param string $name Method name being called
-     * @param array  $args Method arguments
-     *
-     * @return object
-     */
-    public static function __callStatic($name, $args)
-    {
-        // Strip "get" from method name to get name of class; pass first argument
-        // on assumption that it should be the ServiceManager object.
-        return static::getGenericController(
-            substr($name, 3), isset($args[0]) ? $args[0] : null
-        );
-    }
-
     /**
      * Construct the BrowseController.
      *
diff --git a/module/VuFind/src/VuFind/Controller/GenericFactory.php b/module/VuFind/src/VuFind/Controller/GenericFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..1a3141d51783ebf15ce155dcb0488e6123496a3e
--- /dev/null
+++ b/module/VuFind/src/VuFind/Controller/GenericFactory.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Generic factory for controllers (contains generic default behavior shared
+ * across VuFind's modules).
+ *
+ * PHP version 5
+ *
+ * Copyright (C) Villanova University 2017.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * @category VuFind
+ * @package  Controller
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ */
+namespace VuFind\Controller;
+
+use Zend\ServiceManager\ServiceManager;
+
+/**
+ * Generic factory for controllers (contains generic default behavior shared
+ * across VuFind's modules).
+ *
+ * @category VuFind
+ * @package  Controller
+ * @author   Demian Katz <demian.katz@villanova.edu>
+ * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
+ * @link     https://vufind.org/wiki/development Wiki
+ *
+ * @codeCoverageIgnore
+ */
+class GenericFactory
+{
+    /**
+     * Construct a generic controller.
+     *
+     * @param string         $name Name of table to construct (fully qualified
+     * class name, or else a class name within the current namespace)
+     * @param ServiceManager $sm   Service manager
+     *
+     * @return object
+     */
+    public static function getGenericController($name, ServiceManager $sm)
+    {
+        // Prepend the current (inherited) namespace unless we receive a FQCN:
+        $class = (strpos($name, '\\') === false)
+            ? substr(static::class, 0, strrpos(static::class, '\\') + 1) . $name
+            : $name;
+        if (!class_exists($class)) {
+            throw new \Exception('Cannot construct ' . $class);
+        }
+        return new $class($sm->getServiceLocator());
+    }
+
+    /**
+     * Construct a generic controller.
+     *
+     * @param string $name Method name being called
+     * @param array  $args Method arguments
+     *
+     * @return object
+     */
+    public static function __callStatic($name, $args)
+    {
+        // Strip "get" from method name to get name of class; pass first argument
+        // on assumption that it should be the ServiceManager object.
+        return static::getGenericController(
+            substr($name, 3), isset($args[0]) ? $args[0] : null
+        );
+    }
+}
diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/Factory.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/Factory.php
index ff3b51a0df89f8fdca61a8d0061b8af531f80a9f..2edd4d44783339564a45ba73e1b36bc1a65f1c63 100644
--- a/module/VuFindAdmin/src/VuFindAdmin/Controller/Factory.php
+++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/Factory.php
@@ -40,42 +40,6 @@ use Zend\ServiceManager\ServiceManager;
  *
  * @codeCoverageIgnore
  */
-class Factory
+class Factory extends \VuFind\Controller\GenericFactory
 {
-    /**
-     * Construct a generic controller.
-     *
-     * @param string         $name Name of table to construct (fully qualified
-     * class name, or else a class name within the current namespace)
-     * @param ServiceManager $sm   Service manager
-     *
-     * @return object
-     */
-    public static function getGenericController($name, ServiceManager $sm)
-    {
-        // Prepend the current namespace unless we receive a FQCN:
-        $class = (strpos($name, '\\') === false)
-            ? __NAMESPACE__ . '\\' . $name : $name;
-        if (!class_exists($class)) {
-            throw new \Exception('Cannot construct ' . $class);
-        }
-        return new $class($sm->getServiceLocator());
-    }
-
-    /**
-     * Construct a generic controller.
-     *
-     * @param string $name Method name being called
-     * @param array  $args Method arguments
-     *
-     * @return object
-     */
-    public static function __callStatic($name, $args)
-    {
-        // Strip "get" from method name to get name of class; pass first argument
-        // on assumption that it should be the ServiceManager object.
-        return static::getGenericController(
-            substr($name, 3), isset($args[0]) ? $args[0] : null
-        );
-    }
 }
diff --git a/module/VuFindConsole/src/VuFindConsole/Controller/Factory.php b/module/VuFindConsole/src/VuFindConsole/Controller/Factory.php
index a17a9121e79bee1006e742b8ba94b385badefb25..50a748673be8d6ee757cd26fd9fe510ac53b4f80 100644
--- a/module/VuFindConsole/src/VuFindConsole/Controller/Factory.php
+++ b/module/VuFindConsole/src/VuFindConsole/Controller/Factory.php
@@ -40,42 +40,6 @@ use Zend\ServiceManager\ServiceManager;
  *
  * @codeCoverageIgnore
  */
-class Factory
+class Factory extends \VuFind\Controller\GenericFactory
 {
-    /**
-     * Construct a generic controller.
-     *
-     * @param string         $name Name of table to construct (fully qualified
-     * class name, or else a class name within the current namespace)
-     * @param ServiceManager $sm   Service manager
-     *
-     * @return object
-     */
-    public static function getGenericController($name, ServiceManager $sm)
-    {
-        // Prepend the current namespace unless we receive a FQCN:
-        $class = (strpos($name, '\\') === false)
-            ? __NAMESPACE__ . '\\' . $name : $name;
-        if (!class_exists($class)) {
-            throw new \Exception('Cannot construct ' . $class);
-        }
-        return new $class($sm->getServiceLocator());
-    }
-
-    /**
-     * Construct a generic controller.
-     *
-     * @param string $name Method name being called
-     * @param array  $args Method arguments
-     *
-     * @return object
-     */
-    public static function __callStatic($name, $args)
-    {
-        // Strip "get" from method name to get name of class; pass first argument
-        // on assumption that it should be the ServiceManager object.
-        return static::getGenericController(
-            substr($name, 3), isset($args[0]) ? $args[0] : null
-        );
-    }
 }
diff --git a/module/VuFindDevTools/src/VuFindDevTools/Controller/Factory.php b/module/VuFindDevTools/src/VuFindDevTools/Controller/Factory.php
index 433d8243cd4b88c47814ae39e33b6cf8276f3776..bea3d5e62353a801938b0f36daf43769fd7d2f96 100644
--- a/module/VuFindDevTools/src/VuFindDevTools/Controller/Factory.php
+++ b/module/VuFindDevTools/src/VuFindDevTools/Controller/Factory.php
@@ -40,42 +40,6 @@ use Zend\ServiceManager\ServiceManager;
  *
  * @codeCoverageIgnore
  */
-class Factory
+class Factory extends \VuFind\Controller\GenericFactory
 {
-    /**
-     * Construct a generic controller.
-     *
-     * @param string         $name Name of table to construct (fully qualified
-     * class name, or else a class name within the current namespace)
-     * @param ServiceManager $sm   Service manager
-     *
-     * @return object
-     */
-    public static function getGenericController($name, ServiceManager $sm)
-    {
-        // Prepend the current namespace unless we receive a FQCN:
-        $class = (strpos($name, '\\') === false)
-            ? __NAMESPACE__ . '\\' . $name : $name;
-        if (!class_exists($class)) {
-            throw new \Exception('Cannot construct ' . $class);
-        }
-        return new $class($sm->getServiceLocator());
-    }
-
-    /**
-     * Construct a generic controller.
-     *
-     * @param string $name Method name being called
-     * @param array  $args Method arguments
-     *
-     * @return object
-     */
-    public static function __callStatic($name, $args)
-    {
-        // Strip "get" from method name to get name of class; pass first argument
-        // on assumption that it should be the ServiceManager object.
-        return static::getGenericController(
-            substr($name, 3), isset($args[0]) ? $args[0] : null
-        );
-    }
 }