Php 我如何处理一个购物篮,其中有很多产品的成本计算可能不同
我在做一项任务,我至少有一些产品遇到了问题,比如买一送一,买四送一,买五送八。现在我想知道我该怎么处理这样的问题,因为当我遇到更多的产品时,产品计算的代码库就会失控。这里最好的解决方案是什么?目前我所做的是基于每个产品代码创建一个类,并且产品的计算是该文件的本地计算。对象构造是通过静态工厂方法完成的。任何关于算法改进的建议,如果我能进一步减少代码并使其简单化,都将是非常好的Php 我如何处理一个购物篮,其中有很多产品的成本计算可能不同,php,algorithm,design-patterns,architecture,Php,Algorithm,Design Patterns,Architecture,我在做一项任务,我至少有一些产品遇到了问题,比如买一送一,买四送一,买五送八。现在我想知道我该怎么处理这样的问题,因为当我遇到更多的产品时,产品计算的代码库就会失控。这里最好的解决方案是什么?目前我所做的是基于每个产品代码创建一个类,并且产品的计算是该文件的本地计算。对象构造是通过静态工厂方法完成的。任何关于算法改进的建议,如果我能进一步减少代码并使其简单化,都将是非常好的 class BOGOFCalculator implements BasketPriceCalculator {
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()
或getTotalSaveing()
)时,您都会将交易应用于产品。该“处理”不应影响篮子的内部产品集合,而只应影响返回的集合/结果
每笔交易都包含两个方面:条件(或规则)和奖励。您可以使用规则来确定是否应该将给定的交易添加到用户的购物篮中
在检查是否需要添加交易时,您需要将篮子中已经存在的产品的“真实列表”与“进货”与交易规则进行比较
至于向篮子中添加任何交易,您只需确保篮子中的所有交易都是唯一的
当您从购物篮中移除一个物品时,购物篮应该重新检查所有交易的规则是否仍然匹配,并丢弃过时的规则
当您请求列出用户购物篮中的*产品时,您会应用所有交易的奖励功能,每个交易都会返回一个新的产品列表,而不会影响购物篮的“真实列表”
注意:这也意味着您需要实际克隆产品,因为否则您不能使用奖励来应用折扣(因为否则,由于过路处理程序行为,折扣也会应用于“真实列表”项目)
这样,交易就可以应用,而无需重新检查它们是否符合规则。您只需运行奖励,例如,为每个充电器添加一根免费USB电缆,并在listProducts()
结果中添加两根电缆
至于您如何具体定义交易,则取决于您。它们可以由您的销售CMS生成,也可以硬编码为不同的类,或者您可以将它们混合。这并不真正影响上述方法
更新
因此,我在上面提到的关于条件的“硬编码交易”示例(关于充电器的示例):
canApply()
方法是用于检查交易是否可以应用以及奖励是否包含在apply()
方法中的规则
您可以通过传递产品的“真实列表”从中的调用Basket
类中的这两个。正如您所看到的,交易在任何时候都不会影响原始列表。它总是在副本上起作用
而且,由于每笔交易的逻辑相对简单,您可以创建一些使用CMS中定义的条件的“动态规则”类。在我看来,这里有两个独立的概念:折扣产品(比如-30%,没有自定义条件)还有交易。这意味着,你的篮子里有产品集合和交易集合 每次“处理”篮子(调用它
getTotalPrice()
、listProducts()
或getTotalSaveing()
)时,您都会将交易应用于产品。该“处理”不应影响篮子的内部产品集合,而只应影响返回的集合/结果
每笔交易都包含两个方面:条件(或规则)和奖励。您可以使用规则来确定是否应该将给定的交易添加到用户的购物篮中
在检查是否需要添加交易时,您需要将篮子中已经存在的产品的“真实列表”与“进货”与交易规则进行比较
至于向篮子中添加任何交易,您只需确保篮子中的所有交易都是唯一的
当您从购物篮中移除一个物品时,购物篮应该重新检查所有交易的规则是否仍然匹配,并丢弃过时的规则
当您请求列出用户购物篮中的*产品时,您会应用所有交易的奖励功能,每个交易都会返回一个新的产品列表,而不会影响购物篮的“真实列表”
注意:这也意味着您需要实际克隆产品,因为否则您不能使用奖励来应用折扣(因为否则,由于过路处理程序行为,折扣也会应用于“真实列表”项目)
这样,交易就可以应用,而无需重新检查它们是否符合规则。您只需运行奖励,例如,为每个充电器添加一根免费USB电缆,并在listProducts()
结果中添加两根电缆
至于您如何具体定义交易,则取决于您。它们可以由您的销售CMS生成,也可以硬编码为不同的类,或者您可以将它们混合。这并不真正影响上述方法
更新
因此,我在上面提到的关于条件的“硬编码交易”示例(关于充电器的示例):
canApply()
方法是用于检查的规则
public function addProduct($product)
{
for ($this->deals as $deal) {
if ($this->basket->willMatch($product, $deal) {
$this->basket->addDeal($deal);
}
}
$this->basket->addProduct($product);
}
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;
}
}