C# 使用LinkedList重构类似方法

C# 使用LinkedList重构类似方法,c#,refactoring,C#,Refactoring,这主要是一个重构问题 我正在创建一些方法,根据操作历史的Id/PreviousId关系(请参见下面的基本类)返回/前进操作历史: 背景信息: //The list I'm using to pretend to be my database containing actions private List<Action> _actions { get; set; } private Action _currentAction { get; set; } pr

这主要是一个重构问题

我正在创建一些方法,根据操作历史的Id/PreviousId关系(请参见下面的基本类)返回/前进操作历史:

背景信息:

   //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; }
   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;
   }
   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;
   }
   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();
   }
   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);
       }
   }
我首先从数据库中获取一个操作。如果用户选择“GoBack”,我需要从数据库中获取上一个操作并将其存储在LinkedList中。这意味着用户可以重新访问相同的操作(即,返回然后再次前进),但可以从LinkedList版本调用该操作,而不是再次从数据库获取该操作。我不想首先从数据库中检索所有操作。我有这个功能,但我的GoBack()和GoForward()方法几乎相同

我希望看看是否有一种很好的方法可以将其重构成一个更通用的方法集,而不是复制代码?(注意-我的代码不包括用于减少读取的数据库调用,因此我将伪数据放入一个列表中作为我的数据库)

我在方法中引用的类级变量:

   //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; }
   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;
   }
   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;
   }
   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();
   }
   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);
       }
   }
这是我的GoForward()方法:

   //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; }
   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;
   }
   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;
   }
   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();
   }
   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);
       }
   }
如果你想编译代码。我在构造函数和BuildData方法中添加了以下内容,用于测试:

构造函数:

   //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; }
   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;
   }
   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;
   }
   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();
   }
   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);
       }
   }

提前谢谢

这里消除某些逻辑重复的一种方法是使用访问者模式

    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);
        }            
    }
使用ActionListAction=System.Action;
...
私有void GoBack()
{
移动(新BackwordVisitor());
}
私有无效GoForward()
{
移动(新的ForwardVisitor());
}
私有无效移动(方向访客方向)
{
var current=\u actionLinks.Find(\u currentAction);
如果(当前==null)
返回;
var节点=方向指针(当前);
如果(节点!=null)
{
_currentAction=node.Value;
返回;
}
var action=_actions.FirstOrDefault(i=>direction.NextSelector(i,_currentAction));
//没有进一步的行动
if(action==null)
返回;
添加(_actionLinks,current,action);
_当前动作=动作;
}
私有抽象类DirectionVisitor
{
公共Func指针{受保护集;get;}
public Func NextSelector{protected set;get;}
public ActionListAction添加{protected set;get;}
}
私有类ForwardVisitor:DirectionVisitor
{
公共前锋()
{
指针=n=>n.下一步;
NextSelector=(action,current)=>action.PreviousId==current.Id;
Add=(list,current,node)=>list.AddAfter(current,node);
}   
}
私有类BackwordVisitor:DirectionVisitor
{
公开背景词()
{
指针=n=>n.上一个;
NextSelector=(action,current)=>action.Id==current.PreviousId;
Add=(列表,当前,节点)=>list.AddBefore(当前,节点);
}            
}

因为只有两个选项可以在列表中移动,所以对于这个特定场景来说,这可能有点过头了。将枚举与方向一起传递到Move方法中并使用条件可能会更好。

这看起来有些过分,但我真正感兴趣的是获得一种更通用的方法来解决您提供的问题。感谢您提供的解决方案,并花时间回复。