МЕТРИКИ ОБЪЕКТО ОРИНТИРОВАННОГО ПРОГРАМИРОВАНИЯ. Практическая работа. Практическая работа Экстремальное программирование и разработка через тестирование (testdriven development, tdd)
Скачать 327.73 Kb.
|
Практическая работа Экстремальное программирование и разработка через тестирование (test-driven development, TDD). Обобщенные структуры в C# Теоретическая часть Экстремальное программирование предполагает создание программных проектов как адаптационных процессов открытия и разработки без обременительного и ограничивающего перспективы планирования. TDD (разработка через тестирование) и его последнее воплощение, BDD, являются двумя очевидными примерами экстремального программирования. Идея заключается в разработке программного обеспечения с изначального описания примеров желаемого поведения (известного как тестыили спецификации). Инструменты для модульного тестированияпозволяют определить поведение отдельных классов или других небольших частей кода в изоляции. Они могут быть эффективно использованы только для программного обеспечения, которое было спроектировано как набор независимых модулей, так что каждый тест может быть запущен отдельно. Инструменты автоматизированного тестирования пользовательского интерфейса позволяют моделировать ряд взаимодействий пользователя с запущенным экземпляром приложения. Обобщение Термин обобщение означает параметризированный тип. Особая роль параметризированных типов состоит в том, что они позволяют создавать классы, структуры, интерфейсы, методы и делегаты, в которых обрабатываемые данные указываются в виде параметра. С помощью обобщений можно, например, создать единый класс, который автоматически становится пригодным для обработки разнотипных данных. Класс, структура, интерфейс, метод или делегат, оперирующий параметризированным типом данных, называется обобщенным, как, например, обобщенный класс или обобщенный метод. Основные преимущества использования обобщений: Производительность Одним из основных преимуществ обобщений является производительность. Использование типов значений с необобщенными классами коллекций вызывает упаковку (boxing) и распаковку (unboxing) при преобразовании в ссылочный тип и обратно. Типы значений сохраняются в стеке, а типы ссылок — в куче. Классы C# являются ссылочными типами, а структуры — типами значений. .NET позволяет легко преобразовывать типы значений в ссылочные, поэтому их можно использовать там, где ожидаются объекты (т.е. ссылочные типы). Преобразование типа значений в ссылочный тип называется упаковкой (boxing). Упаковка происходит автоматически, когда метод ожидает параметр ссылочного типа, а ему передается тип значений. С другой стороны, упакованный тип значений может быть обратно преобразован к простому типу значений с помощью распаковки (unboxing). При распаковке требуется операция приведения. Безопасность Другим свойством обобщений является безопасность типов. Обобщения автоматически обеспечивают типовую безопасность всех операций. В ходе выполнения этих операций обобщения исключают необходимость обращаться к приведению типов и проверять соответствие типов в коде вручную. Повторное использование двоичного кода Обобщения повышают степень повторного использования двоичного кода. Обобщенный класс может быть определен однажды, и на его основе могут быть созданы экземпляры многих типов. При этом не нужно иметь доступ к исходным текстам. Общая форма объявления обобщенного класса выглядит следующим образом: class имя_класса<список_параметров_типа> { // ... Синтаксис объявления ссылки на обобщенный класс: имя_класса<список_аргументов_типа> имя_переменной = new имя_класса<список_параметров_типа> (список_аргументов_конструктора); ПРИМЕР 1: Пример программы, использующей несколько обобщенных классов: Когда для класса MyObj указывается аргумент типа, например int или string, то создается так называемый в C# закрыто сконструированный тип. В частности, MyObj Иерархии обобщенных классов
Обобщенные классы могут входить в иерархию классов аналогично необобщенным классам. Следовательно, обобщенный класс может действовать как базовый или производный класс. Главное отличие между иерархиями обобщенных и необобщенных классов заключается в том, что в первом случае аргументы типа, необходимые обобщенному базовому классу, должны передаваться всеми производными классами вверх по иерархии аналогично передаче аргументов конструктора. В производном классе следует непременно указывать параметры типа, требующиеся его обобщенному базовому классу, даже если этот производный класс не обязательно должен быть обобщенным. Разумеется, в производный класс можно свободно добавлять его собственные параметры типа, если в этом есть потребность. ПРИМЕР 2: Пример программы создания сложной иерархии обобщенных классов: Значения по умолчанию При написании обобщенного кода иногда важно провести различие между типами значений и ссылочными типами. Такая потребность возникает, в частности, в том случае, если переменной параметра типа должно быть присвоено значение по умолчанию. Для ссылочных типов значением по умолчанию является null, для неструктурных типов значений — 0 или логическое значение false, если это тип bool, а для структур типа struct — объект соответствующей структуры с полями, установленными по умолчанию. В этой связи возникает вопрос: какое значение следует присваивать по умолчанию переменной параметра типа: null, 0 или нечто другое? Например, если в следующем объявлении класса Test: class Test Т obj ; // ... переменной obj требуется присвоить значение по умолчанию, то какой из двух вариантов следует выбрать?: obj = null; // подходит только для ссылочных типов obj =0; // подходит только для числовых типов перечислений, но не для структур Для разрешения этого вопроса можно воспользоваться формой оператора default, приведенной ниже: default(тип) Эта форма оператора default пригодна для всех аргументов типа, будь то типы значений или ссылочные типы. ПРИМЕР 3: Использование оператора default Статические члены Статические члены обобщенного класса разделяются только одним экземпляром класса. Рассмотрим пример, в котором класс StaticDemo public class StaticDemo<T> { public static int x; } Поскольку класс StaticDemo StaticDemo<string>.x = 4; StaticDemo<int>.x = 5; Console.WriteLine(StaticDemo<string>.x); // записывает 4 Обобщенные методы В дополнение к обобщенным классам можно также определять обобщенные методы. В объявлении обобщенного метода присутствует обобщенный тип. Обобщенные методы могут быть определены внутри необобщенного класса. В методах, объявляемых в обобщенных классах, может использоваться параметр типа из данного класса, а следовательно, такие методы автоматически становятся обобщенными по отношению к параметру типа. Но помимо этого имеется возможность объявить обобщенный метод со своими собственными параметрами типа и даже создать обобщенный метод, заключенный в необобщенном классе. Общая форма объявления обобщенного метода: возвращаемый_тип имя_метода<список_параметров_типа> (список_параметров) {// ... В любом случае список_параметров_типа обозначает разделяемый запятой список параметров типа. Обратите внимание на то, что в объявлении обобщенного метода список параметров типа следует после имени метода. ПРИМЕР 5: Использование обобщенного метода В данном примере используется обобщенный метод Info Обобщенные структуры Подобно классам, структуры также могут быть обобщенными. Они очень похожи на обобщенные классы, за исключением возможности наследования. Рассмотрим обобщенную структуру Nullable Число в базе данных и число в языке программирования имеют важное отличие в своих характеристиках, поскольку число в базе данных может быть null. Число в C# не может быть null. Проблема существует не только с базами данных, но также с отображением данных XML на типы .NET. Это отличие часто служит источником сложностей и требует массы дополнительной работы по отображению данных. Одно из решений состоит в отображении чисел из баз данных и файлов XML на ссылочные типы, потому что ссылочные типы могут иметь значение null. Однако это также означает дополнительные накладные расходы во время выполнения. За счет использования структуры Nullable ПРИМЕР 6: Поле типа bool hasValue определяет, установлено значение или же оно равно null. Помимо этого, обобщенная структура определяет доступные только для чтения свойства HasValue и Value, а также перегрузки некоторых операций. Перегрузка операции приведения Nullable Обобщенные делегаты Как и методы, делегаты также могут быть обобщенными. Общая форма объявления обобщенного делегата: delegate возвращаемый_тип имя_делегата<список_параметров_типа>(список_аргументов); Обратите внимание на расположение списка параметров типа. Он следует непосредственно после имени делегата. Преимущество обобщенных делегатов заключается в том, что их допускается определять в типизированной обобщенной форме, которую можно затем согласовать с любым совместимым методом. Обобщенные делегаты предоставляют более гибкий способ спецификации метода, подлежащего вызову в безопасной к типам манере. ПРИМЕР 7: Применение обобщенных делегатов: Обобщенные интерфейсы Помимо обобщенных классов и методов, в C# допускаются обобщенные интерфейсы. Такие интерфейсы указываются аналогично обобщенным классам. Применяя обобщения, можно определять интерфейсы, объявляющие методы с обобщенными параметрами. ПРИМЕР 8: Применение обобщенных интерфейсов: В данном примере объявляется обобщенный интерфейс ISort в котором используется метод ReWrite(). Класс MyObj реализует данный интерфейс, при этом данный класс должен иметь такое же обобщение, как и интерфейс который он реализует. Обратите внимание, что обобщенный интерфейс ISort использует ограничение на тип значения, поэтому данное ограничение необходимо указать и при объявлении класса MyObj. В методе Main() создается несколько разнотипных экземпляров данного класса, и используется реализованный метод ReWrite(). Сравнение экземпляров параметра типа Для сравнения двух объектов параметра обобщенного типа должны реализовываться интерфейсы IComparable или IComparable Интерфейс IEquatable public interface IEquatable Сравниваемый тип данных передается ему в качестве аргумента типа Т. В этом интерфейсе определяется метод Equals(), как показано ниже: bool Equals(Т other) В этом методе сравниваются вызывающий объект и другой объект, определяемый параметром other. В итоге возвращается логическое значение true, если оба объекта равны, а иначе — логическое значение false. В ходе реализации интерфейса IEquatable ПРИМЕР 9: реализуем программу для создания простейшей таблицы базы данных, в которой будут определены поля id, login, password пользователя, при этом обеспечим возможность сортировки полей по любому заголовку: Переопределение виртуальных методов в обобщенном классе В обобщенном классе виртуальный метод может быть переопределен таким же образом, как и любой другой метод. ПРИМЕР 10: Реализуем программу, в которой переопределяется виртуальные методы GetObj() и ToString(): Ковариантность и контравариантность делегатов
Делегаты становятся еще более гибкими средствами программирования благодаря двум свойствам: ковариантности и контравариантности. Как правило, метод, передаваемый делегату, должен иметь такой же возвращаемый тип и сигнатуру, как и делегат. Но в отношении производных типов это правило оказывается не таким строгим благодаря ковариантности и контравариантности. В частности, ковариантность позволяет присвоить делегату метод, возвращаемым типом которого служит класс, производный от класса, указываемого в возвращаемом типе делегата. А контравариантность позволяет присвоить делегату метод, типом параметра которого служит класс, являющийся базовым для класса, указываемого в объявлении делегата. ПРИМЕР 11: Программа демонстрирующая ковариантность и контравариантность: Ковариантность и контравариантность в обобщениях Применительно к обобщенному интерфейсу ковариантность служит средством, разрешающим методу возвращать тип, производный от класса, указанного в параметре типа. В прошлом возвращаемый тип должен был в точности соответствовать параметру типа в силу строгой проверки обобщений на соответствие типов. Ковариантность смягчает это строгое правило таким образом, чтобы обеспечить типовую безопасность. Параметр ковариантного типа объявляется с помощью ключевого слова out, которое предваряет имя этого параметра. Ниже приведен очень простой интерфейс IMyInfo, в котором применяется ковариантность: // В этом обобщенном интерфейсе поддерживается ковариантность, public interface IMyInfo<out Т> { T GetObject(); } Обратите особое внимание на то, как объявляется параметр обобщенного типа Т. Его имени предшествует ключевое слово out. В данном контексте ключевое слово out обозначает, что обобщенный тип T является ковариантным. А раз он ковариантный, то метод GetObject() может возвращать ссылку на обобщенный тип T или же ссылку на любой класс, производный от типа Т. На применение ковариантности накладываются некоторые ограничения. Ковариантность параметра типа может распространяться только на тип, возвращаемый методом. Следовательно, ключевое слово out нельзя применять в параметре типа, служащем для объявления параметра метода. Применительно к обобщенному интерфейсу контравариантность служит средством, разрешающим методу использовать аргумент, тип которого относится к базовому классу, указанному в соответствующем параметре типа. В прошлом тип аргумента метода должен был в точности соответствовать параметру типа в силу строгой проверки обобщений на соответствие типов. Контравариантность смягчает это строгое правило таким образом, чтобы обеспечить типовую безопасность. Параметр контравариантного типа объявляется с помощью ключевого слова in, которое предваряет имя этого параметра. Ниже приведен обобщенный интерфейс IMyContraVarGenlF контравариантного типа. В нем указывается контравариантный параметр обобщенного типа Т, который используется в объявлении метода Show(): // Это обобщенный интерфейс, поддерживающий контравариантность. public interface IMyContraVarGenlFc<in Т> { void Show(T obj); } Как видите, обобщенный тип T указывается в данном интерфейсе как контравариантный с помощью ключевого слова in, предшествующего имени его параметра. Обратите также внимание на то, что T является параметром типа для аргумента obj в методе Show(). Контравариантность оказывается пригодной только для ссылочных типов, а параметр контравариантного типа можно применять только к аргументам методов. Следовательно, ключевое слово in нельзя указывать в параметре типа, используемом в качестве возвращаемого типа. ПРИМЕР 12: Программа, реализующая использование ковариантности и контравариантности интерфейсов |