Что лучше всего инициализировать в конструкторе класса? - PullRequest
0 голосов
/ 11 августа 2011

У меня вопрос по дизайну о конструкторах. Посмотрев это видео от Google о «Чистых разговорах по коду», он рассказал о важности инициализации классов и многого другого в конструкторе, а также для улучшения параметров тестирования. В моем коде, например, у меня есть служба wcf, размещенная на iis в конструкторе svc службы, мы инициализируем всю службу.

 BService()
    {
        if (!m_DAL.Initilaze())
        {
            throw new Exception("Due to problems in the initialization B2BService is down");
        }

        m_sBadTransactionDir = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath + "BadTransaction";

        _address1 = new EndpointAddress(System.Configuration.ConfigurationManager.AppSettings["1Address"]);
        _address2 = new EndpointAddress(System.Configuration.ConfigurationManager.AppSettings["2Address"]);
        _address3 = new EndpointAddress(System.Configuration.ConfigurationManager.AppSettings["3Address"]);
        _address4 = new EndpointAddress(System.Configuration.ConfigurationManager.AppSettings["4Address"]);

        LoadSitesFromAdaptors();

        double interval1= Convert.ToDouble(System.Configuration.ConfigurationManager.AppSettings["interval1"]);
        m_sentTransactionTimer.Interval = interval1;
        m_sentTransactionTimer.Elapsed += Foo1;
        m_sentTransactionTimer.Start();

        double interval2 = Convert.ToDouble(System.Configuration.ConfigurationManager.AppSettings["interval2"]);
        m_checkStatusTimer.Interval = interval2 ;
        m_checkStatusTimer.Elapsed += Foo2;
        m_checkStatusTimer.Start();

        double interval3= Convert.ToDouble(System.Configuration.ConfigurationManager.AppSettings["interval3"]);
        m_adaptorsTimer.Interval = interval3;
        m_adaptorsTimer.Elapsed += Foo3;
        m_adaptorsTimer.Start();

.... and some more initialization code here


        Logger.Instance.Write("***************  Service is Up  ****************", "INFO");
    }

Ребята, вы знаете, как лучше инициализировать большие классы? облегчить тестирование и вообще ваше мнение по этому поводу?

Ответы [ 2 ]

1 голос
/ 11 августа 2011

Просто бегло взглянув на ваш код, мне кажется, что он должен быть переработан так, чтобы вы избегали всего своего дублированного кода.

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

Это создает очень аккуратный, легко читаемый и обслуживаемый код.

Может быть преобразован во что-то вроде

var addressList = new List<string>() { "1Address", "2Address", "3Address" };
var composedAddress = new List<Address>();    
addressList.ForEach(address => composedAddress.Add(new EndpointAddress(
    System.Configuration.ConfigurationManager.AppSettings[address]))
);

Кроме того, вы можете сделать то же самое с вашими интервалами;

var intervalList = new List<Tuple<string, timer, delegate>>() { 
  new Tuple<string, timer, delegate> { "Interval1", m_sentTransactionTimer, Foo1 },  
  new Tuple<string, timer, delegate>  { "Interval2" m_checkStatusTimer, Foo2 }, 
  new Tuple<string, timer, delegate> { "Interval3", m_adaptorsTimer , Foo3 }  
}

intervalList.ForEach(tuple => {
  var interval = Convert.ToDouble(System.Configuration.ConfigurationManager.AppSettings[tuple.Item1])
  tuple.Item2.Interval = interval;
  tuple.Item2.Elapsed += tuple.Item3;
  tuple.Item2.Start();
});    

Также вы можете убедиться, что ваш BServiceClass не берет на себя больше обязанностей, чем следовало бы. У класса должна быть только 1 ответственность. Следуя принципу единой ответственности, вы должны рассмотреть возможность абстрагирования любого кода, который, по-видимому, нарушает эту единственную ответственность.

Конструктор BService должен быть ничем иным, как функциями более высокого порядка, которые описывают происходящее.

BService() {
    Initialize(addressList, intervalList, someotherList);
    Logger.Instance.Write("***************  Service is Up  ****************", "INFO");

}

Initialize(List<string>, List<Tuple<string, timer, delegate>>, ...){
  ...
}
1 голос
/ 11 августа 2011

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

В качестве альтернативы, если вы действительно хотите протестировать этот класс, вы можете спрятать System.Configuration.ConfigurationManager за изменяемым интерфейсом, чтобы вы могли легко передавать любые параметры, которые вам нужны для ваших модульных тестов. Но в целом у вас почти всегда будут классы высокого уровня в ваших приложениях, которые просто инициализируют весь материал, загружают параметры конфигурации из командной строки / config files / registry / DB / что угодно и передают их другим классам которые делают реальную работу. В хорошо разработанном приложении такие классы несут эту единственную ответственность, и она проверяется интеграционными / системными тестами, а не модульными тестами.

В меньшем масштабе приведенный выше код содержит дублирование в следующих сегментах:

    double interval1= Convert.ToDouble(System.Configuration.ConfigurationManager.AppSettings["interval1"]);
    m_sentTransactionTimer.Interval = interval1;
    m_sentTransactionTimer.Elapsed += Foo1;
    m_sentTransactionTimer.Start();

Их можно легко извлечь в один метод с параметрами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...