Инициализация статических объектов - вопрос разработки кода - PullRequest
0 голосов
/ 09 октября 2008

В моем веб-приложении (C #, .Net 3.5), составленном из библиотеки классов core (содержащей бизнес-логику, слой данных и несколько служебных классов), проект службы Windows, веб-сервис проект и проект веб-сайта, у меня есть пара статических классов в библиотеке core , используемой всеми другими проектами. Эти классы (например, класс Log ) требуют некоторой инициализации (у них есть метод Initialize ), чтобы настроить их для использования. Например, метод Initialize класса Log имеет параметр пути к каталогу, который сообщает Log , куда сохранять файлы журнала. В качестве альтернативы я думал о загрузке «настроек» для класса Log из файла конфигурации в статическом конструкторе. Недостаток в том, что мне нужны другие настройки для юнит-тестирования, чем в рабочем коде.

Есть ли лучший способ спроектировать это? Недостаток этого подхода состоит в том, что все потребители статического класса будут пытаться вызвать Initialize. На самом деле это не проблема, потому что после инициализации метод немедленно вернется вместо выполнения кода Initialize снова, но дизайн мне кажется немного странным.

Надеюсь, моего английского достаточно, чтобы объяснить, что я хочу. Не стесняйтесь спрашивать, если вопрос недостаточно ясен.

Ответы [ 5 ]

6 голосов
/ 09 октября 2008

Я бы старался избегать статических классов для вещей, которые потребуют инициализации. Сделайте ваш класс Log нормальным классом и, возможно, имейте отдельный класс фабрики для создания экземпляров или одного экземпляра. Возможно, вы захотите использовать внедрение зависимостей, чтобы избежать какой-либо статики, хотя это может быть болезненным для чего-то вроде ведения журнала, которое довольно универсально.

В частности, для ведения журналов log4net и log4j (по какой-либо причине вы сами сделали это, кстати?) Используют идиому запроса некоторого фабричного класса для экземпляров журнала, основанных на именах (обычно на основе имени типа, использующего его ). Это часто хранится в статической переменной в каждом классе, который должен выполнять регистрацию. Если на возвращаемые объекты ведения журнала все равно может повлиять инициализация подсистемы ведения журнала впоследствии, у вас не останется проблем с упорядочением, когда DI-контейнер должен инициализировать структуру ведения журнала перед инициализацией клиентов ведения журнала и т. Д.

Использование отдельных объектов ведения журнала вместо статического класса улучшает тестируемость - вы можете заменить объект ведения журнала во время теста на объект, который записывает журналы, чтобы гарантировать, что (скажем) информация аудита будет захвачена. Одним из недостатков статически известных объектов регистрации является то, что это уменьшает возможности параллельного запуска тестов.

Извиняюсь за несколько случайные размышления - я еще не пил свой первый кофе. Надеюсь, это поможет.

5 голосов
/ 09 октября 2008

Я бы посоветовал, если инициализация стоит дорого, использовать Singleton (если вы не знакомы с шаблонами дизайна, шаблон Google Singleton Design). Как часть конструкции экземпляра, вы затем читаете в файле конфигурации, который может быть специфичным для среды.

Таким образом, вы получите лучшее из обоих миров.

0 голосов
/ 12 октября 2008

Добавление к тому, что сказал Трав ...

В дополнение к реализации журнала в Singleton, вы также можете обернуть его вокруг статического класса. Таким образом, вам не нужно менять места, где вы уже используете статический класс Log. И будет очень легко поменять фреймворк Logging для ваших юнит-тестов. Я приведу пример этого на 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);
    }
}
0 голосов
/ 12 октября 2008

Я думаю, что вы согласны с любым из решений, которые вы дали в своем вопросе. Либо вызовите Initialize один раз из вашего метода Main (или поместите его в отдельный метод SetupApplicationInfrastructure, вызываемый из Main). Или загрузите настройки один раз в конструкторе.

Я не думаю, что вам нужно беспокоиться о попытке устранить проверку необходимости инициализации или нет при каждом доступе к экземпляру. Это неплохо.

0 голосов
/ 09 октября 2008

Я думаю, вы должны задать сложные вопросы здесь. Разве этому материалу нужно , чтобы быть статичным? С статическими объектами, будь то в форме Singleton или нет, работать довольно сложно. Если вам действительно нужна статичность, вам может помочь что-то вроде monostate pattern . Это как полиморфный статический класс. Как правило, все переменные-члены являются статическими, а конструкторы - нет. Возможно, благодаря этому вы можете унаследовать этот класс и сделать некоторые переопределения для своих нужд. Просто мысль.

...