Главная страница

МЕТРИКИ ОБЪЕКТО ОРИНТИРОВАННОГО ПРОГРАМИРОВАНИЯ. Практическая работа. Практическая работа Экстремальное программирование и разработка через тестирование (testdriven development, tdd)


Скачать 327.73 Kb.
НазваниеПрактическая работа Экстремальное программирование и разработка через тестирование (testdriven development, tdd)
АнкорМЕТРИКИ ОБЪЕКТО ОРИНТИРОВАННОГО ПРОГРАМИРОВАНИЯ
Дата14.11.2022
Размер327.73 Kb.
Формат файлаdocx
Имя файлаПрактическая работа.docx
ТипПрактическая работа
#787729

Практическая работа

Экстремальное программирование и разработка через тестирование (test-driven development, TDD). Обобщенные структуры в C#

Теоретическая часть
Экстремальное программирование предполагает создание программных проектов как адаптационных процессов открытия и разработки без обременительного и ограничивающего перспективы планирования.

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

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

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

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

Основные преимущества использования обобщений:

Производительность

Одним из основных преимуществ обобщений является производительность. Использование типов значений с необобщенными классами коллекций вызывает упаковку (boxing) и распаковку (unboxing) при преобразовании в ссылочный тип и обратно.

Типы значений сохраняются в стеке, а типы ссылок — в куче. Классы C# являются ссылочными типами, а структуры — типами значений. .NET позволяет легко преобразовывать типы значений в ссылочные, поэтому их можно использовать там, где ожидаются объекты (т.е. ссылочные типы).

Преобразование типа значений в ссылочный тип называется упаковкой (boxing). Упаковка происходит автоматически, когда метод ожидает параметр ссылочного типа, а ему передается тип значений. С другой стороны, упакованный тип значений может быть обратно преобразован к простому типу значений с помощью распаковки (unboxing). При распаковке требуется операция приведения.

Безопасность

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

Повторное использование двоичного кода

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

Общая форма объявления обобщенного класса выглядит следующим образом:

class имя_класса<список_параметров_типа> { // ...

Синтаксис объявления ссылки на обобщенный класс:

имя_класса<список_аргументов_типа> имя_переменной =

new имя_класса<список_параметров_типа> (список_аргументов_конструктора);
ПРИМЕР 1: Пример программы, использующей несколько обобщенных классов:


Когда для класса MyObj указывается аргумент типа, например int или string, то создается так называемый в C# закрыто сконструированный тип. В частности, MyObj является закрыто сконструированным типом. Ведь, по существу, такой обобщенный тип, как MyObj, является абстракцией. И только после того, как будет сконструирован конкретный вариант, например MyObj, создается конкретный тип. А конструкция, подобная MyObj, называется в C#открыто сконструированным типом, поскольку в ней указывается параметр типа Т, но не такой конкретный тип, как int.
Иерархии обобщенных классов




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

В производном классе следует непременно указывать параметры типа, требующиеся его обобщенному базовому классу, даже если этот производный класс не обязательно должен быть обобщенным. Разумеется, в производный класс можно свободно добавлять его собственные параметры типа, если в этом есть потребность.
ПРИМЕР 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 используется как с типом string, так и с типом int, существуют два набора статических полей:

StaticDemo<string>.x = 4;

StaticDemo<int>.x = 5;

Console.WriteLine(StaticDemo<string>.x); // записывает 4
Обобщенные методы

В дополнение к обобщенным классам можно также определять обобщенные методы. В объявлении обобщенного метода присутствует обобщенный тип. Обобщенные методы могут быть определены внутри необобщенного класса.

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

Общая форма объявления обобщенного метода:

возвращаемый_тип имя_метода<список_параметров_типа> (список_параметров) {// ...

В любом случае список_параметров_типа обозначает разделяемый запятой список параметров типа. Обратите внимание на то, что в объявлении обобщенного метода список параметров типа следует после имени метода.

ПРИМЕР 5: Использование обобщенного метода



В данном примере используется обобщенный метод Info выводящий информацию об объекте. При этом в данном методе используется ограничение на базовый класс User с помощью оператора where. В производном классе UserPass переопределяется метод ToString(), в результате чего, в консоли отображается информация о пользователе.
Обобщенные структуры

Подобно классам, структуры также могут быть обобщенными. Они очень похожи на обобщенные классы, за исключением возможности наследования. Рассмотрим обобщенную структуру Nullable, которая определена в .NET Framework.

Число в базе данных и число в языке программирования имеют важное отличие в своих характеристиках, поскольку число в базе данных может быть null. Число в C# не может быть null. Проблема существует не только с базами данных, но также с отображением данных XML на типы .NET.

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

За счет использования структуры Nullable эта проблема может быть легко решена. Структура Nullable определяет ограничение, которое состоит в том, что обобщенный тип T должен быть структурой. С классами в качестве обобщенных типов преимущество минимальных накладных расходов исчезло бы, и поскольку объекты классов все равно могут быть null, то в использовании класса с типом Nullable смысла нет.

ПРИМЕР 6:



Поле типа bool hasValue определяет, установлено значение или же оно равно null. Помимо этого, обобщенная структура определяет доступные только для чтения свойства HasValue и Value, а также перегрузки некоторых операций. Перегрузка операции приведения Nullable к T определена явно, так как она может генерировать исключение в случае, если hasValue равно false. Перегрузка операции для приведения к Nullable определена неявно, потому что она всегда успешна. В этом примере экземпляр Nullable создан как Nullable. Переменная х теперь может быть использована как int, т.е. ей можно присваивать значения и применять в операциях для выполнения некоторых вычислений. 
Обобщенные делегаты

Как и методы, делегаты также могут быть обобщенными. Общая форма объявления обобщенного делегата:

delegate возвращаемый_тип имя_делегата<список_параметров_типа>(список_аргументов);

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

Обобщенные делегаты предоставляют более гибкий способ спецификации метода, подлежащего вызову в безопасной к типам манере.
ПРИМЕР 7: Применение обобщенных делегатов:


Обобщенные интерфейсы
Помимо обобщенных классов и методов, в C# допускаются обобщенные интерфейсы. Такие интерфейсы указываются аналогично обобщенным классам. Применяя обобщения, можно определять интерфейсы, объявляющие методы с обобщенными параметрами.
ПРИМЕР 8: Применение обобщенных интерфейсов:

В данном примере объявляется обобщенный интерфейс ISort в котором используется метод ReWrite(). Класс MyObj реализует данный интерфейс, при этом данный класс должен иметь такое же обобщение, как и интерфейс который он реализует. Обратите внимание, что обобщенный интерфейс ISort использует ограничение на тип значения, поэтому данное ограничение необходимо указать и при объявлении класса MyObj. В методе Main() создается несколько разнотипных экземпляров данного класса, и используется реализованный метод ReWrite().

Сравнение экземпляров параметра типа
Для сравнения двух объектов параметра обобщенного типа должны реализовываться интерфейсы IComparable или IComparable и/или интерфейс IEquatable. В обоих вариантах интерфейса IComparable для этой цели определен метод CompareTo(), а в интерфейсе IEquatable — метод Equals(). Разновидности интерфейса IComparable предназначены для применения в тех случаях, когда требуется определить относительный порядок следования двух объектов. А интерфейс IEquatable служит для определения равенства двух объектов. Все эти интерфейсы определены в пространстве имен System и реализованы во встроенных в C# типах данных, включая int, string и double. Но их нетрудно реализовать и для собственных создаваемых классов. Итак, начнем с обобщенного интерфейса IEquatable.

Интерфейс IEquatable объявляется следующим образом:

public interface IEquatable

Сравниваемый тип данных передается ему в качестве аргумента типа Т. В этом интерфейсе определяется метод Equals(), как показано ниже:

bool Equals(Т other)

В этом методе сравниваются вызывающий объект и другой объект, определяемый параметром other. В итоге возвращается логическое значение true, если оба объекта равны, а иначе — логическое значение false.

В ходе реализации интерфейса IEquatable обычно требуется также переопределять методы GetHashCode() и Equals(Object), определенные в классе Object, чтобы они оказались совместимыми с конкретной реализацией метода Equals(). 
ПРИМЕР 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: Программа, реализующая использование ковариантности и контравариантности интерфейсов



написать администратору сайта