如何在Delphi中强制分层源代码结构

时间:2019-03-18 09:41:52

标签: delphi

在我们的组织中,我们希望强制使用源代码的分层结构。每个单元都处于某个“级别”,并且每个单元只能使用相同或更低级别的单元。

示例:

假设我们具有以下单位:L1_A.pasL1_B.pasL2_C.pasL2_D.pasL3_E.pasLX_表示“ X级”)

L1_AL1_B可以互相使用。 L2_CL2_D可以同时使用所有L1_*个单位。 L3_E可以使用所有其他单位。

如果L1_*单元尝试使用L2_*单元或L3_*单元,则我们需要中止编译并产生一些错误(“较低级别的单元尝试使用较高级别的单元“)。

如果我们使用C语言(或其他带有预处理器的语言)进行编码,则可以例如定义LEVEL_1LEVEL_2LEVEL_3常数以及所有第1(第2)级单位检查是否定义了LEVEL_2LEVEL_3(分别为LEVEL_3)常量,在这种情况下,我们会发出相关错误。

Delphi定义(由{$DEFINE}定义)在定义它们的单元之外无效。可以在外部使用已命名的常量和常量表达式,但是我们看到的常量取决于uses中单位的顺序(即L1_A定义了const Level=1L2_C {{1 }}和const Level=2包含L1_B,而using L2_C, L1_A中的Level将是L1_B)。

我只想出了命名约定[2(或LX.Unit.pas),其中LX_Unit.pas是级别,X是“真实”单元名称],并使用Unit中的脚本提交钩子。

我们只想使用基本的Delphi(不使用外部工具)。

3 个答案:

答案 0 :(得分:9)

但是您可以检查常量。几个单位定义相同的单位,以及它们的类型或值是什么都无所谓,只要其名称与某个模式匹配即可。从某种意义上说,这样的常数就像“导出的” $define

const
  Level3 = 3; 
  • X单元具有恒定的Level1
  • Y单元的Level3不变
  • Z单元具有恒定的Level3
 uses
   X, Y, Z; // Z.Level3 hides Y.Level3, but that doesn't matter.

 {$IF declared(Level3)}

documentation指出:

  如果传递给它的参数是在当前范围内可见的有效声明的Delphi标识符,则

Declared 返回 True

答案 1 :(得分:6)

就像Rudy所说的那样,您可以使用普通常数,并在每个单位中重复它们。

请注意,单元可以包含在接口部分以及实现部分中。在第2级单元的 implementation 部分中包含第3级单元,不会使第3级常量在第2级单元的 interface 部分的范围内。因此,为了完全安全,您需要在接口部分设置常量(首先将其公开),并在实现部分中检查常量。

我至少要在包含的文件中放入检查,也许还有常量。在每个单元中,您都可以包含该级别的文件。

因此,每个第1级单元的“实现”部分将在“ uses”子句之后:

{$include level1check.inc}

在该文件中,您可以进行检查,并使用相当优雅的FATAL消息指令让编译器保释。然后level1check.inc看起来像这样:

{$IF Declared(level2)}
  {$MESSAGE FATAL 'Cannot include a level 2 unit in a level 1 unit'}
{$ENDIF}
{$IF Declared(level3)}
  {$MESSAGE FATAL 'Cannot include a level 3 unit in a level 1 unit'}
{$ENDIF}

我想知道是什么阻止开发人员作弊?如果他们可以修改uses子句,那么他们也可以删除或避开该支票。您可能需要在构建管道中强制执行此操作,但是如果执行此操作,也许可以使用更优雅的方法进行检查,例如,只需获取level1,level2和level3单元名称并扫描使用情况即可。每个level1和level2单元的子句都用于非法单元。

答案 2 :(得分:5)

在L1 * .pas中包含

boolean first = true;
if(somthing && first) {
    val = implMethod1();
    first = false;
} else {
    val = implMethod2();
    first = true;
}

然后在其他单位使用

CONST Level1 = TRUE;

{$IF Declared(Level1) }

您可能可以从那里找出其余的内容:-)