b/162531354 Extract out redundant OpenApi4j object children iteration functionality

Change-Id: I239cbfc5f2005e457d52c0db2ebc1cb87b020a4a
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidator.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidator.java
index 9a5ab66..32a24a9 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidator.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidator.java
@@ -1,23 +1,21 @@
 package com.apigee.security.oas.extendedvalidator;
 
+import com.google.common.collect.ImmutableList;
 import javax.inject.Inject;
 import org.openapi4j.parser.model.v3.OpenApi3;
 
 final class BaseExtendedValidator implements ExtendedValidator {
 
-  private final TraversalCoordinator traversalCoordinator;
-  private final TraversalCommandFactory traversalCommandFactory;
+  private final TraversalHelperFactory traversalHelperFactory;
 
   @Inject
-  BaseExtendedValidator(
-      TraversalCoordinator traversalCoordinator, TraversalCommandFactory traversalCommandFactory) {
-    this.traversalCoordinator = traversalCoordinator;
-    this.traversalCommandFactory = traversalCommandFactory;
+  BaseExtendedValidator(TraversalHelperFactory traversalHelperFactory) {
+    this.traversalHelperFactory = traversalHelperFactory;
   }
 
   @Override
   public void validate(OpenApi3 openApiSpec) {
-    traversalCoordinator.handleTraversalCommand(traversalCommandFactory.create(openApiSpec));
-    traversalCoordinator.traverse();
+    TraversalHelper traversalHelper = traversalHelperFactory.create(ImmutableList.of());
+    traversalHelper.sendOpenApiTraversal(openApiSpec);
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelper.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelper.java
new file mode 100644
index 0000000..5319099
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelper.java
@@ -0,0 +1,111 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.assistedinject.Assisted;
+import java.util.Map;
+import java.util.Optional;
+import javax.inject.Inject;
+import org.openapi4j.parser.model.OpenApiSchema;
+import org.openapi4j.parser.model.v3.Contact;
+import org.openapi4j.parser.model.v3.ExternalDocs;
+import org.openapi4j.parser.model.v3.Info;
+import org.openapi4j.parser.model.v3.License;
+import org.openapi4j.parser.model.v3.OpenApi3;
+import org.openapi4j.parser.model.v3.Server;
+import org.openapi4j.parser.model.v3.ServerVariable;
+import org.openapi4j.parser.model.v3.Tag;
+
+/**
+ * Safely unpacks various OpenApi4j object's children and creates and queues children {@linkplain
+ * TraversalCommand TraversalCommands} via a {@link TraversalCommandFactory} and a {@link
+ * TraversalCoordinator}.
+ */
+final class BaseTraversalHelper implements TraversalHelper {
+
+  private final TraversalCoordinator coordinator;
+  private final TraversalCommandFactory factory;
+  private final ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+      traversalPath;
+
+  @Inject
+  BaseTraversalHelper(
+      TraversalCoordinator coordinator,
+      TraversalCommandFactory factory,
+      @Assisted
+          ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+              traversalPath) {
+    this.coordinator = coordinator;
+    this.factory = factory;
+    this.traversalPath = traversalPath;
+  }
+
+  @Override
+  public void sendContactTraversal(Contact contact) {
+    Optional.ofNullable(contact)
+        .ifPresent(
+            contactObj ->
+                coordinator.handleTraversalCommand(factory.create(contactObj, traversalPath)));
+  }
+
+  @Override
+  public void sendExternalDocsTraversal(ExternalDocs externalDocs) {
+    Optional.ofNullable(externalDocs)
+        .ifPresent(
+            externalDocsObj ->
+                coordinator.handleTraversalCommand(factory.create(externalDocsObj, traversalPath)));
+  }
+
+  @Override
+  public void sendInfoTraversal(Info info) {
+    Optional.ofNullable(info)
+        .ifPresent(
+            infoObj -> coordinator.handleTraversalCommand(factory.create(infoObj, traversalPath)));
+  }
+
+  @Override
+  public void sendLicenseTraversal(License license) {
+    Optional.ofNullable(license)
+        .ifPresent(
+            licenseObj ->
+                coordinator.handleTraversalCommand(factory.create(licenseObj, traversalPath)));
+  }
+
+  @Override
+  public void sendOpenApiTraversal(OpenApi3 openApiSpec) {
+    Optional.ofNullable(openApiSpec)
+        .ifPresent(openApiObj -> coordinator.handleTraversalCommand(factory.create(openApiObj)));
+  }
+
+  @Override
+  public void sendServerTraversals(ImmutableList<Server> serversList) {
+    Optional.ofNullable(serversList)
+        .ifPresent(
+            servers ->
+                servers.forEach(
+                    server ->
+                        coordinator.handleTraversalCommand(factory.create(server, traversalPath))));
+  }
+
+  @Override
+  public void sendServerVariableTraversals(ImmutableMap<String, ServerVariable> serverVariablesMap) {
+    Optional.ofNullable(serverVariablesMap)
+        .ifPresent(
+            serverVariables -> {
+              serverVariables.forEach(
+                  (name, serverVariable) -> {
+                    coordinator.handleTraversalCommand(
+                        factory.create(serverVariable, name, traversalPath));
+                  });
+            });
+  }
+
+  @Override
+  public void sendTagTraversals(ImmutableList<Tag> tagList) {
+    Optional.ofNullable(tagList)
+        .ifPresent(
+            tags ->
+                tags.forEach(
+                    tag -> coordinator.handleTraversalCommand(factory.create(tag, traversalPath))));
+  }
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversal.java
index da66b71..9c6691f 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ContactTraversal.java
@@ -13,22 +13,16 @@
 
   @Inject
   ContactTraversal(
-      TraversalCoordinator traversalCoordinator,
+      TraversalHelperFactory traversalHelperFactory,
       ExtensionValidationIntegrator extensionValidationIntegrator,
-      TraversalCommandFactory traversalCommandFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Contact openApiSchemaObj) {
-    super(
-        traversalCoordinator,
-        extensionValidationIntegrator,
-        traversalCommandFactory,
-        traversalPath,
-        openApiSchemaObj);
+    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
   }
 
   @Override
   public void traverse() {
-    // TODO(b/161441872): Add call to process extensions.
+    // TODO(b/161441872): Process extensions.
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorModule.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorModule.java
index 7078b7b..f7b0dc0 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorModule.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorModule.java
@@ -1,6 +1,7 @@
 package com.apigee.security.oas.extendedvalidator;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
 
 /**
  * Configures the bindings of inner components to be installed in the {@link
@@ -15,5 +16,10 @@
     bind(ExtensionValidationIntegrator.class).to(BaseExtensionValidationIntegrator.class);
 
     bind(TraversalCoordinator.class).to(BaseTraversalCoordinator.class);
+
+    install(
+        new FactoryModuleBuilder()
+            .implement(TraversalHelper.class, BaseTraversalHelper.class)
+            .build(TraversalHelperFactory.class));
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExternalDocsTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExternalDocsTraversal.java
index c3ff914..47b08ab 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExternalDocsTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ExternalDocsTraversal.java
@@ -14,23 +14,17 @@
 
   @Inject
   ExternalDocsTraversal(
-      TraversalCoordinator traversalCoordinator,
+      TraversalHelperFactory traversalHelperFactory,
       ExtensionValidationIntegrator extensionValidationIntegrator,
-      TraversalCommandFactory traversalCommandFactory,
       @Assisted ExternalDocs openApiSchemaObj,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
               traversalPath) {
-    super(
-        traversalCoordinator,
-        extensionValidationIntegrator,
-        traversalCommandFactory,
-        traversalPath,
-        openApiSchemaObj);
+    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
   }
 
   @Override
   public void traverse() {
-    // TODO(b/161441872): Add call to process extensions.
+    // TODO(b/161441872): Process extensions.
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversal.java
index 23134ba..5d89df2 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/InfoTraversal.java
@@ -13,46 +13,21 @@
 
   @Inject
   InfoTraversal(
-      TraversalCoordinator traversalCoordinator,
+      TraversalHelperFactory traversalHelperFactory,
       ExtensionValidationIntegrator extensionValidationIntegrator,
-      TraversalCommandFactory traversalCommandFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Info openApiSchemaObj) {
-    super(
-        traversalCoordinator,
-        extensionValidationIntegrator,
-        traversalCommandFactory,
-        traversalPath,
-        openApiSchemaObj);
+    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
   }
 
   @Override
   public void traverse() {
     // TODO(b/161441872): Process extensions.
 
-    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath =
-        getUpdatedTraversalPath();
+    TraversalHelper traversalHelper = traversalHelperFactory.create(getUpdatedTraversalPath());
 
-    traverseContact(traversalPath);
-    traverseLicense(traversalPath);
-  }
-
-  private void traverseContact(
-      ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath) {
-    Optional.ofNullable(openApiSchemaObj.getContact())
-        .ifPresent(
-            contact ->
-                traversalCoordinator.handleTraversalCommand(
-                    traversalCommandFactory.create(contact, traversalPath)));
-  }
-
-  private void traverseLicense(
-      ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath) {
-    Optional.ofNullable(openApiSchemaObj.getLicense())
-        .ifPresent(
-            license ->
-                traversalCoordinator.handleTraversalCommand(
-                    traversalCommandFactory.create(license, traversalPath)));
+    traversalHelper.sendContactTraversal(openApiSchemaObj.getContact());
+    traversalHelper.sendLicenseTraversal(openApiSchemaObj.getLicense());
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversal.java
index 1f84a6c..e3b20f0 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/LicenseTraversal.java
@@ -13,22 +13,16 @@
 
   @Inject
   LicenseTraversal(
-      TraversalCoordinator traversalCoordinator,
+      TraversalHelperFactory traversalHelperFactory,
       ExtensionValidationIntegrator extensionValidationIntegrator,
-      TraversalCommandFactory traversalCommandFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted License openApiSchemaObj) {
-    super(
-        traversalCoordinator,
-        extensionValidationIntegrator,
-        traversalCommandFactory,
-        traversalPath,
-        openApiSchemaObj);
+    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
   }
 
   @Override
   public void traverse() {
-    // TODO(b/161441872): Add call to process extensions.
+    // TODO(b/161441872): Process extensions.
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java
index 159acb1..3096ba5 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversal.java
@@ -2,10 +2,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.inject.assistedinject.Assisted;
-import java.util.Map;
-import java.util.Optional;
 import javax.inject.Inject;
-import org.openapi4j.parser.model.OpenApiSchema;
 import org.openapi4j.parser.model.v3.OpenApi3;
 
 /** A {@link Traversal} that traverses an {@link OpenApi3} object. */
@@ -14,68 +11,22 @@
 
   @Inject
   OpenApiSpecificationTraversal(
-      TraversalCoordinator traversalCoordinator,
+      TraversalHelperFactory factory,
       ExtensionValidationIntegrator extensionValidationIntegrator,
-      TraversalCommandFactory traversalCommandFactory,
       @Assisted OpenApi3 openApiSchemaObj) {
-    super(
-        traversalCoordinator,
-        extensionValidationIntegrator,
-        traversalCommandFactory,
-        ImmutableList.of(),
-        openApiSchemaObj);
+    super(factory, extensionValidationIntegrator, ImmutableList.of(), openApiSchemaObj);
   }
 
   @Override
   public void traverse() {
     // TODO(b/161441872): Process extensions.
 
-    ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
-        updatedTraversalPath = getUpdatedTraversalPath();
+    TraversalHelper traversalHelper = traversalHelperFactory.create(getUpdatedTraversalPath());
 
-    traverseInfo(updatedTraversalPath);
-    traverseServers(updatedTraversalPath);
-    traverseTags(updatedTraversalPath);
-    traverseExternalDocs(updatedTraversalPath);
-  }
-
-  private void traverseExternalDocs(
-      ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath) {
-    Optional.ofNullable(openApiSchemaObj.getExternalDocs())
-        .ifPresent(
-            externalDocs ->
-                traversalCoordinator.handleTraversalCommand(
-                    traversalCommandFactory.create(externalDocs, traversalPath)));
-  }
-
-  private void traverseInfo(
-      ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath) {
-    Optional.ofNullable(openApiSchemaObj.getInfo())
-        .ifPresent(
-            info ->
-                traversalCoordinator.handleTraversalCommand(
-                    traversalCommandFactory.create(info, traversalPath)));
-  }
-
-  private void traverseServers(
-      ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath) {
-    Optional.ofNullable(openApiSchemaObj.getServers())
-        .ifPresent(
-            servers ->
-                servers.forEach(
-                    server ->
-                        traversalCoordinator.handleTraversalCommand(
-                            traversalCommandFactory.create(server, traversalPath))));
-  }
-
-  private void traverseTags(
-      ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath) {
-    Optional.ofNullable(openApiSchemaObj.getTags())
-        .ifPresent(
-            tags ->
-                tags.forEach(
-                    tag ->
-                        traversalCoordinator.handleTraversalCommand(
-                            traversalCommandFactory.create(tag, traversalPath))));
+    traversalHelper.sendExternalDocsTraversal(openApiSchemaObj.getExternalDocs());
+    traversalHelper.sendInfoTraversal(openApiSchemaObj.getInfo());
+    traversalHelper.sendServerTraversals(ImmutableList.copyOf(openApiSchemaObj.getServers()));
+    traversalHelper.sendTagTraversals(ImmutableList.copyOf(openApiSchemaObj.getTags()));
+    // TODO(b/162508047): Add path object traversal and callback object traversal logic.
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerTraversal.java
index 0d4f381..c7c3967 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerTraversal.java
@@ -1,6 +1,7 @@
 package com.apigee.security.oas.extendedvalidator;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.inject.assistedinject.Assisted;
 import java.util.Map;
 import java.util.Optional;
@@ -13,37 +14,21 @@
 
   @Inject
   ServerTraversal(
-      TraversalCoordinator traversalCoordinator,
+      TraversalHelperFactory traversalHelperFactory,
       ExtensionValidationIntegrator extensionValidationIntegrator,
-      TraversalCommandFactory traversalCommandFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Server openApiSchemaObj) {
-    super(
-        traversalCoordinator,
-        extensionValidationIntegrator,
-        traversalCommandFactory,
-        traversalPath,
-        openApiSchemaObj);
+    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
   }
 
   @Override
   public void traverse() {
-    // TODO(b/161441872): Add call to process extensions.
+    // TODO(b/161441872): Process extensions.
 
-    traverseServerVariables();
-  }
+    TraversalHelper traversalHelper = traversalHelperFactory.create(getUpdatedTraversalPath());
 
-  private void traverseServerVariables() {
-    Optional.ofNullable(openApiSchemaObj.getVariables())
-        .ifPresent(
-            serverVariables -> {
-              serverVariables.forEach(
-                  (name, serverVariable) -> {
-                    traversalCoordinator.handleTraversalCommand(
-                        traversalCommandFactory.create(
-                            serverVariable, name, getUpdatedTraversalPath()));
-                  });
-            });
+    traversalHelper.sendServerVariableTraversals(
+        ImmutableMap.copyOf(openApiSchemaObj.getVariables()));
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerVariableTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerVariableTraversal.java
index 408e38c..d1284ef 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerVariableTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/ServerVariableTraversal.java
@@ -14,17 +14,15 @@
 
   @Inject
   ServerVariableTraversal(
-      TraversalCoordinator traversalCoordinator,
+      TraversalHelperFactory traversalHelperFactory,
       ExtensionValidationIntegrator extensionValidationIntegrator,
-      TraversalCommandFactory traversalCommandFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted ServerVariable openApiSchemaObj,
       @Assisted String name) {
     super(
-        traversalCoordinator,
+        traversalHelperFactory,
         extensionValidationIntegrator,
-        traversalCommandFactory,
         traversalPath,
         openApiSchemaObj,
         name);
@@ -32,6 +30,6 @@
 
   @Override
   public void traverse() {
-    // TODO(b/161441872): Add call to process extensions.
+    // TODO(b/161441872): Process extensions.
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TagTraversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TagTraversal.java
index 755de96..1300951 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TagTraversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TagTraversal.java
@@ -13,32 +13,19 @@
 
   @Inject
   TagTraversal(
-      TraversalCoordinator traversalCoordinator,
+      TraversalHelperFactory traversalHelperFactory,
       ExtensionValidationIntegrator extensionValidationIntegrator,
-      TraversalCommandFactory traversalCommandFactory,
       @Assisted
           ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       @Assisted Tag openApiSchemaObj) {
-    super(
-        traversalCoordinator,
-        extensionValidationIntegrator,
-        traversalCommandFactory,
-        traversalPath,
-        openApiSchemaObj);
+    super(traversalHelperFactory, extensionValidationIntegrator, traversalPath, openApiSchemaObj);
   }
 
   @Override
   public void traverse() {
-    // TODO(b/161441872): Add call to process extensions.
+    // TODO(b/161441872): Process extensions.
 
-    traverseExternalDocs();
-  }
-
-  private void traverseExternalDocs() {
-    Optional.ofNullable(openApiSchemaObj.getExternalDocs())
-        .ifPresent(
-            externalDocs ->
-                traversalCoordinator.handleTraversalCommand(
-                    traversalCommandFactory.create(externalDocs, getUpdatedTraversalPath())));
+    TraversalHelper traversalHelper = traversalHelperFactory.create(getUpdatedTraversalPath());
+    traversalHelper.sendExternalDocsTraversal(openApiSchemaObj.getExternalDocs());
   }
 }
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Traversal.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Traversal.java
index c9c51d7..37ebd51 100644
--- a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Traversal.java
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/Traversal.java
@@ -11,37 +11,32 @@
 abstract class Traversal<T extends OpenApiSchema> {
 
   protected final T openApiSchemaObj;
-  protected final TraversalCoordinator traversalCoordinator;
   protected final ExtensionValidationIntegrator extensionValidationIntegrator;
-  protected final TraversalCommandFactory traversalCommandFactory;
+  protected final TraversalHelperFactory traversalHelperFactory;
   private final ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
       traversalPath;
   private final Optional<String> name;
 
   Traversal(
-      TraversalCoordinator traversalCoordinator,
+      TraversalHelperFactory traversalHelperFactory,
       ExtensionValidationIntegrator extensionValidationIntegrator,
-      TraversalCommandFactory traversalCommandFactory,
       ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       T openApiSchemaObj) {
-    this.traversalCoordinator = traversalCoordinator;
+    this.traversalHelperFactory = traversalHelperFactory;
     this.extensionValidationIntegrator = extensionValidationIntegrator;
-    this.traversalCommandFactory = traversalCommandFactory;
     this.traversalPath = traversalPath;
     this.openApiSchemaObj = openApiSchemaObj;
     this.name = Optional.empty();
   }
 
   Traversal(
-      TraversalCoordinator traversalCoordinator,
+      TraversalHelperFactory traversalHelperFactory,
       ExtensionValidationIntegrator extensionValidationIntegrator,
-      TraversalCommandFactory traversalCommandFactory,
       ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath,
       T openApiSchemaObj,
       String name) {
-    this.traversalCoordinator = traversalCoordinator;
+    this.traversalHelperFactory = traversalHelperFactory;
     this.extensionValidationIntegrator = extensionValidationIntegrator;
-    this.traversalCommandFactory = traversalCommandFactory;
     this.traversalPath = traversalPath;
     this.openApiSchemaObj = openApiSchemaObj;
     this.name = Optional.of(name);
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalHelper.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalHelper.java
new file mode 100644
index 0000000..1df3e3c
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalHelper.java
@@ -0,0 +1,32 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import org.openapi4j.parser.model.v3.Contact;
+import org.openapi4j.parser.model.v3.ExternalDocs;
+import org.openapi4j.parser.model.v3.Info;
+import org.openapi4j.parser.model.v3.License;
+import org.openapi4j.parser.model.v3.OpenApi3;
+import org.openapi4j.parser.model.v3.Server;
+import org.openapi4j.parser.model.v3.ServerVariable;
+import org.openapi4j.parser.model.v3.Tag;
+
+/** A set of methods to handle the building and sending of {@linkplain Traversal Traversals}. */
+interface TraversalHelper {
+
+  void sendContactTraversal(Contact contact);
+
+  void sendExternalDocsTraversal(ExternalDocs externalDocs);
+
+  void sendInfoTraversal(Info info);
+
+  void sendLicenseTraversal(License license);
+
+  void sendOpenApiTraversal(OpenApi3 openApiSpec);
+
+  void sendServerTraversals(ImmutableList<Server> serversList);
+
+  void sendServerVariableTraversals(ImmutableMap<String, ServerVariable> serverVariables);
+
+  void sendTagTraversals(ImmutableList<Tag> tagList);
+}
diff --git a/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalHelperFactory.java b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalHelperFactory.java
new file mode 100644
index 0000000..f48e621
--- /dev/null
+++ b/oas-core/src/main/java/com/apigee/security/oas/extendedvalidator/TraversalHelperFactory.java
@@ -0,0 +1,15 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Map;
+import java.util.Optional;
+import org.openapi4j.parser.model.OpenApiSchema;
+
+/**
+ * Generates a {@link TraversalHelper} via Guice's {@link
+ * com.google.inject.assistedinject.FactoryModuleBuilder}.
+ */
+interface TraversalHelperFactory {
+  TraversalHelper create(
+      ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>> traversalPath);
+}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidatorTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidatorTest.java
index c03db6e..46871da 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidatorTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseExtendedValidatorTest.java
@@ -1,15 +1,16 @@
 package com.apigee.security.oas.extendedvalidator;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
-import com.google.inject.Guice;
+import com.google.common.collect.ImmutableList;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -23,31 +24,20 @@
 
   @Mock private OpenApi3 openApiSpec;
 
-  @Mock private TraversalCoordinator traversalCoordinator;
-  private BaseExtendedValidator baseExtendedValidator;
+  @Mock private TraversalHelperFactory traversalHelperFactory;
+  @Mock private TraversalHelper traversalHelper;
+  @InjectMocks private BaseExtendedValidator baseExtendedValidator;
 
   /** Sets up an {@link ExtendedValidator} for testing. */
   @Before
   public void setup() {
-    TraversalCommandFactory traversalCommandFactory =
-        Guice.createInjector(new ExtendedValidatorMainModule())
-            .getInstance(TraversalCommandFactory.class);
-
-    baseExtendedValidator =
-        new BaseExtendedValidator(traversalCoordinator, traversalCommandFactory);
+    when(traversalHelperFactory.create(any(ImmutableList.class))).thenReturn(traversalHelper);
   }
 
   @Test
   public void validate_openApiSpec_callsTraversalCoordinatorTraverse() {
     baseExtendedValidator.validate(openApiSpec);
 
-    verify(traversalCoordinator, atLeastOnce()).traverse();
-  }
-
-  @Test
-  public void validate_openApiSpec_sendsTraversalCommandToTraversalCoordinator() {
-    baseExtendedValidator.validate(openApiSpec);
-
-    verify(traversalCoordinator).handleTraversalCommand(any(TraversalCommand.class));
+    verify(traversalHelper).sendOpenApiTraversal(openApiSpec);
   }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelperTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelperTest.java
new file mode 100644
index 0000000..00e2e5d
--- /dev/null
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/BaseTraversalHelperTest.java
@@ -0,0 +1,161 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Guice;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.mockito.quality.Strictness;
+import org.openapi4j.parser.model.v3.Contact;
+import org.openapi4j.parser.model.v3.ExternalDocs;
+import org.openapi4j.parser.model.v3.Info;
+import org.openapi4j.parser.model.v3.License;
+import org.openapi4j.parser.model.v3.OpenApi3;
+import org.openapi4j.parser.model.v3.Server;
+import org.openapi4j.parser.model.v3.ServerVariable;
+import org.openapi4j.parser.model.v3.Tag;
+
+@RunWith(JUnit4.class)
+public class BaseTraversalHelperTest {
+
+  private final Contact contact = new Contact();
+  private final ExternalDocs externalDocs = new ExternalDocs();
+  private final Info info = new Info();
+  private final License license = new License();
+  private final OpenApi3 openApiSpec = new OpenApi3();
+
+  private final ImmutableList<Server> servers =
+      ImmutableList.of(new Server(), new Server(), new Server());
+  private final ImmutableList<Tag> tags =
+      ImmutableList.of(new Tag(), new Tag(), new Tag(), new Tag());
+
+  private final ImmutableMap<String, ServerVariable> serverVariables =
+      ImmutableMap.of(
+          "one", new ServerVariable(), "two", new ServerVariable(), "three", new ServerVariable());
+
+  @Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+  @Mock private TraversalCoordinator traversalCoordinator;
+
+  private BaseTraversalHelper traversalHelper;
+
+  /** Sets up live {@link TraversalHelper} with mocked and live instances for testing. */
+  @Before
+  public void setUp() {
+    TraversalCommandFactory traversalCommandFactory =
+        Guice.createInjector(new ExtendedValidatorMainModule())
+            .getInstance(TraversalCommandFactory.class);
+
+    traversalHelper =
+        new BaseTraversalHelper(traversalCoordinator, traversalCommandFactory, ImmutableList.of());
+  }
+
+  @Test
+  public void sendContactTraversal_sendsContactTraversalCommandToCoordinator() {
+    traversalHelper.sendContactTraversal(contact);
+
+    verify(traversalCoordinator).handleTraversalCommand(any(ContactTraversalCommand.class));
+  }
+
+  @Test
+  public void sendContactTraversals_nullParameter_doesNotFail() {
+    traversalHelper.sendContactTraversal(null);
+  }
+
+  @Test
+  public void sendExternalDocsTraversal_sendsExternalDocsTraversalCommandToCoordinator() {
+    traversalHelper.sendExternalDocsTraversal(externalDocs);
+
+    verify(traversalCoordinator).handleTraversalCommand(any(ExternalDocsTraversalCommand.class));
+  }
+
+  @Test
+  public void sendExternalDocsTraversals_nullParameter_doesNotFail() {
+    traversalHelper.sendExternalDocsTraversal(null);
+  }
+
+  @Test
+  public void sendInfoTraversal_sendsInfoTraversalCommandToCoordinator() {
+    traversalHelper.sendInfoTraversal(info);
+
+    verify(traversalCoordinator).handleTraversalCommand(any(InfoTraversalCommand.class));
+  }
+
+  @Test
+  public void sendInfoTraversals_nullParameter_doesNotFail() {
+    traversalHelper.sendInfoTraversal(null);
+  }
+
+  @Test
+  public void sendLicenseTraversal_sendsLicenseTraversalCommandToCoordinator() {
+    traversalHelper.sendLicenseTraversal(license);
+
+    verify(traversalCoordinator).handleTraversalCommand(any(LicenseTraversalCommand.class));
+  }
+
+  @Test
+  public void sendLicenseTraversals_nullParameter_doesNotFail() {
+    traversalHelper.sendLicenseTraversal(null);
+  }
+
+  @Test
+  public void sendOpenApi_sendsOpenApiSpecificationTraversalCommandsToCoordinator() {
+    traversalHelper.sendOpenApiTraversal(openApiSpec);
+
+    verify(traversalCoordinator)
+        .handleTraversalCommand(any(OpenApiSpecificationTraversalCommand.class));
+  }
+
+  @Test
+  public void sendOpenApi_nullParameter_doesNotFail() {
+    traversalHelper.sendOpenApiTraversal(null);
+  }
+
+  @Test
+  public void sendServerTraversals_sendsServerTraversalCommandsToCoordinator() {
+    traversalHelper.sendServerTraversals(servers);
+
+    verify(traversalCoordinator, times(servers.size()))
+        .handleTraversalCommand(any(ServerTraversalCommand.class));
+  }
+
+  @Test
+  public void sendServerTraversals_nullParameter_doesNotFail() {
+    traversalHelper.sendServerTraversals(null);
+  }
+
+  @Test
+  public void sendServerVariables_sendsServerVariableTraversalCommandsToCoordinator() {
+    traversalHelper.sendServerVariableTraversals(serverVariables);
+
+    verify(traversalCoordinator, times(serverVariables.size()))
+        .handleTraversalCommand(any(ServerVariableTraversalCommand.class));
+  }
+
+  @Test
+  public void sendServerVariables_nullParameter_doesNotFail() {
+    traversalHelper.sendServerVariableTraversals(null);
+  }
+
+  @Test
+  public void sendTagTraversals_sendsTagTraversalCommandsToCoordinator() {
+    traversalHelper.sendTagTraversals(tags);
+
+    verify(traversalCoordinator, times(tags.size()))
+        .handleTraversalCommand(any(TagTraversalCommand.class));
+  }
+
+  @Test
+  public void sendTagTraversals_nullParameter_doesNotFail() {
+    traversalHelper.sendServerTraversals(null);
+  }
+}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModuleTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModuleTest.java
index 70810de..7a7103c 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModuleTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ExtendedValidatorMainModuleTest.java
@@ -57,4 +57,10 @@
     assertThat(catchThrowable(() -> injector.getInstance(JsonSchemaFactory.class)))
         .doesNotThrowAnyException();
   }
+
+  @Test
+  public void getInstance_traversalHelperFactory_shouldNotFail() {
+    assertThat(catchThrowable(() -> injector.getInstance(TraversalHelperFactory.class)))
+        .doesNotThrowAnyException();
+  }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java
index 8f47f1e..bb23e34 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/InfoTraversalTest.java
@@ -1,11 +1,11 @@
 package com.apigee.security.oas.extendedvalidator;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import com.google.common.collect.ImmutableList;
-import com.google.inject.Guice;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -24,56 +24,42 @@
 
   @Rule public final MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
 
-  @Mock private TraversalCoordinator traversalCoordinator;
+  @Mock private TraversalHelperFactory traversalHelperFactory;
+  @Mock private TraversalHelper traversalHelper;
   @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
   @Mock private Info info;
 
+  private final License license = new License();
+  private final Contact contact = new Contact();
+
   private InfoTraversal infoTraversal;
 
   /** Sets up live and mocked instances for testing. */
   @Before
   public void setup() {
-    TraversalCommandFactory traversalCommandFactory =
-        Guice.createInjector(new ExtendedValidatorMainModule())
-            .getInstance(TraversalCommandFactory.class);
-
     infoTraversal =
         new InfoTraversal(
-            traversalCoordinator,
-            extensionValidationIntegrator,
-            traversalCommandFactory,
-            ImmutableList.of(),
-            info);
+            traversalHelperFactory, extensionValidationIntegrator, ImmutableList.of(), info);
 
-    when(info.getContact()).thenReturn(new Contact());
-    when(info.getLicense()).thenReturn(new License());
+    when(info.getContact()).thenReturn(contact);
+    when(info.getLicense()).thenReturn(license);
+
+    when(traversalHelperFactory.create(any(ImmutableList.class))).thenReturn(traversalHelper);
   }
 
   @Test
-  public void traverse_sendsTraversalCoordinatorContactTraversalCommand() {
+  public void traverse_sendsContactToTraversalHelper() {
     infoTraversal.traverse();
 
-    verify(traversalCoordinator).handleTraversalCommand(any(ContactTraversal.class));
+    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
+    verify(traversalHelper).sendContactTraversal(contact);
   }
 
   @Test
-  public void traverse_nullContactChild_doesNotThrowException() {
-    when(info.getContact()).thenReturn(null);
-
-    infoTraversal.traverse();
-  }
-
-  @Test
-  public void traverse_sendsTraversalCoordinatorLicenseTraversalCommand() {
+  public void traverse_sendsLicenseToTraversalHelper() {
     infoTraversal.traverse();
 
-    verify(traversalCoordinator).handleTraversalCommand(any(LicenseTraversal.class));
-  }
-
-  @Test
-  public void traverse_nullLicenseChild_doesNotThrowException() {
-    when(info.getLicense()).thenReturn(null);
-
-    infoTraversal.traverse();
+    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
+    verify(traversalHelper).sendLicenseTraversal(license);
   }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java
index 2ed7df8..7451a2f 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/OpenApiSpecificationTraversalTest.java
@@ -1,13 +1,11 @@
 package com.apigee.security.oas.extendedvalidator;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import com.google.common.collect.ImmutableList;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -28,95 +26,67 @@
 
   @Rule public final MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
 
-  @Mock private TraversalCoordinator traversalCoordinator;
+  @Mock private TraversalHelperFactory traversalHelperFactory;
+  @Mock private TraversalHelper traversalHelper;
   @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
   @Mock private OpenApi3 openApiSpec;
 
+  private final Info info = new Info();
+  private final ExternalDocs externalDocs = new ExternalDocs();
+
+  private final ImmutableList<Server> servers =
+      ImmutableList.of(new Server(), new Server(), new Server());
+  private final ImmutableList<Tag> tags = ImmutableList.of(new Tag(), new Tag(), new Tag());
+
   private OpenApiSpecificationTraversal openApiSpecificationTraversal;
-  private ImmutableList<Server> servers;
-  private ImmutableList<Tag> tags;
 
   /** Sets up live and mocked instances for testing. */
   @Before
   public void setup() {
-    Injector injector = Guice.createInjector(new ExtendedValidatorMainModule());
-
-    TraversalCommandFactory traversalCommandFactory =
-        injector.getInstance(TraversalCommandFactory.class);
-
     openApiSpecificationTraversal =
         new OpenApiSpecificationTraversal(
-            traversalCoordinator,
-            extensionValidationIntegrator,
-            traversalCommandFactory,
-            openApiSpec);
+            traversalHelperFactory, extensionValidationIntegrator, openApiSpec);
 
-    servers = ImmutableList.of(new Server(), new Server(), new Server());
     when(openApiSpec.getServers()).thenReturn(servers);
 
-    tags = ImmutableList.of(new Tag(), new Tag(), new Tag());
     when(openApiSpec.getTags()).thenReturn(tags);
 
-    when(openApiSpec.getInfo()).thenReturn(new Info());
+    when(openApiSpec.getInfo()).thenReturn(info);
 
-    when(openApiSpec.getExternalDocs()).thenReturn(new ExternalDocs());
+    when(openApiSpec.getExternalDocs()).thenReturn(externalDocs);
+
+    when(traversalHelperFactory.create(any(ImmutableList.class))).thenReturn(traversalHelper);
   }
 
   @Test
-  public void traverse_sendsInfoChildTraversalToTraversalCoordinator() {
+  public void traverse_sendsInfoToTraversalHelper() {
     openApiSpecificationTraversal.traverse();
 
-    verify(traversalCoordinator).handleTraversalCommand(any(InfoTraversal.class));
+    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
+    verify(traversalHelper).sendInfoTraversal(info);
   }
 
   @Test
-  public void traverse_nullInfoChild_doesNotThrowException() {
-    when(openApiSpec.getInfo()).thenReturn(null);
-
+  public void traverse_sendsExternalDocsToTraversalHelper() {
     openApiSpecificationTraversal.traverse();
+
+    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
+    verify(traversalHelper).sendExternalDocsTraversal(externalDocs);
   }
 
   @Test
-  public void traverse_sendsExternalDocsChildTraversalToTraversalCoordinator() {
+  public void traverse_sendsServersToTraversalHelper() {
     openApiSpecificationTraversal.traverse();
 
-    verify(traversalCoordinator).handleTraversalCommand(any(ExternalDocsTraversalCommand.class));
+    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
+    verify(traversalHelper).sendServerTraversals(servers);
   }
 
   @Test
-  public void traverse_nullExternalDocsChild_doesNotThrowException() {
-    when(openApiSpec.getExternalDocs()).thenReturn(null);
-
-    openApiSpecificationTraversal.traverse();
-  }
-
-  @Test
-  public void traverse_sendsServerChildrenTraversalsToTraversalCommander() {
+  public void traverse_sendsTagsToTraversalHelper() {
     openApiSpecificationTraversal.traverse();
 
-    verify(traversalCoordinator, times(servers.size()))
-        .handleTraversalCommand(any(ServerTraversalCommand.class));
-  }
-
-  @Test
-  public void traverse_nullServerChildren_doesNotThrowException() {
-    when(openApiSpec.getServers()).thenReturn(null);
-
-    openApiSpecificationTraversal.traverse();
-  }
-
-  @Test
-  public void traverse_sendsTagChildrenTraversalsToTraversalCommander() {
-    openApiSpecificationTraversal.traverse();
-
-    verify(traversalCoordinator, times(tags.size()))
-        .handleTraversalCommand(any(TagTraversalCommand.class));
-  }
-
-  @Test
-  public void traverse_nullTagChildren_doesNotThrowException() {
-    when(openApiSpec.getTags()).thenReturn(null);
-
-    openApiSpecificationTraversal.traverse();
+    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
+    verify(traversalHelper).sendTagTraversals(tags);
   }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ServerTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ServerTraversalTest.java
index c031d72..24294a8 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ServerTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/ServerTraversalTest.java
@@ -1,13 +1,12 @@
 package com.apigee.security.oas.extendedvalidator;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.inject.Guice;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -25,7 +24,8 @@
 
   @Rule public final MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
 
-  @Mock private TraversalCoordinator traversalCoordinator;
+  @Mock private TraversalHelperFactory traversalHelperFactory;
+  @Mock private TraversalHelper traversalHelper;
   @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
   @Mock private Server server;
   @Mock private ServerVariable serverVariable;
@@ -36,39 +36,26 @@
   /** Sets up live and mocked instances for testing. */
   @Before
   public void setup() {
-    TraversalCommandFactory traversalCommandFactory =
-        Guice.createInjector(new ExtendedValidatorMainModule())
-            .getInstance(TraversalCommandFactory.class);
-
     serverTraversal =
         new ServerTraversal(
-            traversalCoordinator,
-            extensionValidationIntegrator,
-            traversalCommandFactory,
-            ImmutableList.of(),
-            server);
+            traversalHelperFactory, extensionValidationIntegrator, ImmutableList.of(), server);
 
     serverVariables =
         ImmutableMap.of(
             "serverVariableOne", serverVariable,
             "ServerVariableTwo", serverVariable,
             "ServerVariableThree", serverVariable);
+
+    when(server.getVariables()).thenReturn(serverVariables);
+
+    when(traversalHelperFactory.create(any(ImmutableList.class))).thenReturn(traversalHelper);
   }
 
   @Test
   public void traverse_sendsServerVariableChildrenTraversalsToTraversalCommander() {
-    when(server.getVariables()).thenReturn(serverVariables);
-
     serverTraversal.traverse();
 
-    verify(traversalCoordinator, times(serverVariables.size()))
-        .handleTraversalCommand(any(ServerVariableTraversal.class));
-  }
-
-  @Test
-  public void traverse_nullServerVariableChildren_doesNotFail() {
-    when(server.getVariables()).thenReturn(null);
-
-    serverTraversal.traverse();
+    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
+    verify(traversalHelper).sendServerVariableTraversals(ImmutableMap.copyOf(serverVariables));
   }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TagTraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TagTraversalTest.java
index 9c5a6ee..3e57ee7 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TagTraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TagTraversalTest.java
@@ -1,11 +1,11 @@
 package com.apigee.security.oas.extendedvalidator;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import com.google.common.collect.ImmutableList;
-import com.google.inject.Guice;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -23,41 +23,31 @@
 
   @Rule public final MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
 
-  @Mock private TraversalCoordinator traversalCoordinator;
+  @Mock private TraversalHelperFactory traversalHelperFactory;
+  @Mock private TraversalHelper traversalHelper;
   @Mock private ExtensionValidationIntegrator extensionValidationIntegrator;
   @Mock private Tag tag;
 
+  private final ExternalDocs externalDocs = new ExternalDocs();
+
   private TagTraversal tagTraversal;
 
   /** Sets up live and mocked instances for testing. */
   @Before
   public void setup() {
-    TraversalCommandFactory traversalCommandFactory =
-        Guice.createInjector(new ExtendedValidatorMainModule())
-            .getInstance(TraversalCommandFactory.class);
-
     tagTraversal =
         new TagTraversal(
-            traversalCoordinator,
-            extensionValidationIntegrator,
-            traversalCommandFactory,
-            ImmutableList.of(),
-            tag);
+            traversalHelperFactory, extensionValidationIntegrator, ImmutableList.of(), tag);
+
+    when(tag.getExternalDocs()).thenReturn(externalDocs);
+    when(traversalHelperFactory.create(any(ImmutableList.class))).thenReturn(traversalHelper);
   }
 
   @Test
   public void traverse_sendsTraversalCommanderExternalDocsTraversalCommand() {
-    when(tag.getExternalDocs()).thenReturn(new ExternalDocs());
-
     tagTraversal.traverse();
 
-    verify(traversalCoordinator).handleTraversalCommand(any(ExternalDocsTraversal.class));
-  }
-
-  @Test
-  public void traverse_nullExternalDocChild_doesNotFail() {
-    when(tag.getExternalDocs()).thenReturn(null);
-
-    tagTraversal.traverse();
+    verify(traversalHelperFactory, atLeastOnce()).create(any(ImmutableList.class));
+    verify(traversalHelper).sendExternalDocsTraversal(externalDocs);
   }
 }
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactoryTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactoryTest.java
index 585ee28..9eab5e8 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactoryTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalCommandFactoryTest.java
@@ -23,10 +23,10 @@
 @RunWith(JUnit4.class)
 public class TraversalCommandFactoryTest {
 
-  private static final TraversalCommandFactory traversalCommandFactory =
+  private final TraversalCommandFactory traversalCommandFactory =
       Guice.createInjector(new ExtendedValidatorMainModule())
           .getInstance(TraversalCommandFactory.class);
-  private static final ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
+  private final ImmutableList<Map.Entry<Class<? extends OpenApiSchema>, Optional<String>>>
       traversalPath = ImmutableList.copyOf(new ArrayDeque<>());
 
   @Test
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalHelperFactoryTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalHelperFactoryTest.java
new file mode 100644
index 0000000..3770534
--- /dev/null
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalHelperFactoryTest.java
@@ -0,0 +1,23 @@
+package com.apigee.security.oas.extendedvalidator;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Guice;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class TraversalHelperFactoryTest {
+
+  private final TraversalHelperFactory traversalHelperFactory =
+      Guice.createInjector(new ExtendedValidatorMainModule())
+          .getInstance(TraversalHelperFactory.class);
+
+  @Test
+  public void create_returnsBaseTraversalHelper() {
+    assertThat(traversalHelperFactory.create(ImmutableList.of()))
+        .isInstanceOf(BaseTraversalHelper.class);
+  }
+}
diff --git a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalTest.java b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalTest.java
index c33087f..e99a7d3 100644
--- a/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalTest.java
+++ b/oas-core/src/test/java/com/apigee/security/oas/extendedvalidator/TraversalTest.java
@@ -22,9 +22,8 @@
 
   @Rule public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
 
-  @Mock private TraversalCoordinator baseTraversalCoordinator;
+  @Mock private TraversalHelperFactory traversalHelperFactory;
   @Mock private ExtensionValidationIntegrator baseExtensionValidationIntegrator;
-  @Mock private TraversalCommandFactory traversalCommandFactory;
 
   @Test
   public void instantiate_traversal_addsTraversalsOpenApiObjClassToTraversalPath() {
@@ -33,9 +32,8 @@
         ImmutableList.copyOf(new ArrayDeque<>());
     InfoTraversal traversal =
         new InfoTraversal(
-            baseTraversalCoordinator,
+            traversalHelperFactory,
             baseExtensionValidationIntegrator,
-            traversalCommandFactory,
             prevTraversalPath,
             new Info());