когда я должен создать частный метод в C # - PullRequest
2 голосов
/ 22 марта 2011

Так как после DI и TDD, я немного запутался, когда мне следует создавать закрытый метод.Не могли бы вы сказать, какие практические правила следует учитывать при создании метода private, помня о тестируемости и внедрении зависимостей?

Полагаю, что здесь может помочь пример:

Предположим, яиметь интерфейс с тремя методами, такими как:

public interface IWordFrequencyAnalyzer
{
    int CalculateHighestFrequency(string forText);
    int CalculateFrequencyForWord(string text, string word);
    IList<IWordFrequency> CalculateMostFrequentNWords(
        string text, int n);
}

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

ИЛИ

Я могу извлечь этот закрытый метод в отдельный класс, сказать что-токак WordProcessor, который реализует IWordProcessor, с помощью одного открытого метода, который разбивает предложение на слова и передает его как зависимость от реализации IWordFrequencyAnalyzer.Таким образом, реализация разделения слов также поддается проверке.

Какой подход вы предложите?

Спасибо, -Mike

Ответы [ 6 ]

7 голосов
/ 22 марта 2011

С тех пор, как я стал больше разбираться в DI и TDD, я все реже использовал частные методы, но причина была не в том, что мне нужно было, чтобы они были публичными для тестов.Это связано с тем, что в качестве побочного продукта использования DI я узнаю больше о применении принципов SOLID к моему коду, и это (в свою очередь) побуждает меня писать классы с меньшим количеством методов в целом и почти без личных.

Итак, допустим, у вас есть фрагмент кода, который вы используете в различных методах вашего класса.Вы знаете о СУХОЙ и реорганизуете ее в частный метод, и все хорошо.За исключением того, что часто вы понимаете, что можете обобщить то, что делает этот закрытый метод, и внедрить эту функциональность как внешнюю зависимость класса: таким образом, вы можете протестировать и смоделировать это, конечно, но, прежде всего, вы можете повторно использовать то, что этот метод делает даже вдругие классы или другие проекты, если это необходимо.Удаление его из исходного класса является применением принципа единой ответственности.

Как я уже сказал, это изменение в способе кодирования напрямую не зависит от того факта, что я использую TDD или DI, но это побочный эффектПродукт принципов TDD побуждает меня применять и удобство, которое обеспечивают контейнеры DI при составлении множества небольших классов, являющихся результатом этого подхода.

РЕДАКТИРОВАТЬ: Второй подход в примере, который вы добавили к своему вопросу, является хорошим примером того, о чем я говорил.Тот факт, что ваш новый класс WordProcessor теперь можно тестировать, является плюсом, но тот факт, что он теперь можно комбинировать и многократно использовать, является реальным преимуществом.

6 голосов
/ 22 марта 2011

Ваши методы должны быть private, если они не должны быть public для вашего приложения, а не для вашего теста.

Как правило, вы не должны делать вещи public только для поддержки модульного тестирования (internal может помочь в этом).И в этом случае, как правило, вы все равно должны тестировать public интерфейсы, а не private сведения о классе, которые с большей вероятностью изменят и сломают ваш тест.

Если у вас есть доступ к копии" Искусство модульного тестирования " Роя Ошерова очень хорошо решает эту проблему

2 голосов
/ 22 марта 2011

Вы определяете методы как частные, когда они используются только внутренне объектом, обычно вызывается из других методов, некоторые из которых будут открытыми или защищенными. Вы с большей вероятностью создадите приватные методы в результате следования принципам СУХОЙ (не повторять себя). В этом сценарии вы извлекаете некоторый общий код, используемый несколькими методами, в закрытый метод, вызываемый этими методами.

1 голос
/ 22 марта 2011

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

1 голос
/ 22 марта 2011

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

0 голосов
/ 23 марта 2011

Политика единой ответственности поможет вам в этом вопросе. Рассмотрим этот пример:

internal class Boss
{
    private bool _notEnoughStaff;
    private IList<Employee> _staff; 

    public Boss(bool notEnoughStaff)
    {
        _notEnoughStaff = notEnoughStaff;
    }

    public void GiveOrders()
    {
        if (_notEnoughStaff)
            HireStaff();

        foreach (Employee employee in _staff)
        {
            employee.DoWork();
        }
    }

    private void HireStaff()
    {
        _staff.Add(new Employee());
    }
}

public class Employee
{
    public void DoWork()
    {

    }
}

В этом случае я вижу не одну, а две обязанности: делегаты босса работают и нанимают новых сотрудников. В этом примере я всегда извлекаю закрытый метод HireStaff в новый класс (назовем его HR) и внедряю его в класс Boss. Это очень упрощенный пример, но по мере того, как вы приобретаете все больший опыт в мышлении TDD, вы обнаружите, что не многие частные методы имеют легитимность.

С уважением, Morten

...