2014-02-07 45 views
1

这主要是一个重构问题。使用LinkedList重构类似的方法

我创造了一些方法,通过一个动作的履历根据其ID/PreviousId关系去前进/后退(见下基本类):

public class Action 
{ 
    public int Id { get; set; } 
    public int PreviousId { get; set; } 
    public string Title { get; set; } 
} 

背景资料:

我开始通过从数据库中获取单个动作来关闭。如果用户选择“GoBack”,我需要从数据库中获取前一个操作并将其存储在LinkedList中。这意味着用户可能会重新访问相同的操作(即通过再次前进),而是通过从LinkedList版本中调用它,而不是再次从数据库中获取它。我不想首先从数据库中检索所有操作。我有这个功能工作,但我的GoBack()和GoForward()方法几乎完全相同。

我希望看看是否有一种很好的方式将此重构为更通用的方法集而不是重复代码? (注意 - 我的代码不包含数据库调用来减少阅读,所以我将dummy数据放入List中充当我的数据库)。

类级变量,我引用的方法:

//The list I'm using to pretend to be my database containing actions 
    private List<Action> _actions { get; set; } 

    private Action _currentAction { get; set; } 
    private LinkedList<Action> _actionLinks { get; set; } 

这里是我的GoBack()方法:

private void GoBack() 
    { 
     var current = _actionLinks.Find(_currentAction); 

     if (current == null) 
      return; 

     //If we've already stored the previous action. Just point to it 
     if (current.Previous != null) 
     { 
      _currentAction = current.Previous.Value; 
      return; 
     } 

     //We don't have this action stored so go get it from the database and cache it in the list 
     var previousAction = _actions.FirstOrDefault(i => i.Id == _currentAction.PreviousId); 

     //There are no previous actions 
     if(previousAction == null) 
      return; 

     _actionLinks.AddBefore(current, previousAction); 

     //Now reset the current action 
     _currentAction = previousAction; 
    } 

这里是我的GoForward()方法:

private void GoForward() 
    { 
     var current = _actionLinks.Find(_currentAction); 

     if (current == null) 
      return; 

     //If we've already stored the next action. Just point to it 
     if (current.Next != null) 
     { 
      _currentAction = current.Next.Value; 
      return; 
     } 

     //We don't have this action stored so go get it from the database and cache it in the list 
     var nextAction = _actions.FirstOrDefault(i => i.PreviousId == _currentAction.Id); 

     //There are no further actions 
     if (nextAction == null) 
      return; 

     _actionLinks.AddAfter(current, nextAction); 

     //Now reset the current action 
     _currentAction = nextAction; 
    } 

如果你想编译代码。我在我的构造和BuildData方法加我使用测试:

构造:

public LinkListTest() 
    { 
     _actionLinks = new LinkedList<Action>(); 
     _actions = new List<Action>(); 
     BuildData(); 

     //Just set current to the latest action id 
     _currentAction = _actions.First(i => i.Id == 6); 

     //Add it to the linkedlist 
     _actionLinks.AddFirst(_currentAction); 

     //Start navigating as a user would 
     GoBack(); 
     GoBack(); 
     GoForward(); 
     GoBack(); 
     GoForward(); 
     GoBack(); 
     GoBack(); 
    } 

BuildData方法:

private void BuildData() 
    { 
     for (int i = 6; i >= 0; i--) 
     { 
      var action = new Action(); 
      action.Id = i; 
      if (i != 0) 
       action.PreviousId = i - 1; 
      else 
       action.PreviousId = -1; 

      action.Title = string.Format("Action {0}", i); 

      _actions.Add(action); 
     } 
    } 

提前感谢!

回答

1

解除复制某些逻辑的一种方法是使用访问者模式。

using ActionListAction = System.Action<System.Collections.Generic.LinkedList<Package.Action>, System.Collections.Generic.LinkedListNode<Package.Action> ,Package.Action>; 

    ... 

    private void GoBack() 
    { 
     Move(new BackwordVisitor()); 
    } 

    private void GoForward() 
    { 
     Move(new ForwardVisitor()); 
    } 

    private void Move(DirectionVisitor direction) 
    { 
     var current = _actionLinks.Find(_currentAction); 

     if (current == null) 
      return; 

     var node = direction.Pointer(current); 
     if (node != null) 
     { 
      _currentAction = node.Value; 
      return; 
     } 

     var action = _actions.FirstOrDefault(i => direction.NextSelector(i, _currentAction)); 

     //There are no further actions 
     if (action == null) 
      return; 

     direction.Add(_actionLinks, current, action); 

     _currentAction = action;   
    } 

    private abstract class DirectionVisitor 
    { 
     public Func<LinkedListNode<Action>, LinkedListNode<Action>> Pointer { protected set; get; } 
     public Func<Action, Action, bool> NextSelector { protected set; get; } 
     public ActionListAction Add { protected set; get; } 
    } 

    private class ForwardVisitor : DirectionVisitor 
    { 
     public Forward() 
     { 
      Pointer = n => n.Next; 
      NextSelector = (action, current) => action.PreviousId == current.Id; 
      Add = (list, current, node) => list.AddAfter(current, node); 
     } 
    } 

    private class BackwordVisitor : DirectionVisitor 
    { 
     public Backword() 
     { 
      Pointer = n => n.Previous; 
      NextSelector = (action, current) => action.Id == current.PreviousId; 
      Add = (list, current, node) => list.AddBefore(current, node); 
     }    
    } 

由于只有两个用于整个列表的选项,这可能是矫枉过正的这种特殊情况。通过方向和使用条件将枚举传入Move方法可能会更好。

+0

它确实看起来过度杀伤力,但我真的很想找到一个更通用的方法来解决您提供的问题。感谢您的解决方案并花时间回复。 – JBond