【问题标题】:Applying the Strategy Pattern应用策略模式
【发布时间】:2014-06-03 22:14:08
【问题描述】:

假设您有一家工厂,产品在制造过程中通过皮带传送。在这中间的某个地方,有必要根据某些业务逻辑将来自单个传送带输入的产品流拆分到多个传送带上。也许他们需要用红色、绿色和蓝色产品的输出带按颜色分开。也许这是某种简单的负载平衡,因此需要根据每个输出带的百分比来拆分产品。

                  ________________
                 / ___Color=Red___...
    ____________/ /_______________
 ..._Products___  ____Color=Green_...
                \ \_______________
                 \____Color=Blue__...

或者

                  __________
                 / ___60%___...
    ____________/ /_________
 ..._Products___  ____20%___...
                \ \_________
                 \____20%___...

具体来说,我正在尝试对routing functionality of ProModel 进行逆向工程。路由方法应该能够在运行时更改,单个输出“带”的路由属性也应该能够更改(例如更改颜色标准)。如果路由方法切换回之前定义的方法,它会记住使用的设置。

为了使我的代码通用化,产品是“实体”对象,分离带是“路由”对象,内部路由逻辑是“路由规则”,整个输入带/内部逻辑/输出带是一个“路由器”。根据 ProModel 的用法,“RoutingRule”适用于“Router”而不是单个“Routes”——如果规则是 RouteByPercent,那么每个传出的“Route”都应该有一个百分比,而如果它是RouteByAttribute,每个“路线”都会有一个属性/比较器/标准(例如“颜色”、“=”、“蓝色”)来测试传入的“实体”。

我可以看到,使用策略模式可以灵活地支持和交换“RoutingRule”对象(例如 ProModel 链接中可能的规则列表);但是,我不太明白如何在不紧密耦合“RoutingRule”和“Route”对象的情况下存储每个规则的状态信息。

在将策略模式应用于这种情况时,我应该如何存储适用于每个路由的 RoutingRule 特定属性?我正在尝试考虑我的选项,但我不喜欢其中任何一个:

  1. 天真的方法是在 Route 类中为所有可能的 RoutingRules 设置成员变量:m_Percentm_EntityAttribute/m_EntityComparator/m_EntityCriteria ... 但由于显而易见的原因,这将是可怕的。每当我添加一种新的 RoutingRule 时,我都必须向 Route 类添加成员变量。
  2. 我一直计划这样做的方式是使用关联数组(字典或自定义哈希表),因此可以即时添加或删除属性。这通常很好,但是必须有一些代码来 a) 初始化特定于规则的属性 b) 告诉我需要为当前规则设置哪些属性(我不能只提示用户输入所有 Route 属性,因为它们可能不会全部被当前选择的规则使用) c) 验证 Route 属性是否处于一致状态。
  3. 我能想到的唯一其他方法是将 Route 属性存储在 RoutingRule 本身中,一个关联数组中的关联数组,这样我就可以按名称查找 Route 并获取适用于每个的确切属性的列表当前规则的路由。这种方式解耦最多,但是我不确定如何构造它,以便向 Route 询问其属性是一个透明的过程。而不是像#1 和#2 那样的Route.Attributes(strAttrName),它更像是RoutingRule.RouteAttributes(Route.Name).Attributes(strAttrName)
  4. 也许我想多了?

顺便说一句,我在 VBA 中执行此操作时会受到所有暗示的限制。

【问题讨论】:

  • 我很感兴趣,但是@Blackhawk 有什么问题?
  • @ckuhn203 看看我从粗体文本向下的编辑是否有助于澄清我的困惑:(
  • @Blackhawk 我不确定,因为这已经很扭曲了,但也许this solution 可以告诉你如何迭代列表中的属性(第 3 号)?

标签: vba design-patterns modeling strategy-pattern


【解决方案1】:

检查这个用某种语言编写的骨架:

class Entity { // Or call this the EntityHolder
  Map attrs;
  RealEntity re; // The payload
}

abstract class RouteElement {
  void process(Entity entity);
  Map attrs;
}

interface RouterRule {
  RouteElement pickNextRouteElement(Entity e, List<RouteElement> allOutRoutes);
}

class Router extends RouteElement {
  List<RouteElement> outRoutes;
  RouteElement deadRoute;
  RouterRule routerRule;

  void process(Entity entity) {
    RouteElement nextRouteElement = routerRule.pickNextRouteElement(entity, outRoutes);

    if (nextRouteElement == null) {
      if (deadRoute) {
        deadRoute.process(entity);
      }
    } else {
      nextRouteElement.process(entity);
    }
  }

  void setRouterRule(RouterRule rr) {
    routerRule = rr;
  }

  RouterRule getRouterRule() {
    return routerRule;
  }

  void addOutRouteElement(RouteElement re) {
    outRoutes.add(re);
  }

  void setDeadRoute(RouteElement re) {
    deadRoute = re;
  }
}

class ColorRouterRule implements RouterRule {

  RouteElement pickNextRouteElement(Entity e, List<RouteElement> allOutRoutes) {
    foreach(RouteElement re in allOutRoutes) {
      if (e.getAttr("color") == re.getAttr("color") {
    return re;
      }
    }

    return null;
  }
}

class RandomRouterRule implements RouterRule {

  RouteElement pickNextRouteElement(Entity e, List<RouteElement> allOutRoutes) {
    int rand = random(0, allOutRoutes.length());
    return allOutRoutes.get(rand);
  }
}

class PercentRouterRule implements RouterRule {
  RouteElement pickNextRouteElement(Entity e, List<RouteElement> allOutRoutes) {
    int[] weights = int[allOutRoutes.length()];

    int prevWeight = 0;
    for (i = 0; i < weights, i++) {
      weights[i] = prevWeight + allOutRoutes.get(i).getAttr("percent");
      prevWeight = weights[i];
    }

    int rand = random(0, prevWeight);

    for (i = 0; i < weights, i++) {
      if (rand <= weights[i]) {
        return allOutRoutes[i];
      }
    }
  }
}

class RouterManager {

  Router routerBeingManaged;
  Stack<RouterRule> oldRouterRules;

  void setNewRouterRule(RouterRule rr) {
    oldRouterRules.push(routerBeingManaged.getRouterRule());
    routerBeingManaged.setRouterRule(rr);
  }

  void revertToOldRouterRule() {
    routerBeingManaged.setRouterRule(ooldRouterRules.pop());
  }

  ..
}


class Route extends RouteElement {
  List<RouteElement> route;

  void addRouteElement(..) {
    ..
  }

  void process(Entity entity) {
    foreach(RouteElement as re in route) {
      re.process(entity);
    }
  }
}

class MyApp {

  void main() {
    Route r = new Route();

    // Build the route
    r.addRouteElement(...);
    r.addRouteElement(...);

    Router router1 = new Router();
    router1.addOutRouteElement(..);
    router1.addOutRouteElement(..);
    router1.addOutRouteElement(..);

    RouterManager router1Mgr = new RouterManager(router1);
    router1Mgr.setRouterRule(new RandomRouterRule());

    r.addRouteElement(router1);

    r.addRouteElement(...);
    r.addRouteElement(...);

    // Get entities from somewhere
    r.process(entity);

    // Change the router rule
    router1Mgr.setRouterRule(new PercentRouterRule());

    // Get entities from somewhere
    r.process(entity);

    // Revert the router rule
    router1Mgr.revertToOldRouterRule();

    // Get entities from somewhere
    r.process(entity);
  }
}

【讨论】:

  • +1 感谢您抽出宝贵时间输入此内容!这似乎与我正在考虑的选项列表中的 #2 最相似,并添加了一种用于保存和恢复以前的 RoutingRules 的机制。填充正确 Entity 属性的责任仍在 RoutingRule 类之外,因此使用 API 的任何人都必须“只知道”要填充哪些 Route 属性,以及用于更改当前 RoutingRule 和相关 Route 属性的任何用户界面(参见我的问题中的链接)必须引用关联 RoutingRules 和 Route 属性的表。
  • 由 RoutingRule 填充的实体属性表明此类属性不是实体的内在属性。相反,RoutingRules 应该只根据实体或路由的现有属性进行路由。如果您需要将路由属性添加到实体(源路由),请考虑同时添加两个 RouteElement,UI 可能给人的印象是只添加一个。在这里,一个是路由器。在路由器之前的一些元素,您添加确定所需属性的 RouteElement 并将其添加到实体。为清楚起见,您的 UI 可能允许显式添加两者。
  • 对不起,我的意思是说 Route 属性而不是 Entity 属性:P 我认为这是我将采用的实现方法。非常感谢您的建议!
  • 在那种情况下,正如您已经确定的那样,这些属性不是路由固有的,而是路由器和路由之间的关联(在我的术语中高于 RouteElement)。您可以正确编码这些属性以由 RoutingRule 持有(恢复也将起作用)。并且您的 RoutingRule 应该在其逻辑中使用此类属性来确定已接受实体的目的地。所以这样想:一个 RoutingRule 会根据 Entities、next RouteElements 和 Routernext RouteElement 关联属性的属性进行路由。
  • 向 RoutingRule 添加一个(或多个)方法以返回它用于路由的关联属性。我的意思是返回元数据(如属性名称、它们的类型)。使用它们将输入小部件动态添加到 UI,其中小部件计数还将取决于路由器连接到的下一个 RouteElement 的数量。当用户输入并保存关联属性时,将它们打包到一个对象集合中(新类型,如 RouterRouteElementAssociationAttrs)并添加到 RoutingRule 的实例中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多