【问题标题】:Refactoring an algorithm重构算法
【发布时间】:2023-03-12 10:58:01
【问题描述】:

我正在尝试在 C++ 中为我自己的 MVC 应用程序重新实现 ASP.NET MVC 路由规则。
目前,如果访问 unordered_map 中的控制器/动作不是 O(1),则代码在最佳情况下至少需要 O(N)
我希望我的代码更喜欢带有已经在 URI 中的控制器的路由,例如,如果当前 URI 是 'projects/2/show' 并且我有 '[Controller]/ [Action]/[ID]/', '[Controller]/[ID]/[Action]/', 'projects/[ID]/[Action]/ ''projects/[ID]/show' 我更喜欢先测试匹配的最后一条路线。
我的问题是怎么做?

到目前为止,它将遍历所有路由并尝试匹配它。
我尝试尽可能多地记录代码,但如果有不清楚的地方请告诉我。

我当前的代码如下所示:

// handlePathChange() is called whenever the URI changes

void MVCApplication::handlePathChange()
{
 // Right now I'm iterating a list (O(N) runtime)
 RoutesListType::iterator iter = routes.begin(); 

 // If there are no routes then something is wrong
 if ( iter == routes.end() )
 {
  log("Error") << "No routes found";
  return;
 }

 bool pageFound = false;

 // iterate until a route matches or until the routes end
 while ( iter != routes.end() ) 
 {
  if ( matches(*iter) )
  {
   pageFound = true;
   break;
  }

  iter++;
 }

 // If a page is not found then log it
 if (!pageFound)
  log("Error") << "404, page at url " << internalPath() << " not found";
}

bool MVCApplication::matches(Route &r)
{
 log("Notice") << "Matching route pattern " << r.getPattern() + " to url " << internalPath();

  // gets the URI
 const string url = internalPath();

 char_separator<char> urlSep("/");
 char_separator<char> patternSep("[]/");

 boost::tokenizer<boost::char_separator<char> > patternTokens(r.getPattern(), patternSep);
 tokenizer<char_separator<char> > urlTokens(url, urlSep);

 int pos = 1;

 bool actionFound = false;

 Route::RouteDefaultsType &defaults = r.getDefaults(); // unordered_set<string, string>
 ControllerMapType &controllers = getControllers(); // unordered_set<string, shared_ptr<Controller> >

 ControllerType currentController; // shared_ptr<Controller>
 Controller::ActionType action; // boost::function that returns a view

 for (tokenizer<char_separator<char> >::iterator pattern_iter = patternTokens.begin(), url_iter = urlTokens.begin(); pattern_iter != patternTokens.end(); ++pattern_iter, pos++)
 {
  if ( url_iter == urlTokens.end() ) // If the number of URI tokens is lower then route tokens seek default values
  {
   if ( *pattern_iter == "Controller" || pos == 1) // Map controller to default
   {
    if ( defaults.find(*pattern_iter) != defaults.end() )
     currentController = controllers[defaults[*pattern_iter]];
    else
    {
     log("Error") << "No default controller found";

     return false;
    }
   }
   else if ( *pattern_iter == "Action" ) // Map action to default
   {
    Route::RouteDefaultsType::const_iterator iter = defaults.find(*pattern_iter);
    if ( iter != defaults.end() )
    {
     if ( currentController->getActions().find(iter->second) != currentController->getActions().end() )
     {
      action = currentController->getActions()[iter->second];
      actionFound = true;
     }
    }
   }
   // Checks whether the hard-coded value in the route is an action or a parameter 
   else
   {
    Route::RouteDefaultsType::const_iterator iter = defaults.find(*pattern_iter);
    // Search for a static action eg. /[Controller]/edit/
    if ( currentController->getActions().find(iter->second) != currentController->getActions().end() ) 
    {
     action = currentController->getActions()[iter->second];
     actionFound = true;
    }
    else // Maps parameters to defualt values
    {
     boost::unordered_map<string, string>::const_iterator iter = defaults.find(*pattern_iter);
     if ( iter != defaults.end() )
      currentController->addParameter(*pattern_iter, iter->second);
    }
   }
  }
  else // Match non-default values
  {
   if ( *pattern_iter == "Controller" || pos == 1) // Match controller
   {
    if ( getControllers().find(*url_iter) != getControllers().end() )
     currentController = controllers[*url_iter];
    else
     return false;
   }
   else if ( *pattern_iter == "Action" ) // Match action
   {
    if ( currentController->getActions().find(*url_iter) != currentController->getActions().end() )
    {
     action = currentController->getActions()[*url_iter];
     actionFound = true;
    }
   }
   // Checks whether the hard-coded value in the route is an action or a parameter
   else 
   {

    if ( currentController->getActions().find(*url_iter) != currentController->getActions().end() )
    {
     action = currentController->getActions()[*url_iter];
     actionFound = true;
    }
    else // If not, as a parameter
     currentController->addParameter(*pattern_iter, *url_iter);
   }

   ++url_iter;
  }
 }
// If controller action found show view
 if ( actionFound )
 {
  if ( currentView )
   root()->removeWidget(currentView);

  currentView = action(); // Perform action
  root()->addWidget(currentView);
 }
 else
 {
  log("Error") << "No action found";
  return false;
 }

 return true;
}

算法如下:

foreach route:
  if the route matches then: break.

  if number of url tokens < number of route pattern tokens then:
    if pattern token == "Controller" or it is the first token then:
      if the default controller exists then:
        assign it as the current controller.
      else:
        return false
    else if pattern token == "Action" then:
        if the default action exists in the current controller then:
          assign it as the current action.
          set actionFound to true.
    else:
        if the hard-coded action in the routes exists in the current controller then:
          assign it as the current action.
          set actionFound to true.
        else:
          if a default value for this parameter exists:
            add a parameter with a default value and route token as name to the current controller.
  else:
    if pattern token == "Controller" or it is the first token then:
      if the url token matches a controller name then:
        assign it as the current controller.
      else:
        return false
    else if pattern token == "Action" then:
      if the url token matches an action name inside the current controller then:
        assign it as the current action.
        set actionFound to true.
    else:
        if the hard-coded action in the uri token exists in the current controller then:
          assign it as the current action.
          set actionFound to true.
        else:
            add a parameter with a uri token as value and route token as name to the current controller.

if actionFound == true then:
  perform controller action.
  render view.

return actionFound

我很乐意提供任何改进建议,包括格式和代码结构,但主要是运行时效率。

【问题讨论】:

  • 我怀疑任何人都能够帮助您处理该代码。也许你可以用伪代码简明扼要地总结一下你的算法。
  • @Hogan:“我希望我的代码更喜欢带有控制器的路由,该控制器已经在 URI 中,例如,如果当前 URI 是 'projects/2/show' 并且我有 '[Controller ]/[Action]/[ID]/'、'[Controller]/[ID]/[Action]/'、'projects/[ID]/[Action]/' 和 'projects/[ID]/show' I更喜欢先测试匹配的最后一条路线。”。怎么办?

标签: c++ model-view-controller url-routing asp.net-mvc-routing


【解决方案1】:

尝试将其发布到http://refactormycode.com/

也就是说,有几个指针(没有双关语):

  • 尽量将行数保持在 80-120 个字符以内,太长会导致难以阅读。
  • 注释行前,而不是行后
  • if .. then .. else 树也使其难以阅读。将其拆分为函数或使用每种类型的函数对象。
  • typedef 模板(boost::tokenizer&lt;boost::char_separator&lt;char&gt; &gt; 重复不可读)

这些只是样式提示。这种结构很难阅读代码本身;)。

【讨论】:

  • 你会如何选择拆分成函数的内容?
  • 每个分支变成一个函数怎么样。
  • 但函数是可重用的代码部分。这些代码只执行一次。
  • @the_drow:将其分解为函数不仅关乎可重用性,还关乎可读性(对于现代编译器,代码应该同样具有高性能)。
  • 我的问题是所有这些函数都会改变匹配的状态。在某些分支中,他们必须返回 false,而在某些分支中,他们只需设置一个标志。我不知道从哪里开始。
猜你喜欢
  • 2013-08-27
  • 1970-01-01
  • 1970-01-01
  • 2011-11-28
  • 1970-01-01
  • 1970-01-01
  • 2018-04-17
  • 2015-04-30
  • 1970-01-01
相关资源
最近更新 更多