65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 NerdCaps 设计强大的标识符

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2015年2月12日

CPOL

12分钟阅读

viewsIcon

13590

downloadIcon

70

本文深入探讨了标识符的各个方面,并证明任何一组标识符都包含可用于探索软件模型的强有力证据。

引言

虽然像 LINQ 这样强大的新技术让软件设计变得更加容易,但我们更没有理由忽略软件设计中的细节。NerdCaps 为类型标识符提供了强大的分析功能,任何一组类型名称都可以被翻译成结构化的、自解释的表达式。本文将解释 NerdCaps 的各种断言,将 NerdCaps 呈现为一个工具和一个概念。

本文深入探讨了标识符的各个方面,并证明任何一组标识符都包含可用于探索软件模型的强有力证据。本文中的一些概念最好用数学或编程语言(特别是前提分配)来解释,但通过使用该工具进行实验可以更好地理解它们。为确保简单,我只包含了 NerdCaps 表达式。要快速了解 NerdCaps,请直接跳转到“示例”部分。

我怀疑未来 NerdCaps 的一个子集将被用于面向对象编程(OOP)领域之外,以统一任何人类定义的标识符,例如电子邮件地址、产品名称和论坛昵称,从而消除像“iPhone”这样的缺陷。像 c2 Wiki 这样的网站早在 1995 年就已经采用了这种形式,这可能是在类似情况下从 OOP 约定中衍生出来的。

注意: 本文不适用于诸如变量命名之类的琐碎问题。

 

术语

注意: 本节为参考。

命名空间

命名空间是一个抽象概念,它指定了一系列元素中的某个范围。每个元素都意味着一个唯一的命名空间。

实体

实体是从元素派生出的抽象概念。一个替代元素隐式地确认一个实体,而一个非替代元素则显式地确认一个实体。例如:AeIAeAeBase 都共享 Ae 这同一个实体。

Child

子元素是已分配给某个目录的元素。

根是一个虚拟元素,用作元素树中的基本点。

Directory(目录)

目录是一个元素,它充当另一个元素的命名空间,从而形成层级关系。

前提 (Premise)

前提是一个断言另一个元素含义的元素。在 OOP 术语中,这个术语被称为继承。

替代元素 (Alternative Element)

替代元素是处于替代形式的元素。这基本上意味着该元素被一个词缀所掩盖,使其失去了权威地位。

权威

权威 (authority) 是一个明确确认某个实体的元素。根据定义,“权威”与“非替代类”同义。

零扩展 (Zero-extension)

零扩展是指在目录分配中缺少实体,这可能在两个带后缀的元素堆叠时发生。例如:AeBaseAeBaseBase 构成了一个零扩展的情况。

范例 (Exemplar)

范例是接受目录分配或前提分配的元素。

 

元素

元素是可以生成表达式的组件。多个元素构成一个集合,这样就可以根据不同元素之间的连接形成复杂的结构。在原子层面上,元素是一个字符串,它只包含以下范围内的字符:{0-9, a-z, A-Z}

注意: 虽然下划线在特殊情况下也常用于标识符,但它被省略了,因为它不作为具有结构性含义的组件,这违背了 NerdCaps 的目的。

在顶层,一个元素被解析成一个单词序列。存在三种单词:

小写词 (Low Words)

小写词主要由小写字母 {a-z} 组成。首字母可以是大小写字母 {A-Z},用于与前一个单词分隔。

注意: 所有普通单词都属于此类。

大写词 (High Words)

大写词完全由大写字母组成。在实践中,这类词用于表示缩略词。

数字

数字完全由数字字符 {0-9} 组成。在实践中,这类词用于表示与类型容量相对应的幂,例如“Int64”。

注意: 这种类型的单词不能是序列中的第一个单词,因为数字在人类可读的标签中起辅助作用。此外,开头的数字会使编译器无法区分数字字面量和标识符。

 

指示符 (Signifiers)

指示符是一个元素,它专门用作后续元素的命名空间,即匹配该指示符的、更长的非指示符元素。这个概念将在“目录分配”中进一步解释。在非简单上下文中使用指示符对于避免与框架组件冲突至关重要。

要在元素集中表示一个指示符,必须在其前面加上“~”前缀。

示例

~Xe
XeAe

<Translates to>

SX  Xe
CL     XeAe

注意: 为了可读性和易于访问,请仅使用单个单词的指示符。要表示多个单词,请改用缩略词。

 

主指示符 (Primes)

主指示符是一种可以从一系列元素中的任何点进入的指示符,与常规指示符不同。如果一个指示符是通用元素(不特定于某个上下文)的命名空间,那么能够访问该指示符中的元素是可取的。在实践中,这类元素对应于在链接的源文件中定义的 OOP 构造,这在 Visual Studio 项目中是常规做法。

示例

Ae
~~Xe
XeAe
~Ye
YeAe

<Translates to>

CL  Ae
PM  Xe
CL     XeAe : Ae
SX  Ye
CL     YeAe : XeAe

要在元素集中表示一个主指示符,必须在其前面加上“~~”前缀。

注意: NerdCaps 维护一个名为“Internal”的标准主指示符。

注意: 在一个元素集中,只允许一个主指示符,以避免优先级冲突。

 

类 (class) 是一个普通元素。这种元素类型至关重要,因为它能够通过目录分配从元素集中形成复杂结构。

示例

Ae
AeBe
AeBeCe

<Translates to>

CL  Ae
CL     AeBe
CL        AeBeCe

 

基类 (Baseclasses)

基类是通过添加后缀“Base”形成的替代元素。使用基类的目的是声明一个实体而不占据权威地位,这样非替代类就可以在接受前提分配的同时选择性地授权该实体。

注意: 这种类型的元素意味着等效的 OOP 构造被修饰为“abstract”。

注意: 这种模式在某些软件设计中是常规做法,但在 API 中应避免使用。

 

接口

接口 (interface) 是通过添加前缀“I”形成的替代元素。接口对应于 OOP 语义,其中接口是可调用成员的模板。

 

替代形式 (Alternative Forms)

替代形式是通过使用词缀(前缀或后缀)定义的格式。

带前缀和带后缀的元素在语义上有所区别。虽然两者都隐式地假定有一个类来授权词缀之外描述的实体,但带后缀的元素本身就是一个类,因为它允许除指示符外的任何其他元素进行扩展。带前缀的元素不是一个类,因为它只允许同类元素进行扩展,这实际上在缺少权威时发生。带前缀的元素纯粹是为了权威假定,因为它在实现上语义上缺乏具体价值。

注意: 虽然每个带后缀的元素都是一个类,但带前缀的元素不可能承担这样的类,因为一个元素只能包含一个词缀。

 

目录分配 (Directory Assignment)

目录分配是通过在一系列元素中解析命名空间的过程,它有效地为元素添加目录注册,同时将元素作为子元素添加到目录中。

对于每个元素,通过从左到右匹配每个单词来比较所有其他元素,以确定胜者。胜者主要由最长的单词数决定。对于有相同单词数的多个候选者,胜者由以下类型顺序决定:{ > 基类 > 接口}

每种元素类型在比较中都遵循独特的条件:

注意: 不完全匹配表示后缀未能匹配的结果。对于这种匹配,该元素仍会因其实体而被计入。

指示符 (Signifier)

  • 对应元素的实体单词数不能更多。
  • 任何最佳匹配都会导致冲突,因为指示符不符合扩展的条件,所以指示符经历此过程仅为检测冲突。

  • 对应元素的实体单词数不能相等或更多。
  • 如果最佳匹配是带前缀的元素,或者最佳匹配不完整,则会抛出冲突,因为在没有权威的情况下,这样的最佳匹配是无效的。

带前缀的元素 (Prefixed Element)

  • 如果对应元素是非替代类,则其实体单词数不能更多。
  • 如果对应元素不是非替代类,则其实体单词数不能相等或更多。
  • 如果最佳匹配不完整,则会抛出冲突,因为在没有权威的情况下,这样的最佳匹配是无效的。
  • 如果最佳匹配的实体单词数相等,意味着最佳匹配是一个权威,那么范例将被分配给权威的父级,以保持实体位置的一致性。

带后缀的元素 (Suffixed Element)

  • 如果对应元素是非替代类,则其实体单词数不能更多。
  • 如果对应元素不是非替代类,则其实体单词数不能相等或更多。
  • 如果最佳匹配是带前缀的元素,则会抛出冲突,因为在没有权威的情况下,这样的最佳匹配是无效的。
  • 如果最佳匹配是处于零扩展状态的带后缀元素,则会抛出冲突,因为缺少实体。
  • 如果最佳匹配的实体单词数相等,意味着最佳匹配是一个权威,那么范例将被分配给权威的父级,以保持实体位置的一致性。

 

前提分配 (Premise Assignment)

前提分配是通过遍历和比较在一系列元素中解析前提的过程。前提分配可以简化为一种复合形式(如语言学中),并辅以命名空间和 OOP 语义的参与。

以下操作序列定义了该过程:

  1. 对于每个元素,调用一个从该元素的目录开始,到处理完根目录后结束的遍历。
  2. 对于遍历中的每个目录,调用一个递归来确定范例的所有派生。派生的数量等于范例的实体单词数。例如:“AeBeCe”的派生是“AeBeCe”、“BeCe”和“Ce”。
  3. 对于每个派生,调用对当前目录子列表的枚举。
  4. 对于每个子元素,调用范例和子元素之间的单词序列比较。
  5. 如果子元素可以被派生从左到右完全匹配,同时排除任一元素的潜在词缀和目录特定部分,则比较成功。
  6. 如果范例不能被子元素完全匹配,则从子元素的目录开始递归,试图完成匹配。如果子元素有后缀,则必须首先匹配该后缀,同时不消耗范例的单词。

在此过程中适用以下原则:

  • 一个元素不能将自身分配为前提。
  • 指示符不符合前提分配的条件,因为它们在语义上是不可继承的。
  • 如果范例与一个非主指示符相关联,则可以遍历主指示符。
  • 候选者的类型必须等于或低于范例的类型,顺序为:{ > 基类 > 接口}
  • 对于分布在多个目录中的多个候选者,胜者由最长的实体单词数决定。如果第二个候选者的实体单词数没有超过前者,则会被忽略。
  • 对于同一目录中的多个候选者,胜者主要由最长的实体单词数决定。实体单词数相等的第二个候选者可以替换先前的候选者,如果其类型在 { > 基类 > 接口} 顺序中更重要,或者类型顺序相同但先前的候选者没有与指示符相关联。

以下示例按复杂性顺序演示了前提分配的效果。前提由“:”符号表示。

无递归的前提分配

Ae
BeAe
CeBeAe

<Translates to>

CL  Ae
CL  BeAe : Ae
CL  CeBeAe : BeAe

带递归的前提分配

Ae
AeBe
CeAeBe

<Translates to>

CL  Ae
CL     AeBe
CL  CeAeBe : AeBe

带替代元素的前提分配

IAe
AeBase
Ae
AeBe
CeAeBe

<Translates to>

IF  IAe
BS  AeBase : IAe
CL  Ae : AeBase
CL     AeBe
CL  CeAeBe : AeBe

带指示符的前提分配

IAe
AeBase
Ae
~~Xe
XeAe
XeAeBe
XeCeAeBe
~Ye
YeCeAeBe

<Translates to>

IF  IAe
BS  AeBase : IAe
CL  Ae : AeBase
PM  Xe
CL     XeAe : Ae
CL        XeAeBe
CL     XeCeAeBe : XeAeBe
SX  Ye
CL     YeCeAeBe : XeCeAeBe

 

保留 (Reservation)

保留是与前提分配同时发生的过程。保留确保对于任何元素,其底层的目录和前提不会被选为前提,这对于保护上下文敏感性至关重要。原则上,特定路径中的每个元素都断言一个唯一且不兼容的值。

以下示例演示了保留的效果:

目录的保留

Ae
AeAe

<Translates to>

CL  Ae
CL     AeAe

注意: 对于“CL(AeAe)”,“CL(Ae)”被排除作为前提。

前提的保留

Ae
Be
BeAe
BeAeAe

<Translates to>

CL  Ae
CL  Be
CL     BeAe : Ae
CL        BeAeAe

注意: 对于“CL(BeAeAe)”,“CL(Ae)”被排除作为前提。

 

表达式

NerdCaps 依赖表达式来呈现特定元素集中的含义。表达式基本上是一棵树,其中每个节点都以类型指示符为前导,并且每个节点都可以由前提指示符补充。

表达式格式如下:

  • 每行以类型指示符开始。
  • 类型指示符后跟空格。空格序列的长度是 4 的倍数,不包括用于结尾的 2 个标准空格字符。深度级别由该倍数决定。
  • 空格后跟元素指示符,它精确地标识了输入的元素。
  • 元素指示符可以选择性地由前提指示符补充,由符号“:”引入,该符号被单个空格字符包围。

类型指示符映射如下:

  • CL: 类 (Class)
  • BS: 基类 (Baseclass)
  • IF: 接口 (Interface)
  • SX: 指示符 (Signifier)
  • PM: 主指示符 (Prime)

 

排序

排序是一个确保每个表达式具有统一外观的过程。此过程适用于每个命名空间。

排序操作按以下条件的重要性顺序执行:

  • 接口大于基类。
  • 基类大于类。
  • 类大于主指示符。
  • 主指示符大于指示符。
  • 如果 A 的长度比 B 短,则 A 大于 B
  • 最终的对决通过字符串比较解决。

 

冲突 (Conflicts)

冲突是为了确保一系列元素语义有效性而发生的错误。

定义了以下冲突:


MultiplePrimeSignifiers (多个主指示符)

此冲突表示在只允许一个主指示符的同一上下文中存在多个主指示符。

ElementConflict (元素冲突)

此冲突表示两个元素之间存在不平衡的对立。此冲突可能在多种情况下发生:

  • 一个实体完全或部分等于一个指示符。
  • 在没有非替代类来授权元素实体的情况下,一个元素优先于一个实体。这种情况在语义上是无效的,因为非权威元素在保留过程中无法被访问,因为它们不能作为该实体的目录。
  • 两个带后缀的元素构成了零扩展的情况。

LeadingNumber (数字开头)

此冲突表示元素的第一个单词是数字。这在语义上是无效的,因为标识符必须是文本而不是数量。

UnexpectedAffix (意外的词缀)

当指示符包含词缀,或者当元素同时包含前缀和后缀时,会抛出此冲突。

 

建议

建议完全是 NerdCaps 实现的一个功能。它们鼓励标识符的统一性,但在语义上并非至关重要。

定义了以下建议:

  • NC001:使用帕斯卡命名法 (pascal case)。
  • NC002:接口前缀后不要以数字开头。
  • NC003:不要使用单字母缩写。
  • NC004:对于超过 2 个字符的缩略词,请使用帕斯卡命名法。
  • NC005:不要使用前导零格式化数字。
  • NC006:数字后继续使用帕斯卡命名法。
  • NC007:特殊单词不适合用在实体中。

 

示例

以下表达式是使用 DotNet Framework 中的 CLR 命名空间“System.Xml”对 NerdCaps 功能的真实演示:

IF  IStreamProvider
IF  IHasXmlNode
IF  IApplicationResourceStreamResolver
IF  IFragmentCapableXmlDictionaryWriter
CL  NameTable
CL  UniqueId
SX  Xml
IF     IXmlDictionary
IF     IXmlLineInfo
IF     IXmlNamespaceResolver
IF     IXmlBinaryReaderInitializer
IF     IXmlBinaryWriterInitializer
IF     IXmlMtomReaderInitializer
IF     IXmlMtomWriterInitializer
CL     XmlAttribute
CL        XmlAttributeCollection
CL     XmlComment
CL     XmlConvert
CL     XmlDeclaration
CL     XmlDictionary : IXmlDictionary
CL        XmlDictionaryReader : XmlReader
CL           XmlDictionaryReaderQuotas
CL        XmlDictionaryString
CL        XmlDictionaryWriter : XmlWriter
CL     XmlDocument
CL        XmlDocumentFragment
CL        XmlDocumentType
CL     XmlElement
CL     XmlEntity
CL        XmlEntityReference
CL     XmlException
CL     XmlImplementation
CL     XmlNode
CL        XmlNodeList
CL        XmlNodeReader : XmlReader
CL        XmlNodeChangedEventArgs
CL     XmlNotation
CL     XmlReader
CL        XmlReaderSettings
CL     XmlResolver
CL     XmlText
CL        XmlTextReader : XmlReader
IF           IXmlTextReaderInitializer
CL        XmlTextWriter : XmlWriter
IF           IXmlTextWriterInitializer
CL     XmlWhitespace
CL     XmlWriter
CL        XmlWriterSettings
CL     XmlCharacterData
CL     XmlDataDocument : XmlDocument
CL     XmlLinkedNode : XmlNode
CL     XmlNamespaceManager
CL     XmlNameTable : NameTable
CL     XmlParserContext
CL     XmlProcessingInstruction
CL     XmlQualifiedName
CL     XmlSecureResolver : XmlResolver
CL     XmlSignificantWhitespace : XmlWhitespace
CL     XmlUrlResolver : XmlResolver
CL     XmlValidatingReader : XmlReader
CL     XmlXapResolver : XmlResolver
CL     XmlBinaryReaderSession
CL     XmlBinaryWriterSession
CL     XmlCDataSection
CL     XmlNamedNodeMap
© . All rights reserved.