是否有适用于折扣模型的设计模式?


35

有没有实施折扣模型的已知设计模式?

通过折扣模型,我的意思是:

  1. 如果客户购买产品X,产品Y和产品Z,则可获得10%或$ 100的折扣。

  2. 如果客户购买100个产品X,他将获得15%的折扣或$ 500

  3. 如果客户去年带来的收入超过$ 100K,则可获得20%的固定折扣

  4. 如果客户购买了2个单位的产品X,则可以免费获得1个单位的产品X(或产品Y)。

  5. ...

是否可以使用一种通用模式来处理所有上述情况?我正在考虑一些模型,但无法遇到通用模型。


IIRC我所见过的所有带有折扣示例的教程(有很多)都提出了策略模式
-t

2
@Kanini这是一个现实世界的问题吗?在这种情况下,该系统是实时的还是延迟的?这些规则是作为规则引擎还是数据库值呈现的?折扣搜索是否基于优先级?策略模式在大多数情况下都适用,但是必须考虑您的规则才能使其
生效

3
另外,如果有人购买2个产品X,一个产品Y和一个产品Z,是否会同时获得10%和一个额外的产品X?
Ubermensch 2012年

@gnat请提供一些其中一些教程的链接。
user16764 2012年

Answers:


18

如果问题是您需要应用多个折扣,则在给定的情况下,您可能需要考虑“ 责任链”模式。

简而言之,您可以将要处理的信息传递到第一个处理器,然后在返回结果之前决定是否从那里将其传递给其他处理器。

这样,您可以更改处理器的结构和顺序,而无需更改调用代码。


责任链是另一个好方法。甚至可能会加上策略。在只能应用一种折扣的情况下,每种策略都与另一种策略联系在一起。每个链计算其折扣(如果客户符合条件),将其与上一个折扣进行比较,然后将客户,订单和折扣数据传递给下一个链。+1。
Thomas Owens

1
只是我的意见,但我发现“责任链”在这种情况下过度设计的可能性更大。一个简单的折扣模型清单(如果需要,带有订购编号)就可以做到。清单本身独立于客户及其订单,因为所有客户都应得到同等对待。当折扣模型列表在运行时以高度动态的方式非常频繁地更改时,“责任链”更为合适。
布朗

11

策略,装饰者和状态模式对我来说是潜在的起点。状态对于2或3可能特别有用,因为2取决于订单的状态,而3取决于一段时间内的客户的状态。策略和装饰器在其他产品中脱颖而出,因为您可以使用策略来实现多个订单价格计算算法,装饰器可以为订单添加新的折扣。

但是,请记住,设计模式只是模型。可能没有适用的单个模式,而是一个模式系统。还可以考虑对描述的模型进行修改,以使其更适合您的解决方案。拥有一个好的设计总比强迫一个模式不一定要有用,因为仅仅为了能够说出您有一个模式,这是更好的选择。


不过,这不是状态模式的真正目的,不是吗?
pdr 2012年

@pdr我不明白为什么不。但这取决于您的实现。如果您的客户对象跟踪与客户相关的折扣,则可能有一项操作可返回客户有资格获得的折扣。当客户购买商品时,属性会更改,并且此方法的实现会通过状态模式进行更改。
Thomas Owens

1
嗯,我明白你的意思了。我认为这取决于客户是应用程序中的半永久对象,还是仅仅是存在于数据库中且需要更新的对象。问题尚不清楚,如此公平。+1
pdr 2012年

3
根据我的经验,善变的营销/销售部门始终会修改这些折扣业务规则。迫切需要使它们成为数据驱动和用户可修改的,而不是完全由代码驱动的。这将如何影响模型的选择?
jfrankcarr 2012年

@jfrankcarr在我看来,不会。我将使用某种配置填充导致折扣的项目集的值,折扣百分比等。可以动态构建状态机转换以及装饰器和策略的属性。
Thomas Owens

10

好吧,我将折扣模型设计为“ Precondition”和“ Discount”对,其中“ Precondition”是带有方法的类

  bool IsFulfilled(Customer c);

或/和

  bool IsFulfilled(Customer c, Order o);

折扣有一种方法void ApplyTo(Customer c)。这使您能够将任何类型的前提条件与任何类型的折扣组合在一起(我认为这是“过渡模式”的一种形式)。

如果您有固定数量的前提条件,则可以通过构建特定的子类型(策略模式)来解决问题。但是,当您的前提条件非常复杂时,使用AND,OR和NOT这样的逻辑语句,则可以更好地为条件实现某种规则解释器。规则可以是用简单的“特定于域的语言”编写的纯文本字符串。

对于“折扣”类也是如此,您可以为不同类型的折扣提供一些子类型,也可以采用一种通用方法,其中折扣规则以文本形式给出,并由某些解释程序进行评估。


我的直觉表明,这可能是他在问题中正在寻找的东西
Ubermensch 2012年

4
  • 可能需要一个IDiscount接口,因为所有不同的折扣都是同一件事,我们将在概念上将它们作为通用折扣来处理。

  • “此客户的订单”类可能需要折扣集合。清单?哈希?链表?不在乎。折扣适用于购买,不适用于客户!

  • 将Discount实例的构建与客户,购物车,历史记录等分开。这会发生很大变化-正如@jfrankcarr指出的那样。

  • 每种折扣的类别可能不同,因为每种折扣的算法和参数变化很大且无法预测。

  • 我看到很多事件处理,因为折扣计算会响应购物车的更改,反之亦然。

设计模式应用

  • 我看到一个strategy pattern。IDiscount是用于实现不同折扣算法的接口。
  • 我看到一个factoryabstract factory pattern在分析的这一点上,当然不是成熟的,而是单个类。合理地,必须在一个地方有足够的上下文来确定要应用哪些折扣,然后再创建折扣。一堂课 我认为,如果后来由于市场部的热闹派对而导致折扣折扣规则突然爆发,那么我认为,在这个基础工厂类别中,仍然必须合并任何其他折扣建设逻辑。

  • 我可以看到Chain of Responsibility。这不是该factory想法相互排斥的。每个折扣都将呼叫下一个人,而不是迭代折扣集合。在这种情况下,“客户订单”类不包含折扣集合。

  • 我认为,责任链中的“嗯……”因素是,每项折扣都有一个参考。含义是顺序很重要。事实并非如此。同样,CoR体现的概念是一个对象无法处理该请求,因此将其“传递给下一个更高的权限”。我们的模型是不同的。唯一的要求是计算。每个折扣都可以做到这一点。输出或效果可能为空,但会计算所有折扣。我本能地倾向于更高的真实度。

假设条件

  • 折扣基于当前的购物车和/或购买历史记录
  • 零或更多折扣可能适用。没有互斥的折扣
  • 正确的计算不取决于折扣的应用顺序。

什么变化,什么保持不变?

  • 折扣大不相同。组成每个规则的参数数量和种类不同。

  • 随着购物车的更改,合格折扣的参数也会更改。

  • 可用折扣数量变化

  • 该客户的折扣符合其购物车变更时的变更资格。

  • 购物历史不会因此次购买而改变

  • 总成本随采购额和折扣的变化而动态变化。

  • 首次应用后,折扣的输出可能会发生变化,例如,购买数量发生变化。


绝佳而完整的答案...但是我只关心一个问题,那就是假设部分不应存在,至少不要主导答案。整个想法是,该模式有助于准确地给人以安慰,而不必理会“假设”,通用规则不需要知道如何进行计算以及上下文中使用的任何详细实现(客户,购物车项) ,白天时间,季节等)。确实可以帮助您
le0diaz

前期的项目符号和“假设”部分只是我“展示您的工作”,说明问题本身,当然这也折衷于折扣模型设计。例如,我对折价执行顺序和相互依赖性的假设使我不再强调责任链。请注意,我在考虑模式的意图,而不是@docbrown那样的复杂性。我是反映设计意图的旺盛拥护者。
Radarbob

1

从逻辑上讲,折扣模型可以是任何模型,因此您不能假设可以事先对所有情况进行编程。回答您问题的任何人也不能完全确定您的实际需求。但是,假设您获得了现实世界中常见的折扣...

一个很大的问题是,是否将对折扣进行编程,或者是否希望用户输入折扣。如上所述,您永远不能对它们进行编程,但是通常的目标是像在一般情况下那样,尝试使其更多的数据输入,而不是全部编程。即使使用程序员来创建所有折扣,这在一定程度上也适用。

马丁·福勒(Martin Fowler)在“分析模式:可重用对象模型”中提到了“个体实例方法”,这是如何为会计系统实施“过帐规则”的一部分,但该规则与您的规则相当相似。我会提供更多细节,但这是受版权保护的作品,

对于用户界面,您要么需要提出非常简单的用例,要么构建一个解释器和查询构建器。可能两者兼而有之,一种用于简单案例,另一种用于高级案例。如果您确实编写了解释器,则使用Interpreter模式可能是一个相当不错的情况,因为与解析器生成器相比,它的编码相对简单,解析时间的缩短可能并不重要。(如果您喜欢使用解析器生成器,请不要让我阻止您)。

但是,不要尝试使用解释器来做所有事情-在某些时候,您只是在使用自己的简陋语言进行编程,因此您不妨使用一种真正的语言。如果您的解释语言支持功能(它可能应该支持调用它们-定义它们是可疑的),则可以用真实语言进行编码。不要沿着这条路走下去。

无论您做什么,最终都会有人希望折扣基于他们是否在促销后的30个工作日内购买-工作日仅在商店的邮政编码或客户的邮政编码所定义的区域中没有假日的情况下才算邮政编码。因此,不要尝试预先设计完美的系统-假设您有时需要编写代码来获得新的折扣并相应地进行设计。


0

有什么问题要问是否有一个有用的模式吗?需要哪种类型的模式-结构或行为模式?

理想情况下,如果我要为此编写软件,则只需一个算法即可。计算总折扣的简单函数如下:

cart.calculateDiscount(productVector);

您只需要这些!

需要澄清的是:我知道会有很多规则-这种表示的最基本的应该是规则库的形式(数据属性集和相对于它的折扣),并且上面的函数会对其进行迭代以对其进行计算。如果添加或删除了规则,则不应最终更改代码-只需更改规则库即可。

仅当我们需要不同的对象需要访问彼此的API或进行通信以将任务放置到位时,才需要使用模式。

PS:考虑一下-当防火墙处理数据包并通过或拒绝数据包(或对其进行修改)时,它将使用哪种设计模式?答案是上述任何一个!


当然,您还需要更多!想法是该算法与代码实现没有紧密联系。如果您检查场景很有可能会出现更多场景,您也以某种方式提到过它,那么他真的不是其他“规则”所依赖的。幼稚地认为规则仅取决于产品清单,而实际上取决于客户,时间,季节等。不知道您检查了哪种防火墙实施,但是我检查过的实施
确实
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.