在继续之前,验证一堆变量的有效方法是什么?
本文关键字:变量 一堆 有效 方法 是什么 继续 验证 | 更新日期: 2023-09-27 18:07:47
我已经阅读了关于方法的理想大小和单一职责原则,然后我去看看我的一些代码。我觉得我可以把我的很多东西(>90%)分解成小的可管理的方法,但然后我要验证一个数据或一个表单。它总是看起来又大又臃肿。我倾向于用嵌套的if语句验证数据,并尝试在每个级别捕获错误或问题。但当我开始得到6、8、10级以上的验证时,这就非常麻烦了。但是我不知道怎样把它分解才能更有效。
下面是一个我认为很麻烦但不知道如何改进的例子。每个级别都有一个与之相关联的唯一动作,只有当所有条件都返回true时,整个东西才能返回true
,但这很难阅读,特别是在一个月左右回到程序后。
if (InitialUsageSettings.zeroed || sender.Equals(btnZero))
{
if (InitialUsageSettings.StandardFilterRun || sender.Equals(btnStandard))
{
if (InitialUsageSettings.ReferenceFilterRun || sender.Equals(btnReference) || sender.Equals(btnStandard))
{
if (InitialUsageSettings.PrecisionTestRun || sender.Equals(btnPrecision) || sender.Equals(btnReference) || sender.Equals(btnStandard))
{
if (txtOperatorID.Text.Length > 0 && cboProject.Text.Length > 0 && cboFilterType.Text.Length > 0 && cboInstType.Text.Length > 0)
{
if (txtFilterID.Text.Length > 0 && txtLot.Text.Length > 0)
{
return true;
}
else
{
if (txtFilterID.Text.Length == 0)
{
//E
}
if (txtLot.Text.Length == 0)
{
//D
}
}
}
else
{
if (txtOperatorID.Text.Length == 0)
{
//A
}
if (cboProject.Text.Length == 0)
{
//B
}
if (cboFilterType.Text.Length == 0)
{
//C
}
if (cboInstType.Text.Length == 0)
{
//D
}
//return false;
}
}
else
{
outputMessages.AppendLine("Please correct the folloring issues before taking a reading: X");
}
}
else
{
outputMessages.AppendLine("Please correct the folloring issues before taking a reading: Y");
}
}
else
{
outputMessages.AppendLine("Please correct the folloring issues before taking a reading: Z");
}
}
else
{
outputMessages.AppendLine("Please correct the folloring issues before taking a reading: A");
}
如果您的主要目的是将方法分解成可管理的块,那么您可以将每个if
块封装在自己的方法中。例如:
if (InitialUsageSettings.zeroed || sender.Equals(btnZero))
{
ValidateStandardFilter();
}
else
{
outputMessages.AppendLine("Please correct the folloring issues before taking a reading: A");
}
但是在我看来,这个方法有太多的责任:你试图使它验证并输出消息。相反,该方法应该单独负责验证。
public ValidationResult Validate(Sender sender)
{
if (!(InitialUsageSettings.zeroed || sender.Equals(btnZero)))
{
return ValidationResult.Error("A");
}
if (!(InitialUsageSettings.StandardFilterRun || sender.Equals(btnStandard)))
{
return ValidationResult.Error("Z");
}
// Etc...
if (txtOperatorID.Text.Length == 0)
{
errors.Add("A");
}
if (cboProject.Text.Length == 0)
{
errors.Add("B");
}
if (cboFilterType.Text.Length == 0)
{
errors.Add("C");
}
if (cboInstType.Text.Length == 0)
{
errors.Add("D");
}
if(errors.Count > 0)
{
return ValidationResult.Errors(errors);
}
if (txtFilterID.Text.Length == 0)
{
errors.Add("E");
}
if (txtLot.Text.Length == 0)
{
errors.Add("D");
}
return errors.Count > 0
? ValidationResult.Errors(errors)
: ValidationResult.Success();
}
然后调用代码可以考虑输出:
var result = Validate(sender);
if (result.IsError)
{
outputMessages.AppendLine("Please correct...: " + result.Issue);
}
要了解ValidationResult
类可能是什么样子,请参阅我的回答。
上面的代码可以进一步重构以减少重复:
public ValidationResult Validate(Sender sender)
{
if (!(InitialUsageSettings.zeroed || sender.Equals(btnZero)))
{
return ValidationResult.Error("A");
}
if (!(InitialUsageSettings.StandardFilterRun || sender.Equals(btnStandard)))
{
return ValidationResult.Error("Z");
}
// Etc...
var firstErrorBatch = GetEmptyStringErrors(
new[]{
new InputCheckPair(txtOperatorID, "A"),
new InputCheckPair(cboProject, "B"),
new InputCheckPair(cboFilterType, "C"),
new InputCheckPair(cboInstType, "D"),
})
.ToList();
if(firstErrorBatch.Count > 0)
{
return ValidationResult.Errors(firstErrorBatch);
}
var secondErrorBatch = GetEmptyStringErrors(
new[]{
new InputCheckPair(txtFilterID, "E"),
new InputCheckPair(txtLot, "D"),
})
.ToList();
return secondErrorBatch.Count > 0
? ValidationResult.Errors(secondErrorBatch)
: ValidationResult.Success();
}
private class InputCheckPair
{
public InputCheckPair(TextBox input, string errorIfEmpty)
{
Input = input;
ErrorIfEmpty = errorIfEmpty;
}
public TextBox Input {get; private set;}
public string ErrorIfEmpty{get; private set;}
}
public IEnumerable<string> GetEmptyStringErrors(IEnumerable<InputCheckPair> pairs)
{
return from p in pairs where p.Input.Text.Length == 0 select p.ErrorIfEmpty;
}
类似于
if(errorCondition1)
errors.add(message1);
if(errorCondition2)
errors.add(message2);
return errors.Count == 0;
所以每个条件不是嵌套的
你可以用Guard子句来代替if语句。
反向流。而不是
If(cond) {
if(someothercond) {
//great sucess!
return true;
} else {
// handle
return false;
}
} else {
// handle
return false;
}
:
if(!cond1) {
// handle
return false;
}
if(!someothercond) {
// handle
return false;
}
// great sucess!
return true;
一种方法是在执行其他代码之前调用验证方法。
例如:private String ValidateThis() {
StringBuilder result = new StringBuilder();
if (!cond1) {
result.AppendLine("error on cond1");
}
if (!cond2) {
result.AppendLine("error on cond2");
}
return result.ToString();
}
public void ButtonClick(object sender) {
String isValid = ValidateThis();
if (!String.IsNullOrEmpty(isValid)) {
// set your error message
outputMessages.AppendLine(isValid);
return;
}
// ... perform your other operations.
}
我会尝试将每个验证定义为谓词,就像这样…
delegate bool Validator(object sender, out string message);
然后你可以根据需要把它们串在一起
有很多方法可以解决这个问题。您确实希望限制重复代码的数量,例如添加输出消息的代码,它在四个或更多地方几乎相同。
如果您将这些嵌套的if…else
块视为一个序列,一旦其中一个失败,您就采取行动并停止进一步处理,您可以创建一个列表并利用LINQ的FirstOrDefault
功能依次处理条件列表,直到一个失败,或者如果它们都通过,您将获得null
。
创建一个对象来封装条件将有助于合并和减少重复。
下面是一个例子:
public class Validator
{
public Validator(string code, bool settingsCheck, Button button, object sender)
{
Code = code;
IsValid = sender != null && button != null && sender.Equals(button);
}
public bool IsValid { get; private set; }
public string Code { get; private set; }
}
现在,你的方法看起来更像这样:
var validationChecks = new List<Validator>
{
new Validator("A", InitialUsageSettings.zeroed, btnZero, sender),
new Validator("Z", InitialUsageSettings.StandardFilterRun, btnStandard, sender),
new Validator("Y", InitialUsageSettings.ReferenceFilterRun, btnReference, sender),
new Validator("X", InitialUsageSettings.PrecisionTestRun, btnPrecision, sender)
}
var failure = validationChecks.FirstOrDefault(check => !check.IsValid);
if (failure != null)
{
outputMessages.AppendLineFormat(
"Please correct the following issues before taking a reading: {0}", failure.Code);
return;
}
else
{
// further checks; I'm not sure what you're doing there with A-E
}