Есть ли шаблон дизайна, который будет применяться к дисконтным моделям?

35

Существуют ли известные шаблоны проектирования для реализации дисконтных моделей?

Под дисконтными моделями я имею в виду следующее:

  1. Если клиент покупает продукт X, продукт Y и продукт Z, он получает скидку в размере 10% или 100 долларов США.

  2. Если клиент покупает 100 единиц Продукта X, он получает скидку в размере 15% или $ 500.

  3. Если клиент принес в прошлом году более 100 тысяч долларов, он получает фиксированную скидку 20%

  4. Если клиент приобрел 2 единицы Продукта X, он получает 1 единицу Продукта X (или Продукта Y) бесплатно.

  5. ...

Есть ли общий шаблон, который можно применять для обработки всех вышеперечисленных сценариев? Я думаю о нескольких моделях, но не могу найти общую модель.

Kanini
источник
IIRC все учебники, которые я видел, с примерами, связанными со скидками (их много), предлагают шаблон стратегии
gnat
2
@ Канини Это проблема реального мира? В таком случае эта система в режиме реального времени или отложенного времени? Представлены ли эти правила как значения правил или базы данных? Является ли поиск скидок иерархическим по приоритету? Шаблон стратегии будет работать в большинстве случаев, но ваши правила должны быть учтены, чтобы заставить его работать
Ubermensch
3
Также, если кто-то купит 2 единицы Продукта X, один Продукт Y и один Продукт Z, получит и 10%, и дополнительный продукт X?
Ubermensch
@gnat ссылки на некоторые из этих руководств, пожалуйста.
user16764

Ответы:

18

Если проблема заключается в том, что вам необходимо применить несколько скидок, при определенных обстоятельствах вы можете рассмотреть шаблон цепочки ответственности .

Короче говоря, вы передаете информацию, которую хотите обработать, первому процессору, и оттуда решается, передавать ли ее другим процессорам, прежде чем возвращать результат.

Таким образом, вы можете изменить структуру и последовательность процессоров, даже не меняя вызывающий код.

прецизионный самописец
источник
Цепочка ответственности - еще один хороший вариант. Может быть даже в сочетании со стратегией. В случае, когда может быть применена только одна скидка, каждая стратегия связана с другой. Каждая цепочка вычисляет свою скидку (если клиент имеет право), сравнивает ее с предыдущей скидкой и передает данные о клиенте, заказе и скидке в следующую цепочку. +1.
Томас Оуэнс
1
Просто мое мнение, но я нахожу более вероятным, что «Цепочка ответственности» является чрезмерным дизайном для этого случая. Простой список дисконтных моделей (при необходимости, с нумерацией заказа) должен сделать это. Сам список не зависит от клиента и его заказов, поскольку ко всем клиентам следует относиться одинаково. «Цепочка ответственности» более уместна, когда список моделей скидок изменяется очень часто во время выполнения с высокой динамичностью.
Док Браун
11

Образцы стратегии, декоратора и состояния выделяются мне как потенциальные отправные точки. Состояние может быть особенно полезным для 2 или 3, так как 2 зависит от состояния заказа, а 3 зависит от состояния клиента в течение определенного периода времени. Стратегия и Декоратор отличаются от других, поскольку вы можете использовать стратегию для реализации нескольких алгоритмов расчета цены заказа и декоратор для добавления новых скидок к заказу.

Однако помните, что шаблоны проектирования - это просто модели. Там может быть не один шаблон, который применяется, а система шаблонов. Также рассмотрите возможность внесения изменений в описанные модели, чтобы они лучше подходили вашему решению. Лучше иметь хороший дизайн, чем создавать шаблон, который не обязательно помогает просто сказать, что у вас есть шаблон.

Томас Оуэнс
источник
Правда, это не то, для чего предназначен шаблон состояния, не так ли?
pdr
@pdr Не понимаю, почему нет. Но это зависит от вашей реализации. Если ваш объект клиента отслеживает зависящие от клиента скидки, то может возникнуть операция возврата скидок, на которые имеет право клиент. Когда клиент покупает вещи, атрибуты меняются, и реализация этого метода изменяется через шаблон состояния.
Томас Оуэнс
1
Хм, я понимаю, что вы имеете в виду. Я думаю, это зависит от того, является ли клиент полупостоянным объектом в приложении или просто тем, что находится в базе данных и нуждается в обновлении. Это не ясно из вопроса, поэтому достаточно справедливо. +1
pdr
3
Исходя из моего опыта, эти виды бизнес-правил дисконтирования постоянно изменяются непостоянными отделами маркетинга / продаж. Существует большая необходимость сделать их управляемыми данными и изменяемыми пользователем, а не полностью управляемыми кодом. Как это повлияет на выбор модели?
jfrankcarr
@jfrankcarr На мой взгляд, это не так. Я бы заполнил значения для наборов предметов, которые приводят к скидке, процентные ставки и т. Д. Из какой-то конфигурации. Что-то вроде динамического построения переходов моего автомата и атрибутов моих декораторов и стратегий.
Томас Оуэнс
10

Ну, я бы разработал модель скидок в виде пары «Предварительное условие» и «Скидка», где «Предварительное условие» - это класс с методами.

  bool IsFulfilled(Customer c);

или

  bool IsFulfilled(Customer c, Order o);

и скидка имеет метод void ApplyTo(Customer c). Это дает вам возможность комбинировать любые предварительные условия с любым типом скидки (я думаю, что это форма "схемы моста").

Если у вас есть фиксированное количество предварительных условий, то вы можете решить эту проблему путем создания определенных подтипов (шаблон стратегии). Однако, когда ваши предварительные условия могут быть очень сложными, с такими логическими утверждениями, как AND, OR и NOT, вы можете лучше реализовать некоторый интерпретатор правил для условий. Правила могут быть простой текстовой строкой, написанной на простом «предметно-ориентированном языке».

То же самое относится и к классу «Скидка»: у вас могут быть несколько подтипов для разных типов скидок, или общий подход, где правила скидок даются в текстовой форме, оцениваемой некоторым интерпретатором.

Док Браун
источник
Моя интуиция предполагает, что это может быть тем, что он ищет в контексте вопроса
Ubermensch
4
  • Вероятно, нужен интерфейс IDiscount, поскольку все различные скидки - это одно и то же, и мы будем рассматривать их концептуально как общие скидки.

  • Класс "заказ этого клиента", вероятно, нуждается в коллекции скидок. Список? Hash? Связанный список? Пофиг, пока. Скидки распространяются на покупку, а не на покупателя!

  • Держите экземпляр экземпляра Discount отдельно от Customer, Shopping Cart, History и т. Д. Он сильно изменится, как указывал @jfrankcarr.

  • Вероятно, разные классы для каждой скидки, так как алгоритм и параметры для каждой скидки сильно и непредсказуемо различаются.

  • Я вижу много обработки событий, так как расчет скидок реагирует на изменения в корзине покупок, и наоборот.

Дизайн шаблона приложения

  • Я вижу strategy pattern. IDiscount - интерфейс для реализации различных алгоритмов скидок.
  • Я вижу factory. Конечно, не полномасштабный abstract factory pattern, а отдельный класс на данный момент в анализе. Разумно, должно быть единственное место, где есть достаточно контекста, чтобы решить, какие скидки применяются и затем создать их. Один класс. Если правила применения скидок впоследствии срываются из-за вечеринки в отделе маркетинга, любая дополнительная логика Discount Construction должна объединяться в этом базовом заводском классе, я думаю.

  • Я могу видеть Chain of Responsibility. Это не является взаимоисключающей factoryидеей. Вместо того, чтобы повторять коллекцию скидок, каждая скидка вызывает следующего парня. Класс «заказ клиента» в этом случае не содержит коллекцию скидок.

  • Я думаю, что фактор «хмммм…» в цепи ответственности состоит в том, что каждая скидка имеет ссылку на следующую. Подразумевается, что порядок имеет значение. Что не так. Кроме того, концепция, которую воплощает CoR, заключается в том, что один объект не может обработать запрос, поэтому он передается «до следующего более высокого уровня». Наша модель отличается. Единственный запрос - это рассчитать. Каждая скидка делает это. Результат или эффект могут быть нулевыми, но каждая скидка рассчитывается. Я инстинктивно склоняюсь к более высокой точности в реальном мире.

Предположения

  • Скидки основаны на текущей корзине покупок и / или истории покупок.
  • Ноль или более скидки могут применяться. Нет взаимоисключающих скидок
  • Правильный расчет не зависит от порядка, в котором применяются скидки.

Что меняется, что остается прежним?

  • Скидки очень разные. Различное количество и вид параметров для составления каждого правила.

  • Аргументы для соответствующих скидок изменяются при изменении корзины покупок.

  • Количество доступных скидок меняется

  • Скидки, которые этот клиент имеет право на изменения при изменении его корзины покупок.

  • История покупок не меняется в контексте этой покупки

  • Общая стоимость изменяется динамически в зависимости от количества покупок и скидок.

  • После первоначального применения скидка может измениться, например, при изменении количества покупки.

radarbob
источник
Отличный и полный ответ ... НО меня беспокоит только то, что раздел предположений не должен быть там, по крайней мере, чтобы не приводить ответ. В целом идея заключается в том, что шаблон помогает точно дать согласие и забыть о «предположениях», общее правило не должно знать о том, как производятся вычисления, и что любая детальная реализация использует из контекста (Customer, Cart cart). , Время суток, сезон и тд). Правда, помогите, хотя полный
le0diaz
Предварительные маркеры и раздел «Допущения» - это просто мои «показы своей работы», рассуждающие о самой проблеме, которая, конечно же, отрицательно сказывается на дизайне дисконтной модели. Например, мои предположения о порядке исполнения скидок и взаимозависимости побуждают меня преуменьшать значение Цепочки ответственности. Обратите внимание, что я думаю о намерении шаблона, а не о его сложности, как @docbrown. Я большой сторонник для отражения намерений в дизайне.
радар Боб
1

По логике, модель со скидкой может быть чем угодно , поэтому вы не можете предполагать, что можете запрограммировать все случаи заранее. Никто, отвечая на ваш вопрос, не может быть полностью уверен, что вам действительно нужно. Однако при условии, что вы получаете обычные виды скидок, которые можно найти в реальном мире ...

Большой вопрос в том, будут ли запрограммированы скидки или вы хотите, чтобы пользователи вводили их. Как упомянуто выше, вы никогда не сможете запрограммировать их, но обычно цель состоит в том, чтобы попытаться сделать их более вводимыми, как в обычных случаях, а не программировать их все. Это в некоторой степени относится, даже если для создания всех скидок используются программисты.

Мартин Фаулер упоминает «Метод индивидуального экземпляра» в «Шаблонах анализа: многократно используемые объектные модели» как часть того, как реализовать «Правила публикации» для систем бухгалтерского учета, но правила кажутся довольно схожими с вашими. Я бы дал больше деталей, но это работа, защищенная авторским правом и

Для пользовательского интерфейса вам нужно либо придумать довольно простые варианты использования, либо создать интерпретатор и построитель запросов. Возможно оба: один для простых случаев и еще один продвинутый. Если вы действительно пишете интерпретатор, это, вероятно, довольно хороший случай для использования шаблона Интерпретатор, поскольку его относительно просто кодировать по сравнению с генератором синтаксического анализатора, и более медленное время анализа, вероятно, не будет иметь большого значения. (Если вам нравится использовать генераторы парсеров, не позволяйте мне останавливать вас).

Однако не пытайтесь делать все с помощью переводчика - в какой-то момент вы просто программируете на своем собственном непристойном языке, так что вы могли бы также использовать и настоящий. Если ваш интерпретируемый язык поддерживает функции (вероятно, он должен вызывать их - определение их сомнительно), они могут быть закодированы на реальном языке. Не идите дальше по этой дороге, чем нужно.

Независимо от того, что вы делаете, в конечном итоге кто-то захочет, чтобы скидка основывалась на том, были ли они приобретены в течение 30 рабочих дней с момента продвижения по службе, причем рабочие дни учитываются только в том случае, если в регионе не было праздников, определенных почтовым индексом магазина или клиентом. Почтовый индекс. Поэтому не пытайтесь спроектировать идеальную систему заранее - предположим, что вам иногда потребуется написать код для новых видов скидок и разработать соответствующий дизайн.

PSR
источник
0

Есть ли смысл спрашивать, есть ли полезный шаблон для этого? Какой тип модели требуется - структурный или поведенческий?

В идеале, если бы я написал программное обеспечение для этого, все, что нужно, это алгоритм . Простая функция, которая рассчитывает общую скидку следующим образом:

cart.calculateDiscount(productVector);

Вам не нужно ничего больше, чем это!

Чтобы уточнить: я понимаю, что будет много правил - большинство базовых таких представлений должно быть в форме базы правил (набор атрибутов данных и результирующая скидка на нее), и функция, подобная выше, будет выполнять итерацию для ее вычисления. Если правила добавляются или удаляются, вам не следует менять код - просто измените базу правил.

Шаблон понадобится только в том случае, если нам нужны разные объекты, для доступа к API друг друга или для связи, чтобы поставить задачу на место.

PS: Подумайте об этом - когда брандмауэр обрабатывает пакеты и передает или отклоняет пакеты (или изменяет его) - какой шаблон проектирования он использует? Ответ НИЧЕГО из вышеописанного!

Дипан Мехта
источник
Конечно, вам нужно больше, чем это! Идея состоит в том, что алгоритм не тесно связан с реализацией кода. Если вы проверите сценарии, весьма вероятно, что появятся другие сценарии, также вы как-то упомянули об этом, он не совсем то, от чего будет зависеть любое другое «правило». Наивно думать, что правило будет зависеть только от списка продуктов, но на самом деле зависит от клиента, времени, сезона и так далее. Не знаю, какую реализацию Брандмауэра вы проверили, но те, которые я проверял, имеют много структурных / конструктивных шаблонов
le0diaz