Если у класса есть одна общедоступная точка входа, которая принимает в качестве параметров все, что ему нужно, должна ли она быть статической? - PullRequest
3 голосов
/ 03 ноября 2010

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

public class ReportGenerator
{
    public string GenerateReport(List<SomeClass> stuffToReportOn)
    {
        string fileName = String.Empty;
        using(var reportDs = CreateDataSet(stuffToReportOn))
        {
            //do the stuff with the third party tool that 
            //creates the report.
            //Construct the filename.
            //Save the report.
        }
        return fileName;
    }

    private TypedDataSetRequiredByThirdPartyLib CreateDataSet(List<SomeClass> reportItems)
    {
        //create the dataset, calling two other private methods
        //to build the tables/rows
    }
}

После того, как я закончил рефакторинг, я понял, что этот класс может быть полностью статическим. Мой вопрос, не так ли? Должен ли класс, который инкапсулирует всю свою функциональность в одном публичном методе, сделать статическим?

Ответы [ 4 ]

5 голосов
/ 03 ноября 2010

Нет. и каковы ожидаемые выгоды?

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

3 голосов
/ 03 ноября 2010

Нет, это сделает невозможным написание модульных тестов для этого класса - или классов, использующих этот класс.

С помощью такого инструмента, как FakeItEasy , вам даже не нужен этот классреализовать интерфейс, позволяющий имитировать его (это означает, что вы можете быстро приступить к издевательству над старой базой кода, которая не была написана с учетом TDD), но она не может обойти статические вызовы.

ОБНОВЛЕНИЕ
Допустим, вам нужно выполнить модульное тестирование метода GenerateWidgetReports, который вызывает ReportGenerator.GenerateReport.Вам необходимо убедиться, что stuffToReportOn содержит только Widget1 & Widget2.Как вы код этого теста?

То, что вы в конечном итоге делаете, - GenerateWidgetReports использует метод, называемый GetStuffToReportOn, вы можете проверить это, хорошо.GenerateWidgetReports затем просто становится клеем, который вызывает GetStuffToReportOn и передает его результат ReportGenerator.GenerateReport.

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

В идеале класс с GenerateWidgetReports принимает объект IReportGenerator, вы высмеиваете метод GenerateReport и тестируете там для Widget1 & Widget2.

Вы не можете ничего сделатьоб этом с помощью любого инструмента Mocking, если GenerateReport является статическим.

Обновление 2
Я исправлен, TypeMock может перехватывать и перенаправлять вызовы статических методов. См. Этот ответ

1 голос
/ 03 ноября 2010

Кроме трудностей в юнит-тестировании, вы должны спросить себя:

  1. Вносить ли изменения в параметры? Если да, то как часто?
  2. Нужно ли объекту иметь состояние?
  3. Нужно ли держать объект после завершения процесса?

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

public static class ReportExtension
{
    public static string GenerateReport(this List<SomeClass> stuffToReportOn)
    {
        string fileName = String.Empty;
        using(var reportDs = CreateDataSet(stuffToReportOn))
        {
            //do the stuff with the third party tool that 
            //creates the report.
            //Construct the filename.
            //Save the report.
        }
        return fileName;
    }

    private static TypedDataSetRequiredByThirdPartyLib CreateDataSet(List<SomeClass> reportItems)
    {
        //create the dataset, calling two other private methods
        //to build the tables/rows
    }
}

Уверен, у меня правильное форматирование для метода расширения.

Edit:

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

0 голосов
/ 03 ноября 2010

Да, было бы намного проще использовать ваш класс.

...