views:

152

answers:

4

So I have this stinky method, the two conditional blocks do almost the exact same thing, but with radically different parameters (at least in my eyes). I want to clean it Uncle Bob style, but can't for the life of me figure out a tidy way of doing it. So I come to you, my fabulous nerd friends, to see how you might distill this down to something that doesn't make one want to gouge out their eyes. The code is AS3, but that doesn't really make a difference in my opinion.

/**
 * Splits this group into two groups based on the intersection of the group
 * with another group. The group is split in a direction to fill empty
 * cells left by the splitting group.
 *
 * @param onGroup
 * @param directionToMoveSplitCells
 * @return
 *
 */
public function split(onGroup:CellGroup, directionToMoveSplitCells:String):CellGroup
{
 if (!hasIntersection(onGroup))
  return this;
 var numCellsToSplit:int = 0;
 var splitCells:Array;
 var newGroup:CellGroup;
 var numberOfCellsToSplit:int;
 var splitStartIndex:int;
 var resultingGroupStartIndex:int;

 if (directionToMoveSplitCells == "RIGHT")
 {
  numberOfCellsToSplit = endIndex - onGroup.startIndex + 1;
  splitStartIndex = length - numberOfCellsToSplit;
  splitCells = trimCells(splitStartIndex, numberOfCellsToSplit);
  resultingGroupStartIndex = onGroup.endIndex + 1;

  if (splitCells.length > 0)
  {
   newGroup = row.createGroup(splitCells, resultingGroupStartIndex)
   newGroup.nextGroup = nextGroup;
   if (newGroup.nextGroup)
    newGroup.nextGroup.previousGroup = newGroup;
   newGroup.previousGroup = this;
   nextGroup = newGroup;

  }
 }
 else
 {
  numberOfCellsToSplit = onGroup.endIndex - startIndex + 1;
  splitStartIndex = 0;
  splitCells = trimCells(splitStartIndex, numberOfCellsToSplit);
  resultingGroupStartIndex = onGroup.startIndex - splitCells.length;

  if (splitCells.length > 0)
  {
   newGroup = row.createGroup(splitCells, resultingGroupStartIndex)
   newGroup.previousGroup = previousGroup;
   if (newGroup.previousGroup)
    newGroup.previousGroup.nextGroup = newGroup
   previousGroup = newGroup;
   newGroup.nextGroup = this;
   var newX:int = (onGroup.endIndex + 1) * cellSize.width;
   x = newX;
  }

 }

 removeArrayOfCellsFromGroup(splitCells);
 row.joinGroups();
 row.updateGroupIndices();
 repositionCellsInGroup();

 return newGroup;
}
A: 
var groupThatWillReceiveCells = this;
var groupThatWontReceiveCells = onGroup;

if (directionToMoveSplitCells == "RIGHT")
{
    groupThatWillReceiveCells = onGroup;
    groupThatWontReceiveCells = this;
}

Rename stuff as needed.

Anon.
+1  A: 
/**
 * Splits this group into two groups based on the intersection of the group
 * with another group. The group is split in a direction to fill empty
 * cells left by the splitting group.
 *
 * @param onGroup
 * @param directionToMoveSplitCells
 * @return
 *
 */
public function split(onGroup:CellGroup, directionToMoveSplitCells:String):CellGroup
{
        if(!hasIntersection(onGroup)) return this;
        var numCellsToSplit:int = 0;
        var splitCells:Array;
        var newGroup:CellGroup;
        var numberOfCellsToSplit:int;
        var splitStartIndex:int;
        var resultingGroupStartIndex:int;

  numberOfCellsToSplit = (directionToMoveSplitCells == "RIGHT" ? (endIndex - onGroup.startIndex) : (onGroup.endIndex - startIndex)) + 1;
  splitStartIndex = directionToMoveSplitCells == "RIGHT" ? (length - numberOfCellsToSplit) : 0;
  splitCells = trimCells(splitStartIndex, numberOfCellsToSplit);
  resultingGroupStartIndex = directionToMoveSplitCells == "RIGHT" ? (onGroup.startIndex - splitCells.length) : (onGroup.endIndex + 1);

  if (splitCells.length > 0)
        {
                newGroup = row.createGroup(splitCells, resultingGroupStartIndex)
                newGroup.nextGroup = nextGroup; //not sure how to not set this from jump
                newGroup.previousGroup = previousGroup; //not sure how to not set this from jump
                if (newGroup.previousGroup){
     newGroup.previousGroup.nextGroup = newGroup;
     previousGroup = newGroup;
     var newX:int = (onGroup.endIndex + 1) * cellSize.width;
                 x = newX;
    }
                if (newGroup.nextGroup) newGroup.nextGroup.previousGroup = newGroup;
    else{
     newGroup.nextGroup = this;
     newGroup.previousGroup = this;
                 nextGroup = newGroup;
    }
        }

        removeArrayOfCellsFromGroup(splitCells);
        row.joinGroups();
        row.updateGroupIndices();
        repositionCellsInGroup();

        return newGroup;
}
johncblandii
Granted, this was a poor attempt at merely lessening the lines but meh. :-)Man, Stack jacked up that code. My bad.
johncblandii
Code is fixed now. It looks much better.
johncblandii
This seems unnecessary. How many times do you end up testing the direction? And how many times do you really need to?
Anon.
It is totally unnecessary. :-)
johncblandii
BTW, did you not read my first comment?
johncblandii
+1  A: 

This is all that occurs to me. The linked list is really a seperate detail... so maybe it can be refactored out....

    public function split(onGroup:CellGroup, directionToMoveSplitCells:String):CellGroup
{
        if (!hasIntersection(onGroup))
                return this;
        valr splitCells:Array;
        var newGroup:CellGroup ;
        var numberOfCellsToSplit:int;
        var splitStartIndex:int;
        var resultingGroupStartIndex:int;

        if (directionToMoveSplitCells == "RIGHT")
        {
                numberOfCellsToSplit = this.endIndex - onGroup.startIndex + 1;
                splitStartIndex = this.length - numberOfCellsToSplit;
        splitCells = trimCells(splitStartIndex, numberOfCellsToSplit);
                resultingGroupStartIndex = onGroup.endIndex + 1;

                if (splitCells.length > 0)
                {
                        newGroup = row.createGroup(splitCells, resultingGroupStartIndex);
                  nextGroup=insertGroup(newGroup,this,nextGroup);
                }
        }
        else
        {
                numberOfCellsToSplit = onGroup.endIndex - startIndex + 1;
                splitStartIndex = 0;
        splitCells = trimCells(splitStartIndex, numberOfCellsToSplit);
                resultingGroupStartIndex = onGroup.startIndex - splitCells.length;

                if (splitCells.length > 0)
                {
                        newGroup = row.createGroup(splitCells, resultingGroupStartIndex);
                        previousGroup=insertGroup(newGroup,previousGroup,this);
                        var newX:int = (onGroup.endIndex + 1) * cellSize.width;
                        x = newX;
                }

        }

        removeArrayOfCellsFromGroup(splitCells);
        row.joinGroups();
        row.updateGroupIndices();
        repositionCellsInGroup();

        return newGroup;
}

private function insertGroup(toInsert:CellGroup,prior:CellGroup,next:CellGroup):CellGroup
{
    toInsert.nextGroup = next;
    toInsert.previousGroup = prior;
    if (toInsert.nextGroup )
            toInsert.nextGroup.previousGroup = toInsert;
    if (toInsert.previousGroup )
        toInsert.previousGroup.nextGroup = toInsert;
    return toInsert;
}

My unindenting of splitCells assigment is to indicate that it is the inoly non-conditional line in the block. I looked at doing what Anon proposed but I can't see any way to make the code actually better that way.

Tim Williscroft
+1  A: 
public function split(onGroup:CellGroup, directionToMoveSplitCells:String):CellGroup
{
 if (!hasIntersection(onGroup))
  return this;

 var newGroup:CellGroup;

    if (directionToMoveSplitCells == "RIGHT")
    {
  newGroup = splitGroupAndMoveSplitCellsRight(onGroup);
  if (!newGroup)
   return;

  insertAsNextGroupInLinkedList(newGroup);
    }
    else
    {
  newGroup = splitGroupAndMoveSplitCellsLeft(onGroup);
  if (!newGroup)
   return;

  insertAsPreviousGroupInLinkedList(newGroup);

  x = (onGroup.endIndex + 1) * cellSize.width;
 }

    removeArrayOfCellsFromGroup(splitCells);
    row.joinGroups();
    row.updateGroupIndices();
    repositionCellsInGroup();

    return newGroup;
}


private function splitGroupAndMoveSplitCellsRight(onGroup:CellGroup):CellGroup
{
    var numCellsToSplit:int = endIndex - onGroup.startIndex + 1;
    var splitStartIndex:int = length - numberOfCellsToSplit;

    var splitCells:Array = trimCells(splitStartIndex, numberOfCellsToSplit);
    if (!splitCells.length)
     return null;

    var resultingGroupStartIndex:int = onGroup.endIndex + 1;

    return row.createGroup(splitCells, resultingGroupStartIndex);
}

private function splitGroupAndMoveSplitCellsLeft(onGroup:CellGroup):CellGroup
{
    var numCellsToSplit:int = onGroup.endIndex - startIndex + 1;
    var splitStartIndex:int = 0;

    var splitCells:Array = trimCells(splitStartIndex, numberOfCellsToSplit);
    if (!splitCells.length)
     return null;

    var resultingGroupStartIndex:int = onGroup.startIndex - splitCells.length;

    return row.createGroup(splitCells, resultingGroupStartIndex);
}

private function insertAsNextGroupInLinkedList(group:CellGroup):void
{
    var currentNextGroup:CellGroup = nextGroup;
    if (currentNextGroup)
    {
     group.nextGroup = currentNextGroup;
     currentNextGroup.previousGroup = group;
    }

    group.previousGroup = this;
    nextGroup = group;
}

private function insertAsPreviousGroupInLinkedList(group:CellGroup):void
{
    var currentPreviousGroup:CellGroup = previousGroup;
    if (currentPreviousGroup)
    {
     group.previousGroup = currentPreviousGroup;
     currentPreviousGroup.nextGroup = group;
    }

    group.nextGroup = this;
    previousGroup = group;
}
alecmce
A slightly masochistic excercise, since you rather have to assume the functionality of other methods in the class! It's well suited to 4am my time, since I can't sleep :)Since you mentioned Uncle Bob, I tried to keep to the single-responsibility principle. I see since I started to edit my response someone else has pointed out factoring out linked list management code - absolutely.Also, I split the logic into moving left and moving right, but if the reflexivity that Anon suggested works, then this can be made much neater.Hope it helps.
alecmce
I was going to do this, but the methods are still pretty much the same code, which is annoying, but perhaps unavoidable in this case.
Joel Hooks