哪种设计(使用OOP)更好?外部记录还是内部记录?

时间:2019-05-02 06:55:47

标签: oop logging design-patterns refactoring anti-patterns

我需要验证日志信息(错误消息)以及结果集。在我的情况下,日志记录也可以理解为生成报告。

外部记录

我应该将日志消息(是否有任何错误)与结果一起存储,并在业务逻辑步骤之后进行日志记录

优势:

  1. 与解析日志文件相比,这为我提供了日志信息,可用于在单元测试期间验证否定案例。
  2. 将日志记录与业务逻辑分开。
  3. 我可以将日志记录作为单独的功能来实现,在其中我可以根据实现(HTML,JSON等)以不同的格式登录

缺点

  1. 这将有代码重复,因为我最终得到的日志记录循环与结果集的计算相同。
  2. 在记录阶段,父级将必须获取子级信息。存储所有这些信息会使它变得复杂且难以读取。

内部化日志记录

我应该在执行业务逻辑的同时进行日志记录

优势

  1. 不存储任何信息并简化解决方案,并有效地将父对象的上下文传递给子对象,
  2. 发生异常时进行记录。

缺点

  1. 但无法将日志记录/报告与业务逻辑分开。
  2. 我将不会获得日志信息来验证单元测试的否定情况。因此,我将需要分析日志文件以进行验证。

下面的更多上下文:

我正在构建此工具,用于比较两个资源类型(Python中的JSON,属性,VM,REST API等)的属性。

该工具读取元数据json,其结构如下:

{
  "run-name": "Run Tests"
  "tests": [
    {
      "name": "Test 1",
      "checks":[
         {
          "name": "Dynamic Multiple",
          "type": "COMPARE",
          "dynamic": [
            {
              "file": "source.json",
              "type": "JSON",
              "property": "topology.wlsClusters.[].clusterName"
            }
          ],
          "source": {
            "file": "source.json",
            "type": "JSON",
            "property": "topology.wlsClusters.[clusterName == ${1}].Xms"
          },
          "target": {
            "file": "target.properties",
            "type": "PROPERTY",
            "property": "fusion.FADomain.${1}.default.minmaxmemory.main",
            "format": "-Xms{}?"
          }
        },
      ]
    }
  ]
}

上面的JSON告诉我的工具:

  1. 从topology.wlsClusters中的每个wlsCluster对象中获取“ clusterName”。这给出了“ clusterNames”的列表。
  2. 从“ source.json”从每个“ wlsCluster”对象(其中“ clusterName”属于上述列表)中获取Xms值。
  3. 使用上面的列表类似地从target.properties文件中获取所有Xms值。
  4. 比较源Xms列表和目标Xmx列表中的每个值。
  5. 如果全部匹配,则成功,否则失败。

可以直观地将JSON上方的对象映射为:

  • 测试
  • 检查
  • 资源

现在理想情况下,我知道我应该执行以下步骤:

  • 运行所有测试,并在每个测试中进行所有检查。
  • 对于每个测试是否比较类型
    • 读取和计算“动态”值
    • 读取“源”并替换属性字段中的动态值并获取相应的属性
    • 类似地读取“目标”并获取相应的属性
    • 比较并返回“通过”或“失败”

因此,我大致上可以执行以下步骤:

  1. 获取和存储值。
  2. 比较值

我还想以以下格式打印日志。

[<TIMESTAMP> <RUN-NAME> <TEST-NAME> <CHECK-NAME> <ERROR-LEVEL> <MESSAGE-TYPE> <RESOURCE-NAME>] custom-msg

其中

ERROR-TYPE: INFO DEBUG etc
MESSAGE-TYPE: COMPARE, SYNATAX-ERROR, MISSING-PROPERTY etc

现在,如果我遵循上面的对象模型,并且每个对象都负责处理自己的日志记录,那么它将没有所有这些信息。所以我需要:

  • 将此信息向下传递到子对象,
  • 或让父级读取子级对象的信息。

我喜欢第二种方法,因为这样我就可以存储获取的结果,并将日志记录(如果有的话)延迟到比较之后。这样,我也可以在验证错误消息(负面情况)时运行验证(单元测试)。

但这是我的解决方案越来越复杂的地方。

  • 我需要将获取的结果存储在每个对象中,它们可以是找到的值,当找不到值时可以为“无”。现在,当找不到任何值时,我还需要存储错误类型和错误消息。让我们将此类称为“值”。
  • 每个媒体资源都可以生成此类值的列表。
  • 每个资源都可以生成此类属性的列表。
  • 每次检查都可以生成此类资源的列表。

注意:这是用Python开发的。 (如果对您来说很重要。)

1 个答案:

答案 0 :(得分:0)

每个类都应负责自己的状态。当您让类根据其他类的属性做出决策时,最终将得到spagetti代码。

if (test.check.resource.AProperty == aValue)之类的代码清楚地表明您的意大利面已经开始烹饪。

在这种情况下,您想全部登录类。您希望o决定一系列操作是否成功完成。 因此,您想要记录结果

请记住,不要让类完全记录日志,而只能报告他们测试/检查的内容以及结果。

一种常见的方法是提供一个用于接收结果的上下文对象。

以下是一些C#代码来说明(我不太了解python):

interface VerifierContext
{
  void AddSuccess(string checkName, string resourceName, string message);
  void AddFailure(string checkName, string resourceName, SomeEnum failureType, string message);
}
public class SomeChecker
{
    public void Validate(VerifierContext context)
    {
        context.AddFailure("CompanyNameLength", "cluster.Company", SomeEnum.LengthRestriction, "Company name was 30chars, can only be 10");
    }
}

这将为您提供一个简单的验证清单。如果要嵌套,可以添加Enter / Exit方法:

public class SomeChecker
{
    public void Validate(VerifierContext context)
    {
        context.Enter("CompanyValidations");

        foreach (var validator in _childValidators)
            validator.Validate(context);

        context.Exit("CompanyValidations");
    }
}

您当然可以用很多不同的方法来设计它。我的主要观点是您的检查/解析器中的每个类都应该决定是否一切正常。它不应该决定如何记录事物。

然后,触发工作的类可以遍历所有结果并根据errorType等选择日志级别。

所有类都仅取决于上下文,因此也易于测试。