使用构建器模式的 List<T> - C#
Working with List<T> with builder pattern - c#
为标题道歉,因为我无法完全表达我的问题,所以我们开始吧。
我有一个 DiscountBuilder
class,它有两个方法 WithBuyOneGetOneFree
和 WithDiscount
。他们都采用 List<ShoppingItem>
并且像这样孤立地工作:
var items = BasketStore.ShoppingItems;
var updatedItems = new DiscountBuilder()
.WithBuyOneGetOneFree(items)
.Create();
var updatedItems2 = new DiscountBuilder()
.WithDiscount(75, items)
.Create();
但是,当我尝试将这两种方法链接起来以应用 WithBuyOneGetOneFree
和 WithDiscount
时,我只获得了 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);
可以通过多种方式进一步扩展此方法,但这超出了原始问题的范围。
为标题道歉,因为我无法完全表达我的问题,所以我们开始吧。
我有一个 DiscountBuilder
class,它有两个方法 WithBuyOneGetOneFree
和 WithDiscount
。他们都采用 List<ShoppingItem>
并且像这样孤立地工作:
var items = BasketStore.ShoppingItems;
var updatedItems = new DiscountBuilder()
.WithBuyOneGetOneFree(items)
.Create();
var updatedItems2 = new DiscountBuilder()
.WithDiscount(75, items)
.Create();
但是,当我尝试将这两种方法链接起来以应用 WithBuyOneGetOneFree
和 WithDiscount
时,我只获得了 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);
可以通过多种方式进一步扩展此方法,但这超出了原始问题的范围。