简体   繁体   中英

Avoiding code duplication for methods with different return types

I have two builder classes that return a Tree and a Set respectively. The execution of the second builder is just the execution of the first builder with an extra step and a different return type. I really don't want to duplicate the same steps in two different classes, because if there is a change in the Tree -building process, I have to change code in two classes. Here are the two builders:

public class TreeBuilder {

    private NodeHelper nodeHelper;

    public TreeBuilder(NodeHelper nodeHelper) {
        this.nodeHelper = nodeHelper;
    }

    public Tree<EquipmentDetails> buildTree() {

        Tree<EquipmentDetails> tree = new Tree<>(nodeHelper.getEquipmentDetails(nodeHelper.getModelName()));

        return buildTree(tree);
    }

    private Tree<EquipmentDetails> buildTree(Tree<EquipmentDetails> parentTree) {

        Map<String, NativeSlotDisplayLabel> childSubTypeMap = nodeHelper.getChildSubTypeMap(parentTree);

        if (CommonUtils.nullOrEmpty(childSubTypeMap))
            return parentTree;

        for (String holderCommonName : childSubTypeMap.keySet()) {

            NativeSlotDisplayLabel nativeSlotDisplayLabel = childSubTypeMap.get(holderCommonName);

            EquipmentDetails equipmentDetails = nodeHelper.constructEqptDetails(holderCommonName, nativeSlotDisplayLabel, parentTree);

            Tree<EquipmentDetails> childTree = new Tree<>(equipmentDetails);

            parentTree.addChild(childTree);

            String equipmentCommonName = equipmentDetails.getModelEquipType();

            if (equipmentCommonName == null)
                continue;

            buildTree(childTree);
        }

        return parentTree;
    }
}

The second builder uses one more dependency and returns a Set instead of a Tree :

public class FreePortsBuilder {

    private NodeHelper nodeHelper;

    private PortLabelGenerator portLabelGenerator;

    public FreePortsBuilder(NodeHelper nodeHelper, PortLabelGenerator portLabelGenerator) {
        this.nodeHelper = nodeHelper;
        this.portLabelGenerator = portLabelGenerator;
    }

    public Set<PortNameAndLabel> collectFreePorts(){

        Tree<EquipmentDetails> tree = new Tree<>(nodeHelper.getEquipmentDetails(nodeHelper.getModelName()));

        Set<PortNameAndLabel> portNameAndLabels = new HashSet<>();

        return collectFreePorts(tree, portNameAndLabels);
    }

    private Set<PortNameAndLabel> collectFreePorts(Tree<EquipmentDetails> parentTree, Set<PortNameAndLabel> portNameAndLabels) {

        Map<String, NativeSlotDisplayLabel> childSubTypeMap = nodeHelper.getChildSubTypeMap(parentTree);

        if (CommonUtils.nullOrEmpty(childSubTypeMap))
            return portNameAndLabels;

        Set<PortNameAndLabel> generatedPortNameAndLabel = portLabelGenerator.getPortNameAndLabel(childSubTypeMap, parentTree.getUserObject());

        portNameAndLabels.addAll(generatedPortNameAndLabel);

        for (String holderCommonName : childSubTypeMap.keySet()) {

            NativeSlotDisplayLabel nativeSlotDisplayLabel = childSubTypeMap.get(holderCommonName);

            EquipmentDetails equipmentDetails = nodeHelper.constructEqptDetails(holderCommonName, nativeSlotDisplayLabel, parentTree);

            Tree<EquipmentDetails> childTree = new Tree<>(equipmentDetails);

            parentTree.addChild(childTree);

            String equipmentCommonName = equipmentDetails.getModelEquipType();

            if (equipmentCommonName == null)
                continue;

            collectFreePorts(childTree, portNameAndLabels);
        }

        return portNameAndLabels;
    }
}

I am looking for a way to avoid the duplication. Any suggestions?

FreePortsBuilder is doing two things at the same time:

  1. Building a Tree : as you know, this is identical to what TreeBuilder does.
  2. Building a Set<PortNameAndLabel> , where each PortNameAndLabel appears to be associated with a node or child (which is itself a Tree ) in the final Tree .

Instead of doing the above two things simultaneously, do them sequentially. That is, in FreePortsBuilder , first use TreeBuilder to build the Tree , then traverse this Tree to construct the Set<PortNameAndLabel> . One way to do that is the following (I assume that your Tree class has a method like getChildren() that returns a List<Tree<EquipmentDetails>> ).

First, allow a client to obtain a TreeBuilder 's NodeHelper by adding a getter:

public class TreeBuilder {
  private NodeHelper nodeHelper;

  public NodeHelper getNodeHelper() {
    return this.nodeHelper;
  }

  ...
}

Then, inject a TreeBuilder instead of a NodeHelper into FreePortsBuilder ( FreePortsBuilder can use the NodeHelper that is in the TreeBuilder , because of the getter that we just added):

public class FreePortsBuilder {
  private TreeBuilder treeBuilder;
  private PortLabelGenerator portLabelGenerator;

  public FreePortsBuilder(TreeBuilder treeBuilder, PortLabelGenerator portLabelGenerator) {
    this.treeBuilder = treeBuilder;
    this.portLabelGenerator = portLabelGenerator;
  }

  public Set<PortNameAndLabel> collectFreePorts() {
    Tree<EquipmentDetails> tree = treeBuilder.buildTree();
    return collectFreePorts(tree);
  }

  private Set<PortNameAndLabel> collectFreePorts(Tree<EquipmentDetails> tree) {
    Set<PortNameAndLabel> portNameAndLabels = new HashSet<>();

    for (Tree<EquipmentDetails> child : tree.getChildren()) {
      Map<String, NativeSlotDisplayLabel> childSubTypeMap =
          treeBuilder.getNodeHelper().getChildSubTypeMap(child);

      if (CommonUtils.nullOrEmpty(childSubTypeMap))
        continue;

      Set<PortNameAndLabel> generatedPortNameAndLabel =
          portLabelGenerator.getPortNameAndLabel(childSubTypeMap, child.getUserObject());

      portNameAndLabels.addAll(generatedPortNameAndLabel);
    }

    return portNameAndLabels;
  }
}

Now, FreePortsBuilder can truly make use of the TreeBuilder implementation, and the code duplication is removed.

You can use Strategy design pattern or Template Method here. You have an algorithm here where only certain steps change (collectFreePorts & buildTree).

If you cannot use inheritance, you can use a similar solution to this: https://softwareengineering.stackexchange.com/questions/187872/template-method-within-one-class-without-subclasses-or-inheritance

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM