Цитата(elbjarn @ 14.10.2008, 21:24 ) | | и тем не менее, это как раз тот пример, когда применение глобальных переменных оправданно |
Да, но глобальные - это когда область видимости у объекта - все приложение. Т.е. когда или поле - public (гм), или свойство явно намекает что вернет синглтон (Instance). Стандартная реализация не упоминает синглтоны, хотя они там ес-но есть где-то внутри 
Цитата(elbjarn @ 14.10.2008, 21:24 ) | | но кстати не только конфигурации и application. представьте себе, что вы создаете сайт по всем канонам трехзвенной архитектуры. вы стремитесь сделать независимость вашего приложения от БД максимальной. |
А вот этот пример как-раз показывает основную проблему синглтонов - нет нормального управления временем жизни. Т.е. создается один Instance на приложение. Что делает очень трудным написание, например, тестов, которые должны работать независимо друг от друга. Пример:
| Код | public class DataProvider { public static Instance { if (в приложении надо использовать БД MS SQL) return MySiteDataProviderMSSQL.Instance; else (в приложении надо использовать аксесс) return MySiteDataProviderAccess.Instance; } }
static class NewsService { static GetNews() { return DataProvider.Instance.GetNews(); }
static AddNews() { DataProvider.Instance.AddNews(new New()); } }
static class SomeOtherService { static GetTopNewsmakers() { NewsService.GetNews.GetAuthors(); } }
|
И вот теперь надо одтельно проверить работу логики GetNews(), AddNews() и GetTopNewsmakers(). Создавать базу перед вызовом каждого теста - тесты никто не будет писать, они будут работать по пол часа. Не создавать - непонятно, сколько должен вернуть GetNews на тестовых данных.
На самом деле такую проблему принято решать с помощью Inversion of Control Containers and the Dependency Injection. Например, в решении от MS - Unity - тест будет выглядеть так: Добавляете в NewsService конструктор с параметром IDataGateway, и пользуетесь в сервисе только пришедшим значением. А в коде, юзающем сервис, пишете:
| Код | var container = new UnityContainer(); container.RegisterType<INewsService, NewsService>(); container.RegisterType<IDataGateway, TestDataGateway>();
INewsService service = container.Resolve<INewsService>(); service.AddNews(); Assert(service.GetNews().Count =1)
|
и тут же в соседнем тесте создаете новый контейнер и в нем проводите опыты с настоящим SomeOtherService. А еще в одном тесте - с настоящим DataGateway.
А для реального приложения пишете в конфиг всю структуру классов, и создаете контейнер для каждой операции. |