Обновить
240
Сергей Тепляков@SergeyT

Пользователь

377
Подписчики
Отправить сообщение
Вот держу перед глазами книгу Роберта Мартина «Принципы, паттерны и методики гибкой разработки на языке C#», страница 173:
«Принцип подстановки Лисков (Liskov Substitution Principle).
Должна быть возможность вместо базового типа подставить любой его подтип.

Ваше же определение взято из его оригинальной статьи, опубликованной в C++ Report и привязано к языку С++, приведенной мною определение адаптировано автором к языку C#.
На всякий случай напомню, что HashSet не обязательно добавляет элемент. Нарушает ли он тем самым LSP?
Дык, так же стимул есть;)
Обоснуйте плз, поскольку если класс DoubleList нарушает LSP, то так же делает и HashSet, и SortedList, а раз LSP нарушается классами из BCL, то чем DoubleList хуже? ;)
А если серьезно, то все таки почитайте «большую запутанную статью», выводы в ней никуда ничем не притянуты.
Да, вы правы. В приведенном случае мы правда получим «222», но это не будет проблемой, поскольку текст будет валидным и в том и в другом случае (да, это плохо, но не смертельно).

А вот если в текст боксе каким-то образом попадет невалидный текст, например «ааа», то при выделении любого из символов «а» и нажатием на «1», мы получим «111», что пройдет валидацию текста, в результате мы получим «1аа».

Более корректно реализовать метод CreateFullText как-то так:

private static string CreateFullText(TextBox textBox, string inputText)
{
string text;
if (textBox.SelectionLength > 0)
{
// Replacing text box's selected text with text from clipboard
text = textBox.Text.Remove(textBox.CaretIndex, textBox.SelectionLength);
text = text.Insert(textBox.CaretIndex, inputText);
}
else
{
// If we don't have selected text we should insert clipboard text
// into the caret index.
text = textBox.Text.Insert(textBox.CaretIndex, inputText);
}

return text;
}


Т.е. вначале «снести» выделенный текст, а уже потом вставить новый текст согласно положению курсора.
А у меня класс и называется TextBoxBehavior. Или вы не об этом?
Видимо, сам придумал;)
Это был не я! это был однорукий человек! (с) Маска
Часть из этих зависимостей будет примитивными, типа примитивных типов данных или простых классов данных. С ними, ессно, делать ничего не нужно. А если класс зависит от 8 других тяжелых сущностей, которые нужно мокать, то это признак того, что с дизайном чего-то не то.
Да, нужно разбить класс на более мелкие, объединить все зависимости в одну сущность (если это имеет смысл) и поднять зависимостям уровень абстракции.
Я правильно понимаю, что вы покрываете тестами и подобный код:

class MyClass
{
private object someData;

pulbic MyClass(object someData)
{
if (someData == null)
throw new ArgumentNullException("someData");

this.someData = someData;
}

public override string ToString()
{
return someData.ToString();
}
}


И теперь в тесте:
[Test(ExpectedException(typeof(ArgumentNullException))]
public void MyClass_Constructor_Failure()
{
var myClass = new MyClass(null);
}

[Test]
public void MyClass_Custructor_Success()
{
var myClass = new MyClass(new object());
}

[Test]
public void MyClass_ToString_CallsObjectToString()
{
object o = new object();
var myClass = new MyClass(o);
Assert.AreEquals(myClass.ToString(), o.ToString());
}


Ведь если этого не сделать, то 100% покрытия тестами у вас не будет:) Я, например, первый вариант предпочитаю «проверять» с помощью контрактов и статического анализатора, в результате, если что-то пойдет не так, то у меня код просто не скомпилится (ну, скомпилится, но я ошибку я увижу во время компиляции). Кроме того, натравите на свой собственный код какой-нить анализатор, у вас ведь тоже не 100%;)

Я продолжу свою мысль (не столь категоричную, как вашу): тестов должно быть _достаточно_.

Знаете, что самое интересное: Кент Бек — небезызвестный папа TDD, не является фанатом 100% покрытия тестами (пруф в подкасте от SERadio). Не все тесты приносят что-то полезное, они могут и захламлять код тестов, пряча при этом за грудой ненужных тестов парочку важных, которые найти в подобном хаосе будет не просто.

Так что да, я не хочу идеального качества, потому что идеальное качество требует бесконечного количества усилий для его достижения;) Прагматизм рулит и юнит-тесты в этом вопросе не исключение.
Да, там все ок, поскольку в интерфейсе с которым работает вью-модель нет свойства SavedUserName.
Очепятку в коде я поправил, смысла тестировать, что mock.SavedUserName равен mock.SavedUserName мало смысла. А вот сравнить это поле со значением UserName вью-модели смысл имеет.

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

Да, и +100 500 по поводу, что все тестировать не нужно. Разумно тестировать только наиболее важные части кода. При этом, кстати, не обязательно, что наиболее важным является поведение; иногда это так, иногда, важным является состояние. Но идея, правильное.

В одном из подкастов Кент Бек выразил очень прикольную мысль о том, что каждый тест должен рассказывать полезную и интересную историю о тестируемом классе, чтобы читатель этого теста мог не только понять, что «ага, эта хрень, кажись, работает», а и то, что именно она делает полезного в данном конкретном случае. Т.е. тест — это еще и отличный источник спецификации.
Есть два подхода к проектированию ПО. Один из них называется «outside-in approach», описанный в частности в книге “Growing Object Oriented Software”. В нем предлагается начать разработку с UI и двигаться в сторону бизнес-логики. Есть и другой подход, который называется «inside-out», в котором проектирование начинается с бизнес-логики и уже от нее двигаться в сторону UI и базы данных.

Возможно есть приложения, в которых второй подход не работает, что-то типа сверх тонких клиентов, но в большинстве случаев, подход inside-out будет прекрасно работать. Да, проектированием UI приложения двигает именно UI, но даже если брать разработку сложного UI-приложения, то подход inside-out все еще возможен: бизнес-аналитик, архитектор, заказчик определяет желаемое поведение UI-й части. Затем, с помощью MVXXX паттернов дербанится на части представление и логика представления; после чего разрабатывается именно логика представления на которую уже натягивается сама вьюха.

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

У метода Generate не такая уж и большая область применения. Ведь Rx — это больше о потреблении и преобразовании push-последовательностей, а не о их генерации. Так что я не знаю, что еще про него рассказывать.

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

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

З.Ы. Rxx — это Extensions to Reactive Extensions. Сами же реактивные расширения — это Rx.
Да, сори. С просоня понял фразу неправильно.
Что значит скоро? Она уже давно доступен внутри Visual Studio 2011 Developer Preview
Я поторопился. TrimExcess в этом случае вообще не подходит. Вот, что сказано по приведеннной вами же ссылке:

This method can be used to minimize a collection's memory overhead if no new elements will be added to the collection. The cost of reallocating and copying a large List can be considerable, [b]however, so the TrimExcess method does nothing if the list is at more than 90 percent of capacity[/b]. This avoids incurring a large reallocation cost for a relatively small gain.


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

На самом деле любая сериализация, которая сериализирует внутреннее представление объекта нарушает его инкапсуляцию. Ведь закрытые члены потому и сделаны закрытыми, чтобы к ним не имел доступа внешний код. Сериализация обходит это ограничение (да, в некоторых случаях это является ее сутью) и использует внутреннее представление объекта для своей работы. К примеру, xml сериализация не содержит этой проблемы, поскольку XmlSerializer сериализирует только видимую часть объекта (т.е. его абстракцию, а не реализацию).

Еще раз напомню, что я подразумеваю под дырявой абстракцией в данном случае: если нам приходится думать о том, как класс реализован, то он дырявый. Можно ли рассматривать то, что при сериализации списка перечислений пользователю этого кода просачивается внутреннее представление абстракции? Думаю, что да. Ведь в данном случае эта проблема уже не описывается его публичным контрактом, а связано с его внутренней реализацией.
Абстракция — это IEnumerable, которая моделирует последовательность. Массив — это такая же специализация этой абстракции, что и двусвязный список.

Еще раз напомню, что мы живем не в идеальном мире, и такой вопрос, как согласованность кода тоже может играть важную роль. Если у нас уже есть десяток контактов, каждый из которых уже использует типы List, то в одном месте использовать массив или любой другой тип будет, как минимум, не последовательно.

З.Ы. Наименьший общий знаменатель у всех коллекций — это IEnumerable. Массив подходит лишь тем, что он является наиболее распростаренной коллекцией, и вы получите максимальную универсальность вашего интерфейса.

Информация

В рейтинге
Не участвует
Откуда
Washington, США
Зарегистрирован
Активность