Create renderers for ElementType and subclasses (#2080)

diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart
index a3a8b85..a8fe868 100644
--- a/lib/src/element_type.dart
+++ b/lib/src/element_type.dart
@@ -11,7 +11,7 @@
 import 'package:analyzer/dart/element/nullability_suffix.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:dartdoc/src/model/model.dart';
-import 'package:dartdoc/src/render/parameter_renderer.dart';
+import 'package:dartdoc/src/render/element_type_renderer.dart';
 
 /// Base class representing a type in Dartdoc.  It wraps a [DartType], and
 /// may link to a [ModelElement].
@@ -143,13 +143,8 @@
   @override
   String get linkedName {
     if (_linkedName == null) {
-      StringBuffer buf = StringBuffer();
-      buf.write('${returnType.linkedName} ');
-      buf.write('${nameWithGenerics}');
-      buf.write('<span class="signature">');
-      buf.write('(${ParameterRendererHtml().renderLinkedParams(parameters)})');
-      buf.write('</span>');
-      _linkedName = buf.toString();
+      _linkedName =
+          FunctionTypeElementTypeRendererHtml().renderLinkedName(this);
     }
     return _linkedName;
   }
@@ -162,17 +157,8 @@
   @override
   String get nameWithGenerics {
     if (_nameWithGenerics == null) {
-      StringBuffer buf = StringBuffer();
-      buf.write(name);
-      if ((type as FunctionType).typeFormals.isNotEmpty) {
-        if (!typeFormals.every((t) => t.name == 'dynamic')) {
-          buf.write('&lt;<wbr><span class="type-parameter">');
-          buf.writeAll(typeFormals.map((t) => t.name),
-              '</span>, <span class="type-parameter">');
-          buf.write('</span>&gt;');
-        }
-      }
-      _nameWithGenerics = buf.toString();
+      _nameWithGenerics =
+          FunctionTypeElementTypeRendererHtml().renderNameWithGenerics(this);
     }
     return _nameWithGenerics;
   }
@@ -198,21 +184,8 @@
   @override
   String get linkedName {
     if (_linkedName == null) {
-      StringBuffer buf = StringBuffer();
-
-      buf.write(element.linkedName);
-
-      if (!typeArguments.every((t) => t.name == 'dynamic') &&
-          typeArguments.isNotEmpty) {
-        buf.write('<span class="signature">');
-        buf.write('&lt;<wbr><span class="type-parameter">');
-        buf.writeAll(typeArguments.map((t) => t.linkedName),
-            '</span>, <span class="type-parameter">');
-        buf.write('</span>&gt;');
-        buf.write('</span>');
-      }
-
-      _linkedName = buf.toString();
+      _linkedName =
+          ParameterizedElementTypeRendererHtml().renderLinkedName(this);
     }
     return _linkedName;
   }
@@ -221,18 +194,8 @@
   @override
   String get nameWithGenerics {
     if (_nameWithGenerics == null) {
-      StringBuffer buf = StringBuffer();
-
-      buf.write(element.name);
-
-      if (!typeArguments.every((t) => t.name == 'dynamic') &&
-          typeArguments.isNotEmpty) {
-        buf.write('&lt;<wbr><span class="type-parameter">');
-        buf.writeAll(typeArguments.map((t) => t.nameWithGenerics),
-            '</span>, <span class="type-parameter">');
-        buf.write('</span>&gt;');
-      }
-      _nameWithGenerics = buf.toString();
+      _nameWithGenerics =
+          ParameterizedElementTypeRendererHtml().renderNameWithGenerics(this);
     }
     return _nameWithGenerics;
   }
@@ -246,17 +209,12 @@
   @override
   String get linkedName => name;
 
-  String _nameWithGenerics;
   @override
-  String get nameWithGenerics {
-    if (_nameWithGenerics == null) {
-      _nameWithGenerics = name;
-    }
-    return _nameWithGenerics;
-  }
+  String get nameWithGenerics => name;
 
   @override
   ClassElement get _boundClassElement => interfaceType.element;
+
   @override
   InterfaceType get interfaceType => (type as TypeParameterType).bound;
 }
@@ -417,9 +375,13 @@
 
   @override
   String get linkedName {
-    if (name != null && name.isNotEmpty) return super.linkedName;
-    return '${nameWithGenerics}(${ParameterRendererHtml(showNames: false).renderLinkedParams(element.parameters).trim()}) → ${returnType.linkedName}';
+    if (_linkedName == null) {
+      _linkedName = CallableElementTypeRendererHtml().renderLinkedName(this);
+    }
+    return _linkedName;
   }
+
+  String get superLinkedName => super.linkedName;
 }
 
 /// This is an anonymous function using the generic function syntax (declared
@@ -435,7 +397,7 @@
   String get linkedName {
     if (_linkedName == null) {
       _linkedName =
-          '${returnType.linkedName} ${super.linkedName}<span class="signature">(${ParameterRendererHtml().renderLinkedParams(element.parameters)})</span>';
+          CallableAnonymousElementTypeRendererHtml().renderLinkedName(this);
     }
     return _linkedName;
   }
diff --git a/lib/src/render/element_type_renderer.dart b/lib/src/render/element_type_renderer.dart
new file mode 100644
index 0000000..458aea6
--- /dev/null
+++ b/lib/src/render/element_type_renderer.dart
@@ -0,0 +1,111 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:dartdoc/dartdoc.dart';
+import 'package:dartdoc/src/render/parameter_renderer.dart';
+
+abstract class ElementTypeRenderer<T extends ElementType> {
+  String renderLinkedName(T elementType);
+
+  String renderNameWithGenerics(T elementType) => '';
+}
+
+class FunctionTypeElementTypeRendererHtml
+    extends ElementTypeRenderer<FunctionTypeElementType> {
+  @override
+  String renderLinkedName(FunctionTypeElementType elementType) {
+    StringBuffer buf = StringBuffer();
+    buf.write('${elementType.returnType.linkedName} ');
+    buf.write('${elementType.nameWithGenerics}');
+    buf.write('<span class="signature">(');
+    buf.write(
+        ParameterRendererHtml().renderLinkedParams(elementType.parameters));
+    buf.write(')</span>');
+    return buf.toString();
+  }
+
+  @override
+  String renderNameWithGenerics(FunctionTypeElementType elementType) {
+    StringBuffer buf = StringBuffer();
+    buf.write(elementType.name);
+    if (elementType.typeFormals.isNotEmpty) {
+      if (!elementType.typeFormals.every((t) => t.name == 'dynamic')) {
+        buf.write('&lt;<wbr><span class="type-parameter">');
+        buf.writeAll(elementType.typeFormals.map((t) => t.name),
+            '</span>, <span class="type-parameter">');
+        buf.write('</span>&gt;');
+      }
+    }
+    return buf.toString();
+  }
+}
+
+class ParameterizedElementTypeRendererHtml
+    extends ElementTypeRenderer<ParameterizedElementType> {
+  @override
+  String renderLinkedName(ParameterizedElementType elementType) {
+    StringBuffer buf = StringBuffer();
+    buf.write(elementType.element.linkedName);
+    if (elementType.typeArguments.isNotEmpty &&
+        !elementType.typeArguments.every((t) => t.name == 'dynamic')) {
+      buf.write('<span class="signature">');
+      buf.write('&lt;<wbr><span class="type-parameter">');
+      buf.writeAll(elementType.typeArguments.map((t) => t.linkedName),
+          '</span>, <span class="type-parameter">');
+      buf.write('</span>&gt;');
+      buf.write('</span>');
+    }
+    return buf.toString();
+  }
+
+  @override
+  String renderNameWithGenerics(ParameterizedElementType elementType) {
+    StringBuffer buf = StringBuffer();
+    buf.write(elementType.element.name);
+    if (elementType.typeArguments.isNotEmpty &&
+        !elementType.typeArguments.every((t) => t.name == 'dynamic')) {
+      buf.write('&lt;<wbr><span class="type-parameter">');
+      buf.writeAll(elementType.typeArguments.map((t) => t.nameWithGenerics),
+          '</span>, <span class="type-parameter">');
+      buf.write('</span>&gt;');
+    }
+    return buf.toString();
+  }
+}
+
+class CallableElementTypeRendererHtml
+    extends ElementTypeRenderer<CallableElementType> {
+  @override
+  String renderLinkedName(CallableElementType elementType) {
+    if (elementType.name != null && elementType.name.isNotEmpty) {
+      return elementType.superLinkedName;
+    }
+
+    StringBuffer buf = StringBuffer();
+    buf.write(elementType.nameWithGenerics);
+    buf.write('(');
+    buf.write(ParameterRendererHtml(showNames: false)
+        .renderLinkedParams(elementType.element.parameters)
+        .trim());
+    buf.write(') → ');
+    buf.write(elementType.returnType.linkedName);
+    return buf.toString();
+  }
+}
+
+class CallableAnonymousElementTypeRendererHtml
+    extends ElementTypeRenderer<CallableAnonymousElementType> {
+  @override
+  String renderLinkedName(CallableAnonymousElementType elementType) {
+    StringBuffer buf = StringBuffer();
+    buf.write(elementType.returnType.linkedName);
+    buf.write(' ');
+    buf.write(elementType.superLinkedName);
+    buf.write('<span class="signature">(');
+    buf.write(ParameterRendererHtml()
+        .renderLinkedParams(elementType.element.parameters));
+    buf.write(')</span>');
+    return buf.toString();
+  }
+}