我正在做一项任务,我至少有几个产品有问题,例如买一送一,买 4 送一,买 5 并获得 20% 的折扣。现在我想知道我应该怎么做来处理这样的问题,因为当我遇到更多的产品时,产品计算的代码库会失控。这里最好的解决方案是什么?目前我所做的是根据每个产品代码创建一个类,并且产品的计算在该文件中是本地的。对象构造是通过静态工厂方法完成的。关于算法改进的任何建议,如果我可以进一步减少代码并使其简单,那就太好了。
class BOGOFCalculator implements BasketPriceCalculator
{
const MIN_ITEMS_REQUIRED_FOR_DISCOUNT = 2;
/**
* @param Product $product
* @param integer $totalItems
* @return float
*/
public function calculate(Product $product, $totalItems): float
{
return $this->calculateFullPrice($product, $totalItems) - $this->calculateDiscount($product, $totalItems);
}
/**
* @param Product $product
* @param $totalItems
* @return float
*/
private function calculateFullPrice(Product $product, $totalItems): float
{
return $product->getPrice() * $totalItems;
}
/**
* @param Product $product
* @param $totalItems
* @return float
*/
private function calculateDiscount(Product $product, $totalItems): float
{
return $product->getPrice() * floor($totalItems/static::MIN_ITEMS_REQUIRED_FOR_DISCOUNT);
}
}
篮子看起来像下面
class Basket
{
/**
* @param Product[] $products
*
* @return float
*/
public function calculateProductTotal(Product ...$products): float
{
$totalItemsOfEachProduct = $this->getTotalItemsOfEachProductInTheBasket(...$products);
$items = $this->getDistinctProductsInTheBasketWithQuantity(...$products);
$total = 0;
foreach ($items as $productCode => $item) {
/** @var BasketPriceCalculator $priceCalculator */
$priceCalculator = PriceCalculatorFactory::getInstance($productCode);
$total += $priceCalculator->calculate($item, $totalItemsOfEachProduct[$productCode]);
}
return $total;
}
/**
* @param Product[] $products
*
* @return array
*/
private function getTotalItemsOfEachProductInTheBasket(Product ...$products): array
{
$totalItemsPerProductCode = array_map(function ($product) { return $product->getCode(); }, $products);
return array_count_values($totalItemsPerProductCode);
}
/**
* @param Product[] $products
*
* @return array
*/
private function getDistinctProductsInTheBasketWithQuantity(Product ...$products): array
{
$items = [];
foreach ($products as $product) {
if (!array_key_exists($product->getCode(), $items)) {
$items[$product->getCode()] = $product;
}
}
return $items;
}
}
在我看来,这里有两个独立的概念:打折产品(例如 -30% 没有自定义条件)和交易。这意味着,您的购物篮包含产品集合和交易集合。
每次“处理”篮子(调用它getTotalPrice()
,listProducts()
或者getTotalSaving()
您将交易应用于产品。“处理”不应影响篮子的内部产品集合,而只会影响返回的集合/结果。
每笔交易都包含两个方面:条件(或规则)和奖励。您将使用该规则来确定是否应将给定的交易添加到用户的购物篮中。
public function addProduct($product)
{
for ($this->deals as $deal) {
if ($this->basket->willMatch($product, $deal) {
$this->basket->addDeal($deal);
}
}
$this->basket->addProduct($product);
}
当检查是否成交要添加的需求,你需要比较的“真实名单”的产品,已经是篮子里面,再加上“引入的项目”对规则一的交易。
至于将任何交易添加到篮子中,您只需要确保篮子内的所有交易都是唯一的。
当您从购物篮中移除商品时,购物篮应重新检查所有交易的规则是否仍然匹配并丢弃过时的规则。
当您请求从用户的篮子中列出 *products 时,您应用所有交易的奖励函数,其中每个交易返回一个新的产品列表,而不会影响篮子的“真实列表”。
注意:这也意味着,您需要实际购买
clone
产品,否则您将无法使用奖励来应用折扣(因为否则,由于传递处理程序,折扣也将应用于“真实列表”项目行为)
这样,交易就可以应用而无需重新检查它们是否真正符合规则。您只需运行奖励,例如,为每个充电器添加一根免费的 USB 电缆,并在listProducts()
结果中添加 2 根电缆。
至于你如何具体定义交易,这取决于你。它们可以由您的销售 CMS 制作,也可以硬编码为不同的类,也可以混合使用。它并不真正影响上述方法。
所以,我上面提到的条件“硬编码处理”的例子(关于充电器的那个):
namespace Market\Deal;
use Market\Repository\Product as Stock;
use Market\Entity\Product;
use Market\Entity\ProductCollection;
class ChargetExtras implements Applicable
{
private $repository;
public function __construct(Stock $repsoitory)
{
$this->repository = $repository;
}
public function canApply(Product $next, ProductCollection $current): bool
{
$predicted = new ProductCollection;
$predicted->copyFrom($current);
$predicted->add($next);
return $predicted->has(new Product(1515)) >= 2 &&
$predicted->has(new Product(48)) >= 1;
// here 1515 is ID for cables and 48 - for charger
}
public function apply(ProductCollection $current): ProductCollection
{
$next = new PdocutCollectionl
$next->copyFrom($current);
$count = min(
(int) $current->has(new Product(1515))/2,
$current->has(new Product(48))
);
// for each combination of 2 cables and 1 charger, add free cable
while ($count--) {
$gratis = $this->repository->get(1515);
$gratis->setPrice(0);
$next->add($gratis);
}
return $next;
}
}
该canApply()
方法是规则,其用于检查,该交易是否可以被应用,并且奖赏包含在apply()
方法。
你会打电话给他们无论是从内部的Basket
阶层,通过将产品的“真正的名单”。如您所见,这笔交易在任何时候都不会影响原始列表。它始终适用于副本。
而且,由于每笔交易中的逻辑相对简单,您可以创建一些“动态规则”类,该类使用在您的 CMS 中定义的条件。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句