这里写目录标题
- 背景
- 原因
- 抽象过程
- 第一次抽象
- 第二次抽象
- 第三次抽象
- 第四次抽象
- 简单工厂
- 反射
背景
学生信息管理系统中有很多文本框的校验,其中有一点,就是不允许输入过长的信息。
原因
1、文本框校验限制输入长度确保用户内容不超出系统或数据库容量限制,避免数据溢出和系统性能下降。
2、限制输入长度有助于节省服务器和数据库资源,避免长篇文本消耗过多存储和计算资源。
3、提高系统性能和响应速度是限制输入长度的目的之一。
4、限制输入长度可以减少用户输入错误和不必要的长篇内容,提升用户体验。
5、短文本相比长文本在网络传输时更加高效,减少带宽和传输时间,节省网络流量和传输延迟。
抽象过程
第一次抽象
先来看代码
Public Sub LimitTextBoxLength(txtBox As TextBox, maxLength As Integer)
'限制文本框长度If Len(txtBox.Text) > maxLength ThentxtBox.Text = Left(txtBox.Text, maxLength)MsgBox "请输入内容少于 " & maxLength & " 个字符。"End If
End Sub
这种做法就是将用户输入的内容截取成限制的字符长度,比如限制的字符长度为10,用户输入100个字符,输入结束,一点回车,直接被截成10个字符,用户会心里说一句“逗我玩呢”,这种多少有些不尊重用户,用户体验感极差。
后面根据需求更改了代码
第二次抽象
这版还增加了一点功能,对输入内容进行限制
Private Sub txtName_keypress(keyAscii As Integer)' 允许汉字、字母、数字、空格If Not ((keyAscii >= -20319 And keyAscii <= -3652) Or (keyAscii >= 48 And keyAscii <= 57) Or (keyAscii >= 65 And keyAscii <= 90) Or (keyAscii >= 97 And keyAscii <= 122) Or keyAscii = 32 Or keyAscii = 8) ThenkeyAscii = 0MsgBox "您输入的不是有效字符", 0, "温馨提示"End If' 判断名字长度If (Len(txtName.Text) > 10) ThenMsgBox "姓名字符太长", 0, "温馨提示"txtName.SetFocustxtName.Text = ""Exit SubEnd If
End Sub
这段代码是对输入学生信息时,姓名文本框的限制
然后发现除了姓名文本框需要限制,学号也需要限制,就想把这段代码抽出来复用,要实现代码的复用,可以将这段逻辑封装成一个通用的函数,然后在每个文本框的 KeyPress 事件中调用这个函数。
第三次抽象
首先,创建一个新的模块(Module),在其中定义一个通用的函数,用于检查输入字符的有效性和长度限制:
Public Function CheckInputValidity(txtBox As Object, keyAscii As Integer, maxLength As Integer) As Boolean' 允许汉字、字母、数字、空格If Not ((keyAscii >= -20319 And keyAscii <= -3652) Or (keyAscii >= 48 And keyAscii <= 57) Or (keyAscii >= 65 And keyAscii <= 90) Or (keyAscii >= 97 And keyAscii <= 122) Or keyAscii = 32 Or keyAscii = 8) ThenkeyAscii = 0MsgBox "您输入的不是有效字符", 0, "温馨提示"CheckInputValidity = FalseExit FunctionEnd If' 判断字符长度If (Len(txtBox.Text) > maxLength) ThenMsgBox txtBox.Name & " 字符太长", 0, "温馨提示"txtBox.SetFocustxtBox.Text = ""CheckInputValidity = FalseExit FunctionEnd IfCheckInputValidity = True
End Function
然后,在每个文本框的 KeyPress 事件中调用这个通用函数,例如:
Private Sub txtName_KeyPress(KeyAscii As Integer)Dim isValidInput As BooleanisValidInput = CheckInputValidity(txtName, KeyAscii, 10)If Not isValidInput ThenKeyAscii = 0End If
End SubPrivate Sub txtAnotherTextBox_KeyPress(KeyAscii As Integer)Dim isValidInput As BooleanisValidInput = CheckInputValidity(txtAnotherTextBox, KeyAscii, 20)If Not isValidInput ThenKeyAscii = 0End If
End Sub
通过这种方式,可以复用代码逻辑,并在需要时轻松调用通用函数来检查不同文本框的输入有效性和长度限制。
在上面提供的通用函数中,有三个参数,分别是:
txtBox As Object:这是一个对象类型的参数,用于传递文本框控件对象。通过将文本框对象作为参数传递给函数,可以在函数内部对其属性进行操作,比如获取文本框的内容、设置焦点等。
keyAscii As Integer:这是一个整数类型的参数,表示当前按下的键的 ASCII 值。在 KeyPress 事件中,可以通过这个参数获取用户按下的键的 ASCII 值,从而判断输入是否符合规定。
maxLength As Integer:这是一个整数类型的参数,表示允许输入的最大字符长度。在函数中使用这个参数来检查文本框中输入字符的长度是否超过限制。
通过将这三个参数传递给通用函数,可以使该函数具有通用性,可以用于检查不同文本框的输入有效性和长度限制,从而实现代码的复用。
第四次抽象
上面代码除了限制长度,还限制了输入的内容只允许汉字、字母、数字、空格,但是向我上面说的,如过学号只限制数字,那这段代码就没法复用,两个办法
方法一:
把复用函数变成两个函数,一个用于限制输入的内容,另一个用于限制长度。
' 限制输入内容
Public Function CheckInputContent(txtBox As Object, keyAscii As Integer) As Boolean' 允许汉字、字母、数字、空格If Not ((keyAscii >= -20319 And keyAscii <= -3652) Or (keyAscii >= 48 And keyAscii <= 57) Or (keyAscii >= 65 And keyAscii <= 90) Or (keyAscii >= 97 And keyAscii <= 122) Or keyAscii = 32 Or keyAscii = 8) ThenkeyAscii = 0MsgBox "您输入的不是有效字符", 0, "温馨提示"CheckInputContent = FalseExit FunctionEnd IfCheckInputContent = True
End Function' 限制输入长度
Public Function CheckInputLength(txtBox As Object, maxLength As Integer) As Boolean' 判断字符长度If (Len(txtBox.Text) > maxLength) ThenMsgBox txtBox.Name & " 字符太长", 0, "温馨提示"txtBox.SetFocustxtBox.Text = ""CheckInputLength = FalseExit FunctionEnd IfCheckInputLength = True
End Function
在调用这两个函数时,可以分别对应不同的需求进行调用。例如,在文本框的 KeyPress 事件中
Private Sub txtBox_KeyPress(KeyAscii As Integer)Dim isValidContent As BooleanisValidContent = CheckInputContent(txtBox, KeyAscii)Dim isValidLength As BooleanisValidLength = CheckInputLength(txtBox, 10) ' 限制最大长度为 10If Not isValidContent Or Not isValidLength ThenKeyAscii = 0End If
End Sub
通过这种方法,可以更灵活地控制输入内容和长度的限制,使代码更易于维护和扩展。
方法二:就想把这两个函数写在一起
通过传入额外参数来指定不同的限制条件
Public Function CheckInputValidity(txtBox As Object, keyAscii As Integer, maxLength As Integer, allowChars As String) As BooleanDim validChars As StringvalidChars = allowChars' 检查是否为允许的字符If InStr(validChars, Chr(keyAscii)) = 0 And keyAscii <> 8 ThenMsgBox "您输入的不是有效字符", 0, "温馨提示"CheckInputValidity = FalseExit FunctionEnd If' 检查字符长度If (Len(txtBox.Text) > maxLength) ThenMsgBox txtBox.Name & " 字符太长", 0, "温馨提示"txtBox.SetFocustxtBox.Text = ""CheckInputValidity = FalseExit FunctionEnd IfCheckInputValidity = True
End Function
在这个更新后的函数中,增加了一个名为 allowChars 的额外参数,用于指定允许输入的字符。在调用函数时,可以根据需要传入不同的 allowChars 参数来限制不同文本框的输入内容。
在每个文本框的 KeyPress 事件中调用函数时,只需要额外指定对应文本框允许的字符即可
Public Sub RestrictInput(txtBox As Object, keyAscii As Integer, maxLength As Integer, inputType As String)Select Case inputTypeCase "Letter"If Not ((keyAscii >= 65 And keyAscii <= 90) Or (keyAscii >= 97 And keyAscii <= 122) Or keyAscii = 32) ThenkeyAscii = 0MsgBox "您只能输入字母和空格", 0, "温馨提示"End IfCase "Number"If Not (keyAscii >= 48 And keyAscii <= 57) ThenkeyAscii = 0MsgBox "您只能输入数字", 0, "温馨提示"End IfCase "Chinese"If Not (keyAscii >= -20319 And keyAscii <= -3652) ThenkeyAscii = 0MsgBox "您只能输入汉字", 0, "温馨提示"End IfEnd SelectIf (Len(txtBox.Text) > maxLength) ThenMsgBox txtBox.Name & " 字符太长", 0, "温馨提示"txtBox.SetFocustxtBox.Text = ""End If
End Sub
将两个函数整合在一起,并添加了额外参数 allowChars 来指定不同文本框允许输入的字符。这样可以更灵活地控制每个文本框的输入内容和长度限制。
在每个文本框的 KeyPress 事件中调用 CheckInputValidity 函数时,可以根据需要传入不同的 allowChars 参数来限制输入内容
Private Sub txtName_KeyPress(KeyAscii As Integer)RestrictInput txtName, KeyAscii, 10, "Letter" ' 只允许输入字母和空格
End SubPrivate Sub txtNumber_KeyPress(KeyAscii As Integer)RestrictInput txtNumber, KeyAscii, 5, "Number" ' 只允许输入数字
End SubPrivate Sub txtChinese_KeyPress(KeyAscii As Integer)RestrictInput txtChinese, KeyAscii, 10, "Chinese" ' 只允许输入汉字
End Sub
简单工厂
在VB6中,没有像接口和工厂模式这样的概念,可以使用类模块来实现类似的功能,但是这里我还是使用c#来写下实现的逻辑
定义接口: 首先,定义一个接口,其中包含用于校验文本框内容的方法。这个接口可以包含多个校验方法,具体根据需求而定。
public interface ITextBoxValidator
{bool IsValid(string text);
}
创建具体的校验器类: 实现接口,创建具体的文本框校验器类。每个类都实现了校验文本框内容的方法,根据不同的需求进行不同的校验逻辑。
public class AlphaSpaceValidator : ITextBoxValidator
{public bool IsValid(string text){// 实现只允许输入字母和空格的校验逻辑}
}public class NumericValidator : ITextBoxValidator
{public bool IsValid(string text){// 实现只允许输入数字的校验逻辑}
}
创建简单工厂类: 创建一个简单工厂类,根据输入的类型来实例化具体的校验器类。
public class TextBoxValidatorFactory
{public static ITextBoxValidator CreateValidator(string type){switch (type.ToLower()){case "alphaspace":return new AlphaSpaceValidator();case "numeric":return new NumericValidator();// 可以添加更多的具体实现类型default:throw new ArgumentException("Invalid validator type");}}
}
在使用时调用简单工厂创建校验器: 在需要使用文本框校验器的地方,调用简单工厂的方法根据需求创建具体的校验器实例。
string validatorType = "alphaspace"; // 或者 "numeric" 等
ITextBoxValidator validator = TextBoxValidatorFactory.CreateValidator(validatorType);// 在文本框输入事件中调用校验器的校验方法
textBox.TextChanged += (sender, e) =>
{if (!validator.IsValid(textBox.Text)){MessageBox.Show("Invalid input!");textBox.Text = ""; // 清空文本框内容或者采取其他处理方式}
};
通过简单工厂,可以根据不同的需求创建不同的文本框校验器,实现对文本框内容的灵活校验。
反射
最后,再说一下如果想要更灵活可以使用反射,那么实现的逻辑如下
1、定义接口和具体校验器类: 定义接口 ITextBoxValidator 和多个具体的校验器类,每个校验器类实现了接口中的校验方法。
2、创建简单工厂类: 创建一个简单工厂类 TextBoxValidatorFactory,其中包含一个静态方法 CreateValidator。在该方法中,使用反射来动态实例化校验器类。
3、使用反射实例化校验器类: 在 CreateValidator 方法中,根据传入的类型名称,使用反射获取对应类型的 Type 对象,然后使用 Activator.CreateInstance 方法实例化该类型的对象,并将其转换为 ITextBoxValidator 接口类型。
4、返回校验器对象: 将实例化的校验器对象返回给调用方,使调用方可以使用校验器对象进行文本框内容的校验。
通过使用反射,可以使简单工厂更加灵活,不需要在工厂类中硬编码每个具体的校验器类,而是根据传入的类型名称动态实例化相应的校验器类。这样,在需要添加新的校验器类时,只需创建新的类并实现接口,而不需要修改工厂类的代码。
C#中具体实现反射的逻辑基本如下所示
1、获取程序集: 首先,需要获取包含要使用的类型的程序集。可以使用 Assembly.Load 方法加载程序集,或者使用已知的程序集,比如当前程序集。
2、获取类型: 一旦有了程序集,就可以从中获取所需类型。可以使用 Assembly.GetTypes 方法获取程序集中的所有类型,然后根据类型名称筛选出所需的类型。或者,如果已知类型所在的命名空间和程序集,可以直接使用 Assembly.GetType 方法获取指定类型的 Type 对象。
3、实例化对象: 一旦获取了类型的 Type 对象,就可以使用 Activator.CreateInstance 方法实例化该类型的对象。可以通过调用 CreateInstance 方法并传递类型的 Type 对象,来创建类型的实例。
public class TextBoxValidatorFactory
{public static ITextBoxValidator CreateValidator(string typeName){// 获取当前程序集Assembly assembly = Assembly.GetExecutingAssembly();// 获取指定类型的 Type 对象Type validatorType = assembly.GetType(typeName);if (validatorType == null || !typeof(ITextBoxValidator).IsAssignableFrom(validatorType)){throw new ArgumentException("Invalid validator type");}// 使用 Activator.CreateInstance 实例化对象并转换为 ITextBoxValidator 接口类型return (ITextBoxValidator)Activator.CreateInstance(validatorType);}
}
在这段代码中,首先使用 Assembly.GetExecutingAssembly() 获取当前程序集。然后使用 assembly.GetType(typeName) 根据类型名称获取对应的 Type 对象。接着,通过检查获取的类型是否为 ITextBoxValidator 接口的实现,来确保类型的正确性。最后,使用 Activator.CreateInstance 实例化类型的对象,并将其转换为 ITextBoxValidator 接口类型,然后返回给调用方。通过反射来动态实例化任意类型的对象,从而实现灵活的对象创建。