初始化静态对象 - 代码设计问题

本文关键字:问题 代码 静态 对象 初始化 | 更新日期: 2023-09-27 17:47:22

在我的Web应用程序(C#,.Net 3.5)中,由一个核心类库(包含业务逻辑,数据层和几个实用程序类),一个Windows服务项目,一个Web服务项目和网站项目组成,我在所有其他项目使用的核心库中有几个静态类。这些类(例如 Log 类)需要一些初始化(它们具有 Initialize 方法)才能设置它们以供使用。例如,Log 类的 Initialize 方法有一个目录路径参数,该参数告诉 Log 将日志文件保存到何处。或者,我正在考虑从静态构造函数中的配置文件加载 Log 类的"设置"。缺点是,我需要在生产代码中进行不同的单元测试设置。

有没有更好的方法来设计这个?此方法的缺点是,静态类的所有使用者都将尝试调用 Initialize。这不是真正的问题,因为一旦初始化,该方法将立即返回,而不是再次执行 Initialize 代码,但设计对我来说似乎有点奇怪。

我希望我的英语足以解释我所追求的。如果问题不够清楚,请不要犹豫。

初始化静态对象 - 代码设计问题

我会尽量避免使用静态类来处理需要初始化的东西。将 Log 类设置为普通类,并且可能具有单独的工厂类来创建实例或单个实例。您可能希望使用依赖注入来避免使用任何静态数据,尽管这对于诸如日志记录之类的东西来说可能会很痛苦,尽管这是非常通用的。

特别是对于日志记录,log4net 和 log4j(顺便说一句,你自己推出的任何原因?)都使用基于名称(通常基于使用它的类型的名称)向某个工厂类询问日志实例的习惯用法。这通常存储在需要执行日志记录的每个类的静态变量中。如果之后返回的日志记录对象仍可能受到日志记录子系统初始化的影响,则最终不会遇到 DI 容器必须在初始化日志记录客户端之前初始化日志记录框架等的排序问题。

使用单独的日志记录对象而不是静态类可以提高可测试性 - 您可以在测试期间将日志记录对象替换为记录日志的对象,以确保捕获(例如)审核信息。静态已知的日志记录对象的一个缺点是,这减少了并行运行测试的可能性。

为有些随意的思考道歉 - 我还没有喝第一杯咖啡。希望它有所帮助。

我建议,如果初始化很昂贵,使用单例(如果你不熟悉设计模式,谷歌单例设计模式)。作为实例构造的一部分,然后读取配置文件,该文件可以特定于环境。

这样,您将获得两全其美的效果。

我认为您对问题中给出的任何解决方案都很好。要么从 Main 方法调用一次 Initialize(或者将其放在从 Main 调用的某个单独的 SetupApplicationInfrastructure 方法中)。或者,在构造函数中加载一次设置。

我认为您无需担心每次访问实例时都试图消除是否需要检查初始化。这样做还不错。

加上特拉夫所说的...

除了日志的单一实例实现之外,还可以将其包装在静态类周围。这样,就不需要更改已在使用静态 Log 类的位置。将模拟日志记录框架交换为单元测试将非常容易。我将在 Java 中举一个示例 - 假设您有如下所示的内容:

public class Log
{
    public static void debug(Object o, String message)
    {
        System.out.println(o + " [DBUG] " + message);
    }
    public static void info(Object o, String message)
    {
        System.out.println(o + " [INFO] " + message);
    }
}

您可以像下面这样重构它:

public interface ILoggeer
{
    void debug(Object o, String message);
    void info(Object o, String message);
}
public class Log
{
    private static ILogger log = new ConsoleLogger();
    public static setLogger(ILogger impl)
    {
        log = impl;
    }
    public static void debug(Object o, String message)
    {
        log.debug(o, message);
    }
    public static void info(Object o, String message)
    {
        log.info(o, message);
    }
}
public class ConsoleLogger implements ILogger
{
    public void debug(Object o, String message)
    {
        System.out.println(o + " [DBUG] " + message);
    }
    public void info(Object o, String message)
    {
        System.out.println(o + " [INFO] " + message);
    }
}

我认为你必须在这里提出棘手的问题。这些东西需要是静态的吗?静态对象,无论是否采用单例形式,都很难使用。如果你真的需要静态,也许像单态模式这样的东西可以帮助你。这就像一个多态静态类。通常,所有成员变量都是静态的,而构造函数不是。也许有了这个,你可以继承这个类,并根据需要做一些重写。只是一个想法。