使用构建器模式的 List<T> - C#

Working with List<T> with builder pattern - c#

为标题道歉,因为我无法完全表达我的问题,所以我们开始吧。

我有一个 DiscountBuilder class,它有两个方法 WithBuyOneGetOneFreeWithDiscount。他们都采用 List<ShoppingItem> 并且像这样孤立地工作:

var items = BasketStore.ShoppingItems;

var updatedItems = new DiscountBuilder()
    .WithBuyOneGetOneFree(items)
    .Create();
    
var updatedItems2 = new DiscountBuilder()
    .WithDiscount(75, items)
    .Create();

但是,当我尝试将这两种方法链接起来以应用 WithBuyOneGetOneFreeWithDiscount 时,我只获得了 WithBuyOneGetOneFree 返回的购物清单,这是完全合乎逻辑且可以理解的:

var updatedItems3 = new DiscountBuilder()
    .WithDiscount(75, items)
    .WithBuyOneGetOneFree(items)
    .Create();

什么是最好的 way/design 实现这个所以上面所有三个例子都完全和谐地工作,所以我可以选择像现在这样单独使用方法,并将这些方法链接在一起以获得两个折扣?

这是代码的其余部分。

DiscountBuilder:

#region Private Fields
private List<ShoppingItem> internalShoppingItems;
#endregion

#region Public Methods
public List<ShoppingItem> Create()
{
    return internalShoppingItems;
}

public IDiscountBuilder WithBuyOneGetOneFree(List<ShoppingItem> shoppingItems)
{
    internalShoppingItems = new List<ShoppingItem>();

    foreach(var item in shoppingItems)
    {
        var updateItem = new ShoppingItem
        {
            Id = item.Id,
            Price = item.Price,
            Name = item.Name,
            Quantity = item.Quantity + 1
        };

        internalShoppingItems.Add(updateItem);
    }
    return this;
}

public IDiscountBuilder WithDiscount(int percentage, List<ShoppingItem> shoppingItems)
{
    internalShoppingItems = new List<ShoppingItem>();

    foreach (var item in shoppingItems)
    {
        var updateItem = new ShoppingItem
        {
            Id = item.Id,
            Price = GetDiscountedPrice(percentage, item.Price),
            Name = item.Name,
            Quantity = item.Quantity
        };

        internalShoppingItems.Add(updateItem);
    }
    return this;
}
#endregion

购物项目:

public class ShoppingItem
{
    public int Id { get; set; }
    public double Price { get; set; }
    public string Name { get; set; }
    public int Quantity { get; set; }
}

BasketStore(原始虚拟数据的来源):

public static class BasketStore
{
    #region Properties
    public static List<ShoppingItem> ShoppingItems { get; private set; }
    #endregion

    #region Constructor
    static BasketStore()
    {
        ShoppingItems = new List<ShoppingItem>()
        {
            new ShoppingItem {
                Id = 1,
                Price = 10.0,
                Name = "IWatch",
                Quantity = 1
            },
            new ShoppingItem {
                Id = 5,
                Price = 9.99,
                Name = "Ladies Watch",
                Quantity = 3
            },
            new ShoppingItem {
                Id = 7,
                Price = 1.75,
                Name = "Ladies Replacement Strap",
                Quantity = 1
            }
        };
    }
    #endregion
}

这可能是经典 XY problem。问题是每个流畅的动作都覆盖了内部列表,因此最后应用的折扣获胜并更改了列表。

最佳way/design而言,解决这个问题的方法太多了。

这是一个简单的例子。

更改存储内部列表并覆盖它的方法。

public interface IDiscountBuilder {
    IDiscountBuilder WithBuyOneGetOneFree();
    IDiscountBuilder WithDiscount(int percentage);
    List<ShoppingItem> Create(List<ShoppingItem> shoppingItems);
}

改为存储要应用于列表中的项目的操作,然后根据提供的输入创建列表。

例如

public class DiscountBuilder : IDiscountBuilder {
    #region Private Fields        
    private Action<ShoppingItem> buyOneGetOneFree;
    private Action<ShoppingItem> withDiscount;
    #endregion

    #region Public Methods
    public List<ShoppingItem> Create(List<ShoppingItem> shoppingItems) {
        List<ShoppingItem> result = new List<ShoppingItem>();
        foreach (ShoppingItem item in shoppingItems) {
            //copy item details
            ShoppingItem updateItem = new ShoppingItem() {
                Id = item.Id,
                Price = item.Price,
                Name = item.Name,
                Quantity = item.Quantity
            };
            //apply actions
            buyOneGetOneFree?.Invoke(updateItem);
            withDiscount?.Invoke(updateItem);
            result.Add(updateItem);
        }
        return result;
    }

    public IDiscountBuilder WithBuyOneGetOneFree() {
        buyOneGetOneFree = item => {
            item.Quantity = item.Quantity + 1;
        };
        return this;
    }

    public IDiscountBuilder WithDiscount(int percentage) {
        withDiscount = item => {
            item.Price = GetDiscountedPrice(percentage, item.Price);
        };
        return this;
    }

    private double GetDiscountedPrice(int percentage, double price) {
        return price - (price * (percentage / 100D));
    }
    #endregion
}

原始问题中的所有 3 个示例都可以相互独立应用

var items = BasketStore.ShoppingItems;

var updatedItems = new DiscountBuilder()
    .WithBuyOneGetOneFree()
    .Create(items);

var updatedItems2 = new DiscountBuilder()
    .WithDiscount(75)
    .Create(items);

var updatedItems3 = new DiscountBuilder()
    .WithDiscount(75)
    .WithBuyOneGetOneFree()
    .Create(items);

可以通过多种方式进一步扩展此方法,但这超出了原始问题的范围。