假设我有一个代表一个人的对象,使用该人的电子邮件地址的getter和setter方法。 setter方法定义可能如下所示:
setEmailAddress(String emailAddress)
{
this.emailAddress = emailAddress;
}
然后,调用person.setEmailAddress(0)
会产生类型错误,但调用person.setEmailAddress("asdf")
则不会 - 即使“asdf”绝不是有效的电子邮件地址。
根据我的经验,所谓的字符串几乎不是永远任意字符序列,对长度或格式没有限制。我想到了URI--街道地址和电话号码一样,名字也是如此......你明白了。然而,这些数据类型通常存储为“只是字符串”。
返回我的person对象,假设我像这样修改setEmailAddress()
setEmailAddress(EmailAddress emailAddress)
// ...
其中EmailAddress
是一个类......其构造函数采用电子邮件地址的字符串表示形式。我有什么收获吗?
好的,所以电子邮件地址是一个不好的例子。如果URI类将URI的字符串表示形式作为构造函数参数,并提供管理该URI的方法 - 设置路径,获取查询参数等。源字符串的有效性变得很重要。
所以我问你们所有人,你们如何处理具有结构的字符串?您如何在界面中明确您的结构期望?
谢谢。
答案 0 :(得分:9)
“带结构的字符串”是常见代码气味“Primitive Obsession”的症状。
补救措施是密切关注验证或操纵这些结构部分的代码中的重复。在重复的第一个提示 - 但不是之前 - 提取一个封装结构的类,并在那里找到验证和查询。
答案 1 :(得分:2)
这是一个非常常见的问题,属于标题“validation” - 有很多方法可以验证文字用户输入,其中最常见的是Regular Expressions。
您可能还会考虑使用内置的System.Net.MailAddress类,因为它为电子邮件地址提供了验证。
答案 2 :(得分:2)
欢迎来到编程世界!
我认为您的问题不是您的错误症状。相反,它是一个基本问题,它在整个编程世界中以许多形式出现。具有某种结构和含义的字符串在应用程序的不同子系统之间传递,并且每个子系统只能进行大量的解析和验证。
例如,验证电子邮件地址的问题非常棘手。例如,各种人提供接受电子邮件地址的正则表达通常要么“太紧”(不接受所有内容)或“太松散”(接受非法的事情)。 The first google hit代表'正则表达式'电子邮件地址“',例如说:
我收到的正则表达式 大多数反馈,更不用说“bug”了 报告,是你会找到的 就在这个网站的主页上: \ b [A-Z0-9 ._%+ - ] + @ [A-Z0-9 .-] +。[A-Z] {2,4} \ b分析此正则表达式 使用RegexBuddy。这个正则表达式,我 声明,匹配任何电子邮件地址。最 我反驳的反馈意见 通过显示一个电子邮件地址声明 这个正则表达式不匹配。
事实是,有效的电子邮件地址是什么或不是一个复杂的问题,一个给定的程序可能或可能不想解决的问题。 URL的问题甚至更糟,特别是考虑到恶意URL的可能性。
理想情况下,您可以使用库或系统调用来解决此类问题,而不是自己做任何事情(Microsoft Windows调用自定义对话框以允许用户选择或创建文件,因为验证文件名是另一个棘手的问题)。但是,您不能总是指望对给定的“有意义的字符串”进行适当的系统调用。
我想说没有通用的解决方案来解决带结构的字符串问题。相反,它是您在设计应用程序时出现的基本问题。在您的应用程序的 gathering requirements 过程中,您应该确定应用程序将采用哪些数据以及该数据对应用程序的意义。事情变得棘手,因为你可能会注意到应用程序可能会以你的老板或客户可能没有想到的方式增长 - 或者应用程序实际上可能以你们没有想到的方式增长。因此,应用程序需要比看起来最小但是只有一点点的 little 更灵活。它也应该不那么灵活你陷入困境。
现在,如果您决定需要验证/解释给定字符串等,将该字符串放入对象或散列可能是一个很好的方法 - 这是我知道确保您的界面清晰的一种方法。但棘手的是决定你需要多少验证或解释。
做出这些决定是一门艺术 - 没有教条式的答案可以在这里发挥作用。
答案 3 :(得分:1)
字符串是字符串。如果你需要你的字符串比普通字符串更聪明,那么将它们解析为你描述的结构对象将是一个好主意。我会使用正则表达式来做到这一点。
答案 4 :(得分:1)
在格式化字符串时,正则表达式是你的朋友。您还可以将每个部分分别存储在结构中,以避免每次要使用它们时都遇到使用正则表达式的麻烦。 e.g。
struct EMail
{
String BeforeAt = "johndoe123";
String AfterAt = "gmail.com";
}
Struct URL
{
String Protocol = "http";
String Domain = "sub.example.com";
String Path = "stuff/example.html";
}
答案 5 :(得分:0)
好吧,如果你想用EmailAddress对象做几种不同的事情,那些其他动作不必检查它是否是有效的电子邮件地址,因为EmailAddress对象保证有一个有效的字符串。您可以在构造函数中抛出异常,或者使用工厂方法或您正在使用的“One True Methodology”方法。
答案 6 :(得分:0)
就个人而言,我喜欢强打字的想法,所以如果我还在使用这种语言,我会选择第二个例子的风格。我唯一要改变的可能就是使用一个更像“类似强制转换”的结构,比如EmailAddressFromString(String)
,它会生成一个新的EmailAddress
对象(如果字符串不正确,则可以选择一个拟合)因为我是应用匈牙利符号的粉丝。
如果你感兴趣的话,乔尔在http://www.joelonsoftware.com/articles/Wrong.html中很好地涵盖了整个问题。
答案 7 :(得分:0)
我同意强烈键入对象的调用,但是对于那些从字符串解析为对象的情况,答案很简单:错误处理。
处理错误有两种常用方法:异常和返回条件。通常,如果您希望收到格式错误的数据,则应返回错误消息。对于不期望输入的情况,那么我会抛出异常。例如,您可能会传入一个生成错误的电子邮件地址,例如“bob”而不是“bob@gmail.com”。但是,对于空值,您可能会抛出异常,因为您不应该尝试从null形成电子邮件。
回到你的问题,我认为你通过将结构编码成一个对象来获得一些东西。具体来说,您只需要验证字符串是否代表某个特定位置的有效电子邮件地址,例如构造函数。在其他地方,您的代码可以自由地假设EmailAddress对象是有效的,并且您不必依赖具有“EmailHelper”等名称的狡猾类,或者其他类似的。
答案 8 :(得分:0)
在这种情况下,我个人认为不要强烈输入电子邮件地址字符串作为EmailAddress。
要创建您的电子邮件地址,您迟早会做以下事情:
EmailAddress(String email)
或设定者
SetEmailAddress(String email)
在这两种情况下,您都必须验证电子邮件字符串输入,这会让您回到初始验证问题。
正如其他人指出的那样,我会使用正则表达式。
如果您计划稍后必须对存储的信息执行特定操作(例如只获取域名,类似的东西),那么拥有EmailAddress类会很有用。