gpt4 book ai didi

c# - 验证的最佳位置在哪里...构造函数或留给客户调用?

转载 作者:行者123 更新时间:2023-11-30 22:20:22 25 4
gpt4 key购买 nike

这确实是一个通用的(也可能是一个更主观的)问题。我在一些类中使用接口(interface)来定义验证对象状态的标准方法。当我这样做的时候,我开始挠头了……最好是1.) 允许构造函数(或初始化方法)自动过滤掉错误信息或...2.) 允许客户端实例化对象并让客户端在继续之前也调用接口(interface)的 IsValid 属性或 Validate() 方法?

基本上,一种方法是无声的,但可能会产生误导,因为客户可能不知道某些信息由于不符合验证标准而被过滤掉了。另一种方法会更直接,但还会增加一两步?这里有什么典型的?

好的,经过漫长的一天努力跟上其他一些事情,我终于想出了一个例子。请为我代劳,因为它并不理想,也绝不是什么美妙的东西,但希望它能很好地发挥作用,让人们明白这一点。我目前的项目太复杂了,无法为此做一些简单的事情,所以我编了一些东西……相信我……完全编好了。

好了,例子中的对象是这样的:

客户端:代表客户端代码(控制台应用顺便说一下)

IValidationInfo:这是我在当前项目中使用的实际界面。它允许我为不一定供客户端使用的“后端”对象创建验证框架,因为业务逻辑可能足够复杂。这也让我能够根据业务逻辑的需要分离验证代码和调用。

OrderManager:这是一个客户端代码可以用来管理订单的对象。可以说这是对客户友好的。

OrderSpecification:这是客户端代码可用于请求订单的对象。但是,如果业务逻辑无法解决,则可以引发异常(或者如果有必要,不添加订单并忽略异常......)在我的真实示例中,我实际上有一个不那么黑的对象 -白色的是这个栅栏的哪一侧......因此当我意识到我可以将验证请求(调用 IsValid 或 Validate())推送到 cilent 时,我最初的问题。

CustomerDescription:代表我分类的客户(假装是从数据库中读取的。

产品:表示也被分类的特定产品。

OrderDescription:代表官方订单请求。业务规则是客户不能订购任何他们未分类的东西(我知道..这不是很现实,但它给了我一些工作...)

好的...我刚刚意识到我无法在此处附加文件,所以这是代码。我为它的冗长外观道歉。这是我使用验证接口(interface)创建对客户友好的前端和业务逻辑后端所能做的最好的事情:

公共(public)类客户端 { static OrderManager orderMgr = new OrderManager();

    static void Main(string[] args)
{
//Request a new order
//Note: Only the OrderManager and OrderSpecification are used by the Client as to keep the
// Client from having to know and understand the framework beyond that point.
OrderSpecification orderSpec = new OrderSpecification("Customer1", new Product(IndustryCategory.FoodServices, "Vending Items"));
orderMgr.SubmitOrderRequest(orderSpec);
Console.WriteLine("The OrderManager has {0} items for {1} customers.", orderMgr.ProductCount, orderMgr.CustomerCount);

//Now add a second item proving that the business logic to add for an existing customer works
Console.WriteLine("Adding another valid item for the same customer.");
orderSpec = new OrderSpecification("Customer1", new Product(IndustryCategory.FoodServices, "Sodas"));
orderMgr.SubmitOrderRequest(orderSpec);
Console.WriteLine("The OrderManager now has {0} items for {1} customers.", orderMgr.ProductCount, orderMgr.CustomerCount);

Console.WriteLine("Adding a new valid order for a new customer.");
orderSpec = new OrderSpecification("Customer2", new Product(IndustryCategory.Residential, "Magazines"));
orderMgr.SubmitOrderRequest(orderSpec);
Console.WriteLine("The OrderManager now has {0} items for {1} customers.", orderMgr.ProductCount, orderMgr.CustomerCount);


Console.WriteLine("Adding a invalid one will not work because the customer is not set up to receive these kinds of items. Should get an exception with message...");
try
{
orderSpec = new OrderSpecification("Customer3", new Product(IndustryCategory.Residential, "Magazines"));
orderMgr.SubmitOrderRequest(orderSpec);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

Console.ReadLine();
}
}

public interface IValidationInfo
{
string[] ValidationItems { get; }

bool IsValid { get; }

void Validate();

List<string> GetValidationErrors();

string GetValidationError(string itemName);
}

public class OrderManager
{
private List<OrderDescription> _orders = new List<OrderDescription>();
public List<OrderDescription> Orders
{
get { return new List<OrderDescription>(_orders); }
private set { _orders = value; }
}

public int ProductCount
{
get
{
int itemCount = 0;
this.Orders.ForEach(o => itemCount += o.Products.Count);
return itemCount;
}
}

public int CustomerCount
{
get
{
//since there's only one customer per order, just return the number of orders
return this.Orders.Count;
}
}

public void SubmitOrderRequest(OrderSpecification orderSpec)
{
if (orderSpec.IsValid)
{
List<OrderDescription> orders = this.Orders;

//Since the particular customer may already have an order, we might as well add to an existing
OrderDescription existingOrder = orders.FirstOrDefault(o => string.Compare(orderSpec.Order.Customer.Name, o.Customer.Name, true) == 0) as OrderDescription;
if (existingOrder != null)
{
List<Product> existingProducts = orderSpec.Order.Products;
orderSpec.Order.Products.ForEach(p => existingOrder.AddProduct(p));
}
else
{
orders.Add(orderSpec.Order);
}
this.Orders = orders;
}
else
orderSpec.Validate(); //Let the OrderSpecification pass the business logic validation down the chain
}
}


public enum IndustryCategory
{
Residential,
Textile,
FoodServices,
Something
}


public class OrderSpecification : IValidationInfo
{
public OrderDescription Order { get; private set; }


public OrderSpecification(string customerName, Product product)
{
//Should use a method in the class to search and retrieve Customer... pretending here
CustomerDescription customer = null;
switch (customerName)
{
case "Customer1":
customer = new CustomerDescription() { Name = customerName, Category = IndustryCategory.FoodServices };
break;
case "Customer2":
customer = new CustomerDescription() { Name = customerName, Category = IndustryCategory.Residential };
break;
case "Customer3":
customer = new CustomerDescription() { Name = customerName, Category = IndustryCategory.Textile };
break;
}


//Create an OrderDescription to potentially represent the order... valid or not since this is
//a specification being used to request the order
this.Order = new OrderDescription(new List<Product>() { product }, customer);

}

#region IValidationInfo Members
private readonly string[] _validationItems =
{
"OrderDescription"
};
public string[] ValidationItems
{
get { return _validationItems; }
}

public bool IsValid
{
get
{
List<string> validationErrors = GetValidationErrors();
if (validationErrors != null && validationErrors.Count > 0)
return false;
else
return true;
}
}

public void Validate()
{
List<string> errorMessages = GetValidationErrors();
if (errorMessages != null && errorMessages.Count > 0)
{
StringBuilder errorMessageReported = new StringBuilder();
errorMessages.ForEach(em => errorMessageReported.AppendLine(em));
throw new Exception(errorMessageReported.ToString());
}
}

public List<string> GetValidationErrors()
{
List<string> errorMessages = new List<string>();
foreach (string item in this.ValidationItems)
{
string errorMessage = GetValidationError(item);
if (!string.IsNullOrEmpty(errorMessage))
errorMessages.Add(errorMessage);
}

return errorMessages;
}

public string GetValidationError(string itemName)
{
switch (itemName)
{
case "OrderDescription":
return ValidateOrderDescription();
default:
return "Invalid item name.";
}
}

#endregion

private string ValidateOrderDescription()
{
string errorMessage = string.Empty;

if (this.Order == null)
errorMessage = "Order was not instantiated.";
else
{
if (!this.Order.IsValid)
{
List<string> orderErrors = this.Order.GetValidationErrors();
orderErrors.ForEach(ce => errorMessage += "\n" + ce);
}
}

return errorMessage;
}

}

public class CustomerDescription : IValidationInfo
{
public string Name { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public int ZipCode { get; set; }
public IndustryCategory Category { get; set; }

#region IValidationInfo Members
private readonly string[] _validationItems =
{
"Name",
"Street",
"City",
"State",
"ZipCode",
"Category"
};
public string[] ValidationItems
{
get { return _validationItems; }
}

public bool IsValid
{
get
{
List<string> validationErrors = GetValidationErrors();
if (validationErrors != null && validationErrors.Count > 0)
return false;
else
return true;
}
}

public void Validate()
{
List<string> errorMessages = GetValidationErrors();
if (errorMessages != null && errorMessages.Count > 0)
{
StringBuilder errorMessageReported = new StringBuilder();
errorMessages.ForEach(em => errorMessageReported.AppendLine(em));
throw new Exception(errorMessageReported.ToString());
}
}

public List<string> GetValidationErrors()
{
List<string> errorMessages = new List<string>();
foreach (string item in this.ValidationItems)
{
string errorMessage = GetValidationError(item);
if (!string.IsNullOrEmpty(errorMessage))
errorMessages.Add(errorMessage);
}

return errorMessages;
}

public string GetValidationError(string itemName)
{
//Validation methods should be called here... pretending nothings wrong for sake of discussion & simplicity
switch (itemName)
{
case "Name":
return string.Empty;
case "Street":
return string.Empty;
case "City":
return string.Empty;
case "State":
return string.Empty;
case "ZipCode":
return string.Empty;
case "Category":
return string.Empty;
default:
return "Invalid item name.";
}
}

#endregion
}


public class Product
{
public IndustryCategory Category { get; private set; }
public string Description { get; private set; }

public Product(IndustryCategory category, string description)
{
this.Category = category;
this.Description = description;
}
}







public class OrderDescription : IValidationInfo
{
public CustomerDescription Customer { get; private set; }

private List<Product> _products = new List<Product>();
public List<Product> Products
{
get { return new List<Product>(_products); }
private set { _products = value; }
}

public OrderDescription(List<Product> products, CustomerDescription customer)
{
this.Products = products;
this.Customer = customer;
}

public void PlaceOrder()
{
//If order valid, place
if (this.IsValid)
{
//Do stuff to place order
}
else
Validate(); //cause the exceptions to be raised with the validate because business rules were broken
}

public void AddProduct(Product product)
{
List<Product> productsToEvaluate = this.Products;
//some special read, validation, quantity check, pre-existing, etc here
// doing other stuff...
productsToEvaluate.Add(product);
this.Products = productsToEvaluate;
}

#region IValidationInfo Members

private readonly string[] _validationItems =
{
"Customer",
"Products"
};
public string[] ValidationItems
{
get { return _validationItems; }
}

public bool IsValid
{
get
{
List<string> validationErrors = GetValidationErrors();
if (validationErrors != null && validationErrors.Count > 0)
return false;
else
return true;
}
}

public void Validate()
{
List<string> errorMessages = GetValidationErrors();
if (errorMessages != null && errorMessages.Count > 0)
{
StringBuilder errorMessageReported = new StringBuilder();
errorMessages.ForEach(em => errorMessageReported.AppendLine(em));
throw new Exception(errorMessageReported.ToString());
}
}

public List<string> GetValidationErrors()
{
List<string> errorMessages = new List<string>();
foreach (string item in this.ValidationItems)
{
string errorMessage = GetValidationError(item);
if (!string.IsNullOrEmpty(errorMessage))
errorMessages.Add(errorMessage);
}

return errorMessages;
}

public string GetValidationError(string itemName)
{
switch (itemName)
{
case "Customer":
return ValidateCustomer();
case "Products":
return ValidateProducts();
default:
return "Invalid item name.";
}
}

#endregion

#region Validation Methods

private string ValidateCustomer()
{
string errorMessage = string.Empty;

if (this.Customer == null)
errorMessage = "CustomerDescription is missing a valid value.";
else
{
if (!this.Customer.IsValid)
{
List<string> customerErrors = this.Customer.GetValidationErrors();
customerErrors.ForEach(ce => errorMessage += "\n" + ce);
}
}

return errorMessage;
}

private string ValidateProducts()
{
string errorMessage = string.Empty;

if (this.Products == null || this.Products.Count <= 0)
errorMessage = "Invalid Order. Missing Products.";
else
{
foreach (Product product in this.Products)
{
if (product.Category != Customer.Category)
{
errorMessage += string.Format("\nThe Product, {0}, category does not match the required Customer category for {1}", product.Description, Customer.Name);
}
}
}
return errorMessage;
}
#endregion
}

最佳答案

如果信息有效,您有什么理由不希望构造函数大声抛出异常?根据我的经验,最好避免创建处于无效状态的对象。

关于c# - 验证的最佳位置在哪里...构造函数或留给客户调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15032808/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com