1

我有一个用 python/Django 编写的公司应用程序(不需要 python 经验来回答这个问题)。基本上是它的 SAAS。似乎很少有客户对几个模块有不同的要求。假设有一个 URL www.xyz.com/groups 被所有客户端使用,但一些客户端希望在调用相同 URL 时具有不同的输出。我想知道如何在不为每个客户端编写新函数或在单​​个函数中编写条件的情况下做到这一点。我知道这是一个愚蠢的问题,但我知道必须有一些解决方案。

4

1 回答 1

2

如果您的代码需要为案例“a”执行“A”,为案例“b”执行“B”,为案例“c”执行“C”,那么无论您选择哪种解决方案,代码中的某处都必须存在决定是否发生“a/b/c”案例,并且必须存在一些可以为该案例发送正确的“A/B/C”操作的东西,当然这些 A/B/C 操作必须写在某处在代码中,也是。

跳出代码并考虑一下。如果它被指定并且必须发生 - 它必须在某处编码。你无法逃避这一点。现在,如果案例/操作是微不足道和典型的,您可能会发现一些更多甚至更多的可配置库,它意外地允许您配置此类案例和操作,然后您就可以“无代码”拥有它并且没有“混乱”。但是从形式上看,代码在库中很深。因此,对决策者、调度者和动作进行了编码。只是不是由你 - 由猜到你需求的人。

但是,如果您的需求非常重要且非常具体,例如,如果它需要您的各种条件来决定它是哪种 a/b/c 案例 - 那么很可能您将不得不为自己编写“决定者”部分。这意味着大量的 IF 树、嵌套开关、规则 n 循环或任何你喜欢或觉得足够的东西。在此之后,您将处于调度/执行阶段,这可以通过多种方式实现 - 即策略模式 - 正是它:调度(通过与案例相关的具体类)和执行(具体策略具有具体的案例代码)。

让我们尝试类似 OO 的方法:

例如,如果您有用户类型 U1、U2、U3 的案例 a/b/c,则可以引入三个类:

UserType1 inherits from abstract UserType or implements "DoAThing" interface
UserType2 inherits from abstract UserType or implements "DoAThing" interface
UserType3 inherits from abstract UserType or implements "DoAThing" interface

UserType1 implements virtual method 'doTheThing' that executes actionA
UserType2 implements virtual method 'doTheThing' that executes actionB
UserType3 implements virtual method 'doTheThing' that executes actionC

your Users stop keeping "UserType" of type "int" equal to '1/2/3' - now their type is an object: UserType1, UserType2 or UserType3

每当您必须为给定用户做某事时,您现在只需:

result = user.getType().doTheThing( ..params..)

因此,您可以使用 OO:tell, don't ask规则,而不是 iffing/switching。如果待办事项仅依赖于 UserType,则让 UserType 执行它。生成的代码尽可能短 - 但要以创建类的数量为代价,嗯,......

...决策者、调度程序和操作仍在代码中。各种用户类型类中的操作 - 显而易见的。Dispatch - 明显 - 通用抽象基方法的虚拟调用。和决定者..?好吧:有人在某些时候必须为用户选择并构造正确的 UserType 对象. 如果用户存储在数据库中,如果“usertype”只是一个整数 1/2/3,那么在你的 ORM 层的某个地方,这些 1/2/3 数字必须被解码并转换为 UserType1/2/3 类/对象. 这意味着,你需要一个 ifs 树或一个 switch 等等。或者,如果你有一个足够聪明的 ORM——你只需设置一堆规则,它就为你做了,但这只是又一次将部分工作委托给更多或更多可配置的库。更不用说您的 UserType1/2/3 类实际上变成了某种 .. 策略。

好的,让我们攻击“选择”部分。

您可以在某处构建 ifs 或 switch 树来决定和分配,但命令式似乎很难闻。或者,使用 OO,您可以尝试对某些东西进行多态化,以便“它只会做正确的事情”,但它不会解决任何问题,因为您必须再次在某处选择对象类型。所以,让我们尝试数据驱动:让我们使用查找。

we've got five implementations of an action
create a hash/dictionary/map
add usertype1->caseA to the map
add usertype2->caseC to the map
add usertype3->caseB to the map
add usertype4->caseA to the map
add usertype5->caseE to the map
....

现在,每当您有用户并需要决定时,只需查找即可。您可以持有一个随时可用的策略对象,而不是一个“案例”。或者一个可调用的方法。或类型名。或者你需要的任何东西。关键是,而不是写

if( user.type == 1) { ... }
else if( user.type == 2)  ...

或切换,您只需查找它:

thing = map[ user.type ]
if ( thing is null ) ???

但是,请注意,如果不小心,您有时可能在地图中找不到匹配项。而且,必须为所有情况预先定义地图。因此,简单if X < 100可能会变成地图内的一百个 0..99 条目。

当然,您可以使用一些规则引擎而不是映射,并且可以定义一个映射,例如

X<100 -> caseA
X>=100 -> caseB

然后针对您的用户类型“运行”规则并获得一个“结果”,它将告诉您“caseA”。

等等。

每个部分 - 决定,调度,执行 - 您可以以各种方式实现,更短或更长,或多或少可扩展,或多或少可配置,如 OO/命令式/数据驱动/功能/等 - 但您无法逃避它们:

  • 您必须定义案例的判别式
  • 你必须定义动作的实现
  • 您必须定义案例到行动的映射

如何做到这一点,取决于你的审美、语言特性、框架、库以及......你想花在创建和维护它上的时间。

于 2013-06-07T08:17:40.497 回答