Попробовал TDD в своей последней программе и заметил, что очень сильно поменялся не только стиль работы, но и дизайн кода.
Для того, чтобы корректно писать unit-тесты, приходится дробить код на более мелкие классы с четко выделенной функциональностью.
Например, класс, работающий с внешним объектом пришлось поделить на ЧЕТЫРЕ сущности — класс для обработки поступающих данных, интерфейс для чтения данных, класс от него порожденный, непосредственно читающий данные извне, и fake-объект порожденный о этого же интерфейса для тестирования корректной работы первого класса.
С другим кодом ситуация похожая — в результате эксперимента кол-во классов в системе возросло раза в три (качество при этом, кстати, заметно повысилось, да и собственно процесс был затеян для того, чтобы сократить время на общение с заказчиком по поводу возникших ошибок)
Это нормально? Так и должно быть? А как же принцип "не плоди сущностей без надобности"?
Может ли многоуважаемый all поделиться своими наблюдениями в этой области?
Здравствуйте, umnik, Вы писали:
U>Это нормально? Так и должно быть?
По идее, да.
U>А как же принцип "не плоди сущностей без надобности"?
Я не уверен, что этой самой надобности "нет".
Такое разбиение классов на более мелкие обычно приводит либо к "расползанию" системы (ее становится трудно модифицировать, если классы сильно связаны друг с другом), либо к высокой гибкости (тогда модифицировать становится легко).
Т.е. на самом деле был проведен рефакторинг кода А, ИМХО, рефакторинг и TDD — два конца одной палки.
U>Может ли многоуважаемый all поделиться своими наблюдениями в этой области?
ИМХО, можно классы и не дробить. Просто тесты тогда будут совсем другие, возможно менее "корректные"
U>>А как же принцип "не плоди сущностей без надобности"? K>Я не уверен, что этой самой надобности "нет".
надобностью можно назвать то, что "по-другому тестов не напишешь"
K>Такое разбиение классов на более мелкие обычно приводит либо к "расползанию" системы (ее становится трудно модифицировать, если классы сильно связаны друг с другом), либо к высокой гибкости (тогда модифицировать становится легко).
тут еще вопрос, насколько лечге этот код может быть понят другими людьми (в случае групповой работы) и тобой самим через полгода. Ну а насчет связанности классов — разбиение делалось именно с рассчетом на то, чтобы эту связанность устранить.
вообще, получившийся код у меня вызвал ассоциации с конструктором:
было — взяли железную болванку, обработали ее напильником, в результате получилась единая неделимая монолитная хрень, заточенная под конкретную задачу, в которой кроме как напильником ничего не изменишь
стало — взяли и наклепали много мелких деталек, пошлифовали их, проверили винтики-болтики на совместимость (подходил ли размер, правильная ли резьба), шестеренки на смазанность, потом все это вместе собрали, в результате получилась сложная хрень из множества компонент, которую, правда, легко переделать
В XP есть правило, что надо использовать самое простое решение. То что
ты описал под этот подход слабо походит. Можешь поподробней рассказать о
своей программе.
По моим наблюдениям, код который я пишу Test-First получается короче и
проще, чем тот который я пишу без тестов.
Вообще почитай книжку Кента Бека про TDD и многое прояснится.
umnik wrote: > > From: *umnik* </Users/Profile.aspx?uid=12381> </search/?group=72> > </Users/Private/AddFav.aspx?mid=966129> <NewMsg.aspx?gid=72> > <NewMsg.aspx?mid=966129> <?mid=966129> <Message.aspx?mid=966129#966129> > <NewMsg.aspx?mid=966129&edit=1> <Private/Self.aspx?mid=966129> > > Попробовал TDD в своей последней программе и заметил, что очень сильно > поменялся не только стиль работы, но и дизайн кода. > Для того, чтобы корректно писать unit-тесты, приходится дробить код на > более мелкие классы с четко выделенной функциональностью. > > Например, класс, работающий с внешним объектом пришлось поделить на > ЧЕТЫРЕ сущности — класс для обработки поступающих данных, интерфейс для > чтения данных, класс от него порожденный, непосредственно читающий > данные извне, и fake-объект порожденный о этого же интерфейса для > тестирования корректной работы первого класса. > С другим кодом ситуация похожая — в результате эксперимента кол-во > классов в системе возросло раза в три (качество при этом, кстати, > заметно повысилось, да и собственно процесс был затеян для того, чтобы > сократить время на общение с заказчиком по поводу возникших ошибок) > > Это нормально? Так и должно быть? А как же принцип "не плоди сущностей > без надобности"? > Может ли многоуважаемый all поделиться своими наблюдениями в этой области? > Влияние TDD на дизайн кода <?mid=966129> Оценить > <RateList.aspx?mid=966129> <Private/Rate.aspx?mid=966129&rate=-3> > <Private/Rate.aspx?mid=966129&rate=1> > <Private/Rate.aspx?mid=966129&rate=2> > <Private/Rate.aspx?mid=966129&rate=3> > <Private/Rate.aspx?mid=966129&rate=-1> > <Private/Rate.aspx?mid=966129&rate=-2> > <Private/Rate.aspx?mid=966129&rate=-4> > <Private/Rate.aspx?mid=966129&rate=0> >
Здравствуйте, krulez, Вы писали:
K>В XP есть правило, что надо использовать самое простое решение. То что K>ты описал под этот подход слабо походит. Можешь поподробней рассказать о K>своей программе.
Ситуация простая — есть задача прочитать и обработать данные из внешнего устройства. Поскольку я не могу усправлять поступающими данными, а протестировать обработку хочется, то приходится разбивать фукнциональность на 2 класса:
class DataProcessor
{
public:
DataProcessor( IDataReader* reader );
void DoProcessData()
{
Data* data = reader.ReadData();
ProcessData(data);
}
SomeInternalStatistics GetStatistics();
};
class IDataReader
{
public:
virtual Data* ReadData() = 0;
};
и писать две реализации для IDataReader: для реальной работы и для тестирования, т.е.
class DeviceDataReader : public IDataReader
{
...
}
class FakeDataReader :public IDataReader //(вариант FileDataReader)
{
...
};
Соответственно в unit-тестах я делаю
DataProcessor* proc = new DataProcessor( new FakeDataReader() );
прогоняю через него данные и проверяю получившуюся статистику.
K>Вообще почитай книжку Кента Бека про TDD и многое прояснится.
читал
не помогает