Объектно-ориентированные технологии программирования и стандарты проектирования

Содержание

Слайд 2

Контакты:
[email protected]
http://vk.com/kirill.surkov

Контакты: kirill.surkov@gmail.com http://vk.com/kirill.surkov

Слайд 3

Литература

Данная презентация служит методикой изучения предмета и содержит основные понятия ООП и

Литература Данная презентация служит методикой изучения предмета и содержит основные понятия ООП
их представление на языке C#.
«C# Language Specification»: C:\Program Files (x86)\Microsoft Visual Studio 12.0\ VC#\Specifications\1033\CSharp Language Specification.docx
Конспект лекций по языку программирования C++ для студентов, владеющих языком программирования C#, Java или Delphi. Авторы: К.А. Сурков, Д.А. Сурков, Ю.М. Четырько.
«The Java Language Specification»: http://docs.oracle.com/javase/specs/jls/se7/jls7.pdf
Краткое изложение Java для специалистов, владеющих другими объектно-ориентированными языками программирования: http://en.wikipedia.org/wiki/Java_syntax
«Программирование на языке Delphi». Учебное пособие. Авторы: А.Н. Вальвачев, К.А. Сурков, Д.А. Сурков, Ю.М. Четырько. Книга расположена по адресу http://rsdn.ru/?summary/3165.xml
«The Programming Language Oberon». Niklaus Wirth: http://www.inf.ethz.ch/personal/wirth/Oberon/Oberon07.Report.pdf
«Zonnon Language Report». Jurg Gutknecht http://zonnon.ethz.ch/archive/znnLanguageReportv04y090606draft.pdf
«Язык ДРАКОН». Владимир Паронджанов: http://drakon-practic.ru/drakon.pdf – краткое описание, http://drakon.pbworks.com/w/page/18205516/FrontPage – полное описание.

Слайд 4

Базовые понятия структурного программирования

Переменная – ячейка данных
Тип данных – допустимые значения и

Базовые понятия структурного программирования Переменная – ячейка данных Тип данных – допустимые
операции переменной
Указатель – переменная, содержащая адрес
Оператор – единица выполнения алгоритма
Процедура – вызываемая на выполнение подпрограмма
Процедурная переменная – указатель на процедуру
Модуль – единица разработки и доставки программы
Объект – динамический модуль
Исключение – событие и объект с информацией об ошибке

Слайд 5

Модуль

Модуль – единица разработки, применения и поставки.
public static class Helper // модуль
{

Модуль Модуль – единица разработки, применения и поставки. public static class Helper
public static int Min(int x, int y) // процедура модуля
{
int result;
if (x < y)
result = x;
else
result = y;
return result;
}
public static void Test()
{
int n = Min(10, 20);
Console.WriteLine("Min = {0}", n);
}
}

Слайд 6

Разграничение доступа к модулям

Разграничение доступа к модулю осуществляется с помощью ключевых слов:
public

Разграничение доступа к модулям Разграничение доступа к модулю осуществляется с помощью ключевых
– доступ к модулю получают все;
protected – доступ к модулю получают данный модуль и модули расширения;
internal – доступ к модулю получают данный модуль и программы, в которых данный модуль подключается на уровне исходного кода;
protected internal – доступ к модулю получают модули расширения и программы, в которых данный модуль подключается на уровне исходного кода;
private – доступ к модулю получают лишь процедуры этого модуля.
Примеры модулей и процедур с различными режимами доступа:
public static class Module1
{
public static void Procedure1() { ... }
internal static void Procedure2() { ... }
private static void Procedure3() { ... }
}
internal static class Module2
{
public static void Procedure4() { ... }
}

Слайд 7

Объект – динамический модуль

Объект – динамический модуль, у которого может быть много

Объект – динамический модуль Объект – динамический модуль, у которого может быть
экземпляров. Описание динамического модуля – класс.
public class List // динамический модуль
{
public void Add(object item) { ... }
public void RemoveAt(int index) { ... }
public int GetCount() { ... }
public object GetItem(int index) { ... }
}
public static class Helper // статический модуль
{
public static void Test()
{
var list = new List(); // создание модуля
list.Add("Андрей Ершов");
list.Add("Niklaus Wirth");
Console.WriteLine("Count = {0}", list.GetCount());
}
}

Слайд 8

Исключение

Исключение – событие и объект с информацией об ошибке.
Исключение создается:
Аппаратно – в

Исключение Исключение – событие и объект с информацией об ошибке. Исключение создается:
результате ошибки выполнения оператора.
Программно – с помощью оператора создания исключения:
throw new OutOfMemoryException("Не хватает памяти");
Некоторые стандартные исключения модуля System:
Exception
ArithmeticException
DivideByZeroException
OverflowException
ArrayTypeMismatchException
IndexOutOfRangeException
InvalidCastException

NullReferenceException
OutOfMemoryException
StackOverflowException
TypeInitializationException
NotSupportedException
NotImplementedException

Слайд 9

Обработка исключений

Обработка исключения:
try
{
// операторы, подверженные исключениям
}
catch
{
// операторы обработки исключения
}
Частичная обработка

Обработка исключений Обработка исключения: try { // операторы, подверженные исключениям } catch
исключения:
try
{
// операторы, подверженные исключениям
}
catch
{
// операторы обработки исключения
throw; // передача обработки внешнему блоку try...catch
}

Слайд 10

Классификация исключений

Распознавание класса исключения:
try
{
// арифметические вычисления
}
catch (DivideByZeroException e)
{
// обработка деления

Классификация исключений Распознавание класса исключения: try { // арифметические вычисления } catch
на ноль
}
catch (OverflowException e)
{
// обработка переполнения
}
catch (ArithmeticException e)
{
// обработка другой арифметической ошибки
}
catch (Exception e)
{
// обработка любого иного исключения
}

Слайд 11

Защита ресурсов от исключений

Защита ресурсов от исключения:
// запрос ресурса
try
{
// работа с

Защита ресурсов от исключений Защита ресурсов от исключения: // запрос ресурса try
ресурсом
}
finally
{
// освобождение ресурса
}
Пример:
var stream = new FileStream("MyFile.dat", FileMode.Open, FileAccess.Read);
try
{
stream.Read(buffer, 0, buffer.Length);
}
finally
{
stream.Dispose(); // эквивалентно stream.Close();
}

Слайд 12

Защита ресурсов от исключений

С помощью оператора using:
using (var stream = new FileStream(...))
{

Защита ресурсов от исключений С помощью оператора using: using (var stream =
stream.Read(buffer, 0, buffer.Length);
}
Комплексный вариант оператора try:
// запрос ресурса
try
{
// работа с ресурсом
}
catch (Exception e)
{
// обработка исключения
}
finally
{
// освобождение ресурса
}

Слайд 13

Базовые понятия объектно-ориентированного программирования

Класс – тип данных для создания объектов
Объект – экземпляр

Базовые понятия объектно-ориентированного программирования Класс – тип данных для создания объектов Объект
класса
Метод – процедура над объектом
Конструктор и деструктор – особые методы
Свойство – виртуальное поле
Наследование – расширение класса
Виртуальный метод – переопределяемый метод
Делегат – ссылка на метод
Событие – список делегатов
Интерфейс – описание класса без реализации
Шаблон – параметризованный класс
Атрибут – метаданные, механизм рефлексии

Слайд 14

Класс и объект

Класс – тип данных для создания объектов:
public class DelimitedReader
{
public

Класс и объект Класс – тип данных для создания объектов: public class
void Open(string fileName, char delimiter) { ... }
public bool NextLine() { ... }
public bool IsEndOfFile() { ... }
public string[] Items;
private string FileName;
private char Delimiter;
}
Объект – экземпляр класса:
var reader = new DelimitedReader();
reader.Open("MyFile.csv", ';');
while (reader.NextLine())
{
Console.WriteLine("{0}", reader.Items[0]); // выводим первый столбец
}

Слайд 15

Метод

Метод:
public class DelimitedReader
{
public void AssignFields()
{
FileName = "MyFile.csv";
this.Delimiter =

Метод Метод: public class DelimitedReader { public void AssignFields() { FileName =
';';
}
private string FileName;
private char Delimiter;
}
Процедура, эквивалентная методу:
public static void AssignFields(this DelimitedReader reader)
{
reader.FileName = "MyFile.csv";
reader.Delimiter = ';';
}

Слайд 16

Конструктор и деструктор

Конструктор и деструктор – особые методы:
public class DelimitedReader
{
public DelimitedReader(string

Конструктор и деструктор Конструктор и деструктор – особые методы: public class DelimitedReader
fileName, char delimiter)
{
FileName = filename;
Delimiter = delimiter;
}
~DelimitedReader()
{
...
}
...
}
Вызов конструктора:
var reader = new DelimitedReader("MyFile.csv", ';');

Слайд 17

Свойство

Свойство – виртуальное поле:
public class DelimitedReader
{
public bool Active { get {

Свойство Свойство – виртуальное поле: public class DelimitedReader { public bool Active
return fActive; } set { SetActive(value); } }
private bool fActive;
private void SetActive(bool value) { ... }
...
}
Работа со свойством внешне не отличается от работы с полем:
reader.Active = true;
...
if (reader.Active)
{
...
}
Назначение свойств – создание побочных эффектов при обращении, например, открытие файла при установке свойства Active в true.

Слайд 18

Свойство

Свойство с автоматически создаваемым полем для него:
public class DelimitedReader
{
public char Delimiter

Свойство Свойство с автоматически создаваемым полем для него: public class DelimitedReader {
{ get; set; }
...
}
Свойство с разными режимами доступа вне и внутри модуля:
public class DelimitedReader
{
public char Delimiter { get; private set; }
...
}
Свойство с ограничением режима доступа – только чтение:
public class DelimitedReader
{
public char Delimiter { get; }
...
}

Слайд 19

Индексатор

Индексатор – особое свойство, предоставляющее доступ к объекту как к массиву:
public class

Индексатор Индексатор – особое свойство, предоставляющее доступ к объекту как к массиву:
DelimitedReader
{
public string this[int index]
{
get { return fItems[index]; }
set { fItems[index] = value; OnChange(); }
}
private string[] fItems;
private void OnChange() { ... }
...
}
Применение индексатора:
var reader = new DelimitedReader();
...
reader[0] = "Andrey"; // reader.Items[0]
reader[1] = "Yershov"; // reader.Items[1]
...

Слайд 20

Наследование – расширение класса

Базовая абстракция объекта для чтения текста в табличном виде:
public

Наследование – расширение класса Базовая абстракция объекта для чтения текста в табличном
class TableReader
{
public string FileName { get; private set; }
public string[] Items { get { return fItems; } }
public int ItemCount { get { return fItems.Length; } }
public TableReader(string fileName) { ... }
public void Close() { ... }
public bool NextLine() { ... }
public bool IsEndOfFile() { ... }
private StreamReader fStreamReader;
private string[] fItems;
}
Расширение базового класса для чтения таблиц формата CSV *:
public class DelimitedReader : TableReader
{
public char Delimiter { get; private set; }
public DelimitedReader(string fileName, char delimiter) { ... }
public bool NextLine() { ... }
}

* CSV – Comma-Separated Values

Слайд 21

Наследование – расширение класса

Расширение базового класса для чтения таблиц формата TXT:
public class

Наследование – расширение класса Расширение базового класса для чтения таблиц формата TXT:
FixedReader : TableReader
{
public int[] ItemWidths { get; private set; }
public FixedReader(string fileName, params int[] itemWidths) { ... }
public bool NextLine() { ... }
}
Дерево классов:

Object

TableReader

FixedReader

DelimitedReader

Слайд 22

Наследование – расширение класса

Смысл расширения типа заключается в том, чтобы определить переменную

Наследование – расширение класса Смысл расширения типа заключается в том, чтобы определить
базового типа и присваивать ей объекты любых производных типов.
TableReader reader;
reader = new DelimitedReader("MyFile.csv", ';');
reader = new FixedReader("MyFile.csv", 20, 20, 60);
Через переменную базового типа можно безопасно обращаться к полям и методам, определенным в базовом типе. Это обеспечивается благодаря бинарной совместимости всех производных типов с базовым типом.
reader.NextLine();

Слайд 23

Наследование – контроль и приведение типа

Контроль типа осуществляется с помощью оператора is:
TableReader

Наследование – контроль и приведение типа Контроль типа осуществляется с помощью оператора
reader;
...
if (reader is DelimitedReader) // или производного класса (!)
{
...
}
Приведение переменной к ожидаемому типу данных:
char c = ((DelimitedReader)reader).Delimiter;
Приведение типа с помощью оператора as:
char c = (reader as DelimitedReader).Delimiter;
Оператор as отличается от традиционного приведения типа тем, что не создает исключение, если тип не соответствует ожидаемому.
DelimitedReader delimitedReader = reader as DelimitedReader;
if (delimitedReader != null)
{
char c = delimitedReader.Delimiter;
...
}

Слайд 24

Базовый класс Object

Базовый класс System.Object содержит общие для всех объектов методы:
namespace System
{

Базовый класс Object Базовый класс System.Object содержит общие для всех объектов методы:
public class Object
{
public Object();
public virtual bool Equals(object obj);
public static bool Equals(object objA, object objB);
public virtual int GetHashCode();
public Type GetType();
protected object MemberwiseClone();
public static bool ReferenceEquals(object objA, object objB);
public virtual string ToString();
}
}

Слайд 25

Виртуальный метод

Виртуальный метод – переопределяемый в производных классах метод. Вызов виртуального метода

Виртуальный метод Виртуальный метод – переопределяемый в производных классах метод. Вызов виртуального
осуществляется в соответствии с фактическим типом объекта, к которому метод применяется:
public class Object
{
...
public virtual string ToString();
}
public class Point : Object
{
public int X { get; protected set; }
public int Y { get; protected set; }
public Point(int x, int y) { X = x; Y = y; }
...
public override string ToString()
{
return String.Format("X: {0}, Y: {1}", X, Y);
}
}
...
object obj = new Point(10, 20);
string str = obj.ToString(); // str = "X: 10, Y: 20"

Слайд 26

Механизм вызова виртуального метода

Схема вызова obj.ToString(); // obj ссылается на объект класса

Механизм вызова виртуального метода Схема вызова obj.ToString(); // obj ссылается на объект
Point

Сегмент стека

Динамическая память

Сегмент данных
программы

Сегмент кода
программы

Equals()

GetHashCode()

ToString()

PUSH EBP
MOV EBP, ESP

RET

Переменная obj

X

Y

Данные объекта класса Point

Таблица виртуальных методов класса Point

Код метода Point.ToString()

Через объектную переменную выполняется обращение к данным объекта, находящимся в области динамической памяти: MOV EAX, obj
Из данных объекта извлекается адрес таблицы виртуальных методов (первые 4 или 8 байт данных, в зависимости от разрядности CPU): MOV EBX, [EAX+0]
На основании порядкового номера виртуального метода из таблицы извлекается адрес метода: MOV EAX, [EBX+8] ; значение 8 здесь – смещение до поля ToString().
Выполняется вызов кода по извлеченному адресу: CALL EAX

Слайд 27

Абстрактный виртуальный метод

Абстрактный метод – это виртуальный метод без реализации. Для такого

Абстрактный виртуальный метод Абстрактный метод – это виртуальный метод без реализации. Для
метода в таблице виртуальных методов резервируется поле адреса, и оно устанавливается в null. Экземпляры классов, в которых существует хотя бы один не перекрытый абстрактный метод, создавать нельзя.
public class TableReader
{
...
public abstract bool NextLine();
}
В производных классах абстрактный метод перекрывается обычным образом, как и любой виртуальный метод:
public class DelimitedReader : TableReader
{
...
public override bool NextLine() { ... }
}
Чтобы можно было создать экземпляр класса, все его абстрактные методы должны быть перекрыты.

Слайд 28

Динамический виртуальный метод

В некоторых языках программирования разновидностью виртуального метода является динамический метод:
public

Динамический виртуальный метод В некоторых языках программирования разновидностью виртуального метода является динамический
class TableReader
{
...
public dynamic bool NextLine(); // не поддерживается в C# (!)
}
В производных классах динамический метод перекрывается обычным образом – с помощью зарезервированного слова override:
public class DelimitedReader : TableReader
{
...
public override bool NextLine() { ... }
}
Различие между динамическим и виртуальным методами состоит лишь в механизме вызова. Виртуальный метод вызывается максимально быстро, но ценой избыточных затрат памяти на таблицы виртуальных методов. Динамический метод вызывается дольше, но таблицы динамических методов имеют значительно более компактный вид, что способствует экономии памяти.

Слайд 29

Виртуальное свойство

Свойство можно сделать виртуальным. Тогда в производных классах перекрываются его методы

Виртуальное свойство Свойство можно сделать виртуальным. Тогда в производных классах перекрываются его
чтения и записи:
public class Point
{
public virtual int X { get; protected set; }
public virtual int Y { get; protected set; }
...
}
public class Rectangle : Point
{
public override int X { get { ... } protected set { ... } }
public override int Y { get { ... } protected set { ... } }
...
}
При перекрытии свойства нельзя изменять атрибуты доступа методов чтения и записи.

Слайд 30

Запрет на расширение

Наследование от класса можно запретить с помощью ключевого слова sealed

Запрет на расширение Наследование от класса можно запретить с помощью ключевого слова
– «запечатанный». «Запечатывание» позволяет компилятору в некоторых случаях построить более производительный программный код. Например, вызовы всех виртуальных методов «запечатанного» класса можно выполнять так, будто они не виртуальные. Это дает некоторый выигрыш в производительности. Пример «запечатанного класса»:
public sealed class DelimitedReader : TableReader
{
...
}
«Запечатать» можно отдельный виртуальный метод класса:
public class TableReader
{
public virtual bool NextLine() { ... }
}
public class DelimitedReader : TableReader
{
public sealed override bool NextLine() { ... }
}

Слайд 31

Делегат – ссылка на метод

Можно создать процедурную переменную, содержащую адрес метода. Предварительно

Делегат – ссылка на метод Можно создать процедурную переменную, содержащую адрес метода.
для такой переменной определяется тип данных, называемый делегатом:
public delegate bool NextLineDelegate();
Теперь можно объявить переменную с таким типом и присвоить ей ссылку на метод какого-то объекта (сигнатура метода должна совпадать с сигнатурой делегата):
NextLineDelegate NextLineDelegate = Form1.NextLine;
Наконец, можно сделать вызов через процедурную переменную:
NextLineDelegate();
В результате этого оператора у объекта Form1 будет вызван метод bool NextLineNotification(). Это произойдет благодаря тому, что в переменной-делегате (NextLineDelegate) хранится пара указателей: указатель на объект и указатель на код метода. Когда делегату присваивается значение (NextLineDelegate = Form1.NextLine), в нем устанавливаются сразу оба указателя.

Слайд 32

Пример применения делегата

public delegate void NextLineDelegate();
public class DelimitedReader
{
private NextLineDelegate fNextLineDelegate;
public

Пример применения делегата public delegate void NextLineDelegate(); public class DelimitedReader { private
DelimitedReader(string fileName, NextLineDelegate nextLineDelegate)
{ ...; fNextLineDelegate = nextLineDelegate; }
public bool NextLine()
{ ...
if (fNextLineDelegate != null)
fNextLineDelegate();
}
}
public class Form1
{
public void ReadTable()
{
var reader = new DelimitedReader("MyFile.csv", this.NextLine);
...
}
private void NextLine() { /* Подсчет и вывод числа прочитанных строк */ }
}

Слайд 33

Событие – список делегатов

Бывает необходим список переменных-делегатов для уведомления целого множества объектов.

Событие – список делегатов Бывает необходим список переменных-делегатов для уведомления целого множества
Он называется событием. Предварительно для переменной-события определяют тип-делегат:
public delegate bool NextLineDelegate();
Затем определяют переменную-событие:
public event NextLineDelegate NextLineEvent;
Добавление и исключение методов в списке события осуществляется с помощью операторов += и –=:
NextLineEvent += Form1.NextLine;
Наконец, можно сделать вызов через процедурную переменную:
NextLineEvent();
Вызываются все методы, зарегистрированные в списке события.

Слайд 34

Пример применения события

public delegate void NextLineDelegate();
public class DelimitedReader
{
public event NextLineDelegate NextLineEvent;

Пример применения события public delegate void NextLineDelegate(); public class DelimitedReader { public
public DelimitedReader(string fileName) { ... }
public bool NextLine()
{ ...;
NextLineDelegate nextLineEvent = NextLineEvent;
if (nextLineEvent != null)
nextLineEvent();
}
}
public class Form1
{
public void ReadTable()
{
var reader = new DelimitedReader("MyFile.csv");
reader.NextLineEvent += this.NextLine;
...
}
private void NextLine() { /* Подсчет и вывод числа прочитанных строк */ }
}

Слайд 35

Принятый формат для событий

Для событий существует удобный стандартный тип-делегат, который решено применять

Принятый формат для событий Для событий существует удобный стандартный тип-делегат, который решено
для всех событий:
public delegate void EventHandler(object sender, EventArgs e);
Первый параметр предусмотрен для того, чтобы вызываемая процедура могла обрабатывать события нескольких объектов и могла их различить при работе. Второй параметр предусмотрен для передачи любых дополнительных данных в процедуру обработки события. Таким образом, все обработчики события оказываются совместимы друг с другом.
События принято определять на основе типа EventHandler:
public event EventHandler NextLineEvent;
Или на основе параметризованного типа EventHandler:
public event EventHandler NextLineEvent;
public class NextLineEventArgs : EventArgs { ... }

Слайд 36

Обновленный пример применения события

public class DelimitedReader
{
public event EventHandler NextLineEvent;
public DelimitedReader(string

Обновленный пример применения события public class DelimitedReader { public event EventHandler NextLineEvent;
fileName) { ... }
public bool NextLine()
{ ...;
NextLineDelegate nextLineEvent = NextLineEvent;
if (nextLineEvent != null)
nextLineEvent(this, EventArgs.Empty);
}
}
public class Form1
{
public void ReadTable()
{
var reader = new DelimitedReader("MyFile.csv");
reader.NextLineEvent += this.NextLine;
...
}
private void NextLine(object sender, EventArgs e) { ... }
}

Слайд 37

Методы регистрации события

Определяя событие, можно указать методы, которые будут вызываться при добавлении

Методы регистрации события Определяя событие, можно указать методы, которые будут вызываться при
делегата в список и при исключении делегата из списка.
public class DelimitedReader
{
public event EventHandler NextLineEvent
{
add { AddDelegate(NextLineEventKey, value); }
remove { RemoveDelegate(NextLineEventKey, value); }
}
protected void AddDelegate(object key, Delegate delegate) { ... }
protected void RemoveDelegate(object key, Delegate delegate) { ... }
private static readonly object NextLineEventKey = new object();
...
}

Слайд 38

Интерфейс = объект – реализация

Из класса TableReader можно выделить программный интерфейс:
public interface

Интерфейс = объект – реализация Из класса TableReader можно выделить программный интерфейс:
ITableReader
{
string[] Items { get; }
int ItemCount { get; }
bool NextLine();
bool IsEndOfFile();
}
Класс TableReader можно определить с поддержкой этого интерфейса:
public class TableReader : Object, ITableReader
{
public string FileName { get; private set; }
public string[] Items { get { return fItems; } } // ITableReader
public int ItemCount { get { return fItems.Length; } } // ITableReader
public TableReader(string fileName) { ... }
public void Close() { ... }
public bool NextLine() { ... } // ITableReader
public bool IsEndOfFile() { ... } // ITableReader
private StreamReader fStreamReader;
private string[] fItems;
}

Слайд 39

Интерфейс

Интерфейс не может содержать поля, конструкторы, деструктор. Все элементы интерфейса по определению

Интерфейс Интерфейс не может содержать поля, конструкторы, деструктор. Все элементы интерфейса по
являются общедоступными (public) и абстрактными (abstract).
public interface ITableReader
{
string[] Items { get; }
int ItemCount { get; }
bool NextLine();
bool IsEndOfFile();
}
Интерфейс выступает дополнительной точкой доступа к объекту. Можно объявить интерфейсную переменную и присвоить ей объект, поддерживающий интерфейс переменной:
TableReader reader = new TableReader(...);
ITableReader intf = reader;
Через интерфейсную переменную можно вызывать методы интерфейса. В результате вызываются методы объекта:
intf.NextLine(); // reader.NextLine();

Слайд 40

Интерфейс

Объект может поддерживать несколько интерфейсов, что эффективно заменяет множественное наследование:

Object

TableReader

FixedReader

DelimitedReader

IDisposable

ITableReader

Интерфейс Объект может поддерживать несколько интерфейсов, что эффективно заменяет множественное наследование: Object

Слайд 41

Интерфейс

Объект может поддерживать несколько интерфейсов, что эффективно заменяет множественное наследование:
public class TableReader

Интерфейс Объект может поддерживать несколько интерфейсов, что эффективно заменяет множественное наследование: public
: Object, IDisposable, ITableReader
{
public void Dispose() { ... } // IDisposable
public string[] Items { get { return fItems; } }
public int ItemCount { get { return fItems.Length; } }
public bool NextLine() { ... }
public bool IsEndOfFile() { ... }
...
}
Реализация свойств и методов интерфейса может быть закрыта внутри класса:
public class TableReader : Object, IDisposable, ITableReader
{
void IDisposable.Dispose() { ... } // IDisposable
string[] ITableReader.Items { get { return fItems; } }
int ITableReader.ItemCount { get { return fItems.Length; } }
bool ITableReader.NextLine() { ... }
bool ITableReader.IsEndOfFile() { ... }
...
}

Слайд 42

Механизм вызова метода через интерфейс

Схема вызова intf.NextLine(); // intf ссылается на объект

Механизм вызова метода через интерфейс Схема вызова intf.NextLine(); // intf ссылается на
TableReader

Сегмент стека

Динамическая память

Сегмент данных
программы

Сегмент кода
программы

VMT

get_Items()

get_ItemCount()

NextLine()

PUSH EBP
MOV EBP, ESP

RET

Объектная переменная obj

FileName

Items

Данные объекта класса TableReader

Таблица интерфейсных методов ITableReader

Код метода

Через интерфейсную переменную выполняется обращение к данным объекта: MOV EAX, intf
Из объекта извлекается адрес таблицы методов интерфейса: MOV EBX, [EAX]
На основании порядкового номера метода интерфейса из таблицы извлекается адрес метода: MOV EAX, [EBX+8]; значение 8 здесь – смещение до поля NextLine().
Выполняется вызов кода по извлеченному адресу: CALL EAX
На вершине стека корректируется аргумент this, чтобы из значения intf он превратился в ожидаемое значение obj: SUB [SP+4], (intf–obj); значение 4 здесь – смещение до this.
Выполняется прямой переход на код метода: JMP TableReader.NextLine

Интерфейсная переменная intf

IMT

IsEndOfFile()

SUB [SP+4], (intf–obj)
JMP TableReader.NextLine

Переходник

Слайд 43

Шаблон – параметризованный класс

Можно определить класс, параметризованный типом данных. Такой класс называют

Шаблон – параметризованный класс Можно определить класс, параметризованный типом данных. Такой класс
шаблоном или обобщенным классом:
public class List
{
public List();
public void Add(T item);
public void AddRange(List collection);
public bool Remove(T item);
public T[] ToArray();
...
}
На основе шаблона можно сконструировать тип и создать переменную сконструированного типа:
var stringList = new List();
var integerList = new List();
Представление в памяти сконструированного типа зависит от типа-параметра. Представление оптимизировано для каждого отдельного скалярного типа-параметра (например, int, double и т.д.) и для всех ссылочных типов (например, object, string и т.д.).

Слайд 44

Шаблон

При описании шаблона можно ограничить возможные значения параметра-типа:
public class List
where T:

Шаблон При описании шаблона можно ограничить возможные значения параметра-типа: public class List
ListItem
{
...
public void Add(T item); // Внутри шаблона - List.Add()
...
}
Вариант шаблона с несколькими параметрами и ограничениями:
public class Dictionary
where K: IComparable, ISerializable
where V: ISerializable
{
...
public void Add(K key, V value); // Dictionary.Add()
...
}

Слайд 45

Атрибут – метаданные

Атрибуты – это метаданные, которые можно назначать элементам программы. Эти

Атрибут – метаданные Атрибуты – это метаданные, которые можно назначать элементам программы.
метаданные представляются как объекты, производные от класса Attribute.
public class HelpAttribute : Attribute
{
public string Url { get; private set; }
public string Topic { get; set; }
public HelpAttribute(string url)
{
Url = url;
}
}
Применение атрибута:
[Help("http://company.com/help/Widget.htm")]
public class Widget
{
[Help("http://company.com/help/WidgetDisplay.htm", Topic = "Dislay")]
public void Display(string text) { ... }
}

Слайд 46

Атрибут – механизм рефлексии

С помощью механизма рефлексии для каждого элемента программы –

Атрибут – механизм рефлексии С помощью механизма рефлексии для каждого элемента программы
класса, поля, свойства, метода, параметра – можно получить список атрибутов и воспользоваться данными атрибутов.
using System;
using System.Reflection;
using System.Diagnostics;
public class Program
{
static void Main()
{
ShowHelp(typeof(Widget));
ShowHelp(typeof(Widget).GetMethod("Display"));
}
static void ShowHelp(MemberInfo member)
{
HelpAttribute attr = Attribute.GetCustomAttribute(member,
typeof(HelpAttribute)) as HelpAttribute;
Process.Start(attr.Url);
}
}

Слайд 47

Дополнительные понятия

Переменная с непостоянным типом значений
Анонимная функция

Дополнительные понятия Переменная с непостоянным типом значений Анонимная функция

Слайд 48

Переменная с непостоянным типом значений

Для поддержки скриптовых языков в язык C# внесли

Переменная с непостоянным типом значений Для поддержки скриптовых языков в язык C#
возможность создавать переменные с непостоянным типом значений. Для таких переменных допустимые операции зависят от присвоенного в данный момент значения. На этапе компиляции их можно использовать в любых выражениях. Переменные с непостоянным типом значений можно интерпретировать как объекты произвольных типов. Например, можно вызывать неизвестные методы:
public static void Test(dynamic obj)
{
obj.OpenFile("MyFile.txt");
obj.NextLine();
obj.Close();
}
Результат выполнения этой процедуры зависит от того, что передано в параметре obj. Если передан объект, содержащий такие методы, тогда вызовы пройдут успешно. Если это какой-то другой объект, произойдет исключение.

Слайд 49

Анонимная функция

public delegate int FunctionDelegate(int x);
public class Program
{
public void Test()
{

Анонимная функция public delegate int FunctionDelegate(int x); public class Program { public
CallFunction(this.Negate);
CallFunction(delegate(int x) { return -x; });
CallFunction(delegate { return 0; });
CallFunction((int x) => { return -x; }); // лямбда-выражение
CallFunction((int x) => -x); // предпочтительно
CallFunction((x) => -x);
CallFunction(x => -x);
}
public int Negate(int x) { return -x; }
public void CallFunction(FunctionDelegate f)
{
Console.WriteLine("F(10) = {0}", f(10));
}
}

Слайд 50

Анонимная функция

Вместо вручную объявленного делегата:
public delegate int FunctionDelegate(int x);
можно использовать готовый шаблон:
public

Анонимная функция Вместо вручную объявленного делегата: public delegate int FunctionDelegate(int x); можно
delegate TResult Func(T arg);
Прежний вариант:
public void CallFunction(FunctionDelegate f)
{
...
}
Новый вариант:
public void CallFunction(Func f)
{
...
}

Слайд 51

Анонимная функция

Создадим процедурную переменную, которой присвоим ссылку на анонимную функцию:
Func func

Анонимная функция Создадим процедурную переменную, которой присвоим ссылку на анонимную функцию: Func
= (int x) => -x; // код
Существует возможность представить анонимную функцию как данные с помощью шаблона System.Linq.Expressions.Expression:
Expression> exp = func; // данные
Получение анонимного делегата из сконструированного в переменной типа Expression выражения:
Func func = exp.Compile(); // теперь код
Вызов:
int y = func(10);
Конструируемое выражение не может содержать некоторые элементы языка: блок { } и операторы присваивания.

Слайд 52

Приемы программирования

Итератор – абстрактный продвигаемый вперед указатель на элемент контейнера
Одиночка – объект,

Приемы программирования Итератор – абстрактный продвигаемый вперед указатель на элемент контейнера Одиночка
создаваемый в единственном экземпляре
Заместитель – объект, перенаправляющий вызовы к другому объекту
Компоновщик – объект, компонующий набор других однотипных объектов в одно целое
Мост – делегирование функциональности метода другому объекту через интерфейс, чтобы иметь возможность независимо менять реализацию интерфейса
Наблюдатель – объект, получающий уведомления от других объектов
Посетитель – объект, передаваемый другому объекту для вызова своих методов
Фабричный метод – виртуальный метод, создающий объект
Фабрика классов – интерфейс с виртуальными методами, создающими объекты различных классов
Пул объектов – кэш заранее созданных объектов

Слайд 53

Итератор (Iterator/Enumerator)

Итератор – абстрактный продвигаемый вперед указатель на элемент контейнера:
public interface IEnumerator
{

Итератор (Iterator/Enumerator) Итератор – абстрактный продвигаемый вперед указатель на элемент контейнера: public
object Current { get; }
bool MoveNext();
void Reset();
}
public interface IEnumerator : IDisposable, IEnumerator
{
T Current { get; }
}
Контейнеры поддерживают интерфейс создания итератора:
public interface IEnumerable : IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerable
{
IEnumerator GetEnumerator();
}

Слайд 54

Итератор

Для удобства пользования итератором существует оператор foreach:
public static void Test(List args)
{
foreach

Итератор Для удобства пользования итератором существует оператор foreach: public static void Test(List
(string s in args)
Console.WriteLine(s);
}
Показанный выше оператор foreach транслируется в следующий код:
IEnumerator e = args.GetEnumerator();
try
{
while (e.MoveNext())
{
string s = e.Current;
Console.WriteLine(s);
}
}
finally
{
e.Dispose();
}

Слайд 55

Итератор

Если функция возвращает значение типа IEnumerable, то такую функцию можно сделать итератором:
public

Итератор Если функция возвращает значение типа IEnumerable, то такую функцию можно сделать
static void Test(List> lists)
{
foreach (string s in PlainList(lists))
{
Console.WriteLine(s);
}
}
public static IEnumerable PlainList(List> lists)
{
foreach (List list in lists)
foreach (string s in list)
yield return s;
yield break; // здесь можно опустить
}

Слайд 56

Одиночка (Singleton)

Одиночка – объект, создаваемый в единственном экземпляре.
Пример – статическое (static)

Одиночка (Singleton) Одиночка – объект, создаваемый в единственном экземпляре. Пример – статическое
поле класса, инициализированное объектом с помощью оператора new:
public class Program
{
public static Window Desktop = new Desktop();
...
}
Объект Program.Desktop является глобальной переменной, создаваемой один раз при первом обращении к ней.
В примере кроется потенциальная проблема. Если конструктор класса Desktop создаст исключение, объект не будет создан, и переменная Program.Desktop останется равна null. Выход – использовать функцию для создания объекта-одиночки.
Статья MSDN по объектам-одиночкам: https://msdn.microsoft.com/en-us/library/ee817670.aspx

Слайд 57

Одиночка

public class Config
{
private static volatile Config instance = null;
private static

Одиночка public class Config { private static volatile Config instance = null;
readonly object syncRoot = new object();
private Config() { ... }
public static Config Instance() // применение: Config.Instance().ToString()
{
if (instance == null)
{
lock (syncRoot) // лишь один поток может находиться в этом блоке
{
if (instance == null)
{
instance = new Config();
}
}
}
return instance;
}
}

Слайд 58

Заместитель

Прокси (Proxy) или Суррогат (Surrogate) – легковесный объект-заместитель, перенаправляющий вызовы к замещаемому

Заместитель Прокси (Proxy) или Суррогат (Surrogate) – легковесный объект-заместитель, перенаправляющий вызовы к
тяжеловесному объекту.
Обертка (Wrapper) или Декоратор (Decorator) – объект-заместитель, содержащий в себе замещаемый объект и предоставляющий, по сравнению с ним, новые функции.
Адаптер (Adapter) – объект, реализующий некоторый интерфейс путем обращения к другому объекту через свойственный ему интерфейс.

Слайд 59

Прокси (Proxy) / Суррогат (Surrogate)

Прокси (Proxy) или Суррогат (Surrogate) – легковесный объект-заместитель,

Прокси (Proxy) / Суррогат (Surrogate) Прокси (Proxy) или Суррогат (Surrogate) – легковесный
перенаправляющий вызовы к замещаемому тяжеловесному объекту.
Представьте, что у вас есть интерфейс IBookStorage и стандартная реализация этого интерфейса для работы с веб-сервисом, которую вы используете для создания, получения, обновления и удаления (CRUD) книг в вашей электронной библиотеке. Вы решили, что хорошо было бы кэшировать полученные данные на какое-то время. Как добавить кэш?
Можно модифицировать существующий код для работы с кэшем. А что если он должен быть опциональным? А что если другой команде нужен ваш компонент, но без всяких намеков на кэш?
Задача решается созданием промежуточного объекта – прокси, реализующего интерфейс IConfig (IStream) путем обращения к объекту Dictionary (массиву byte[]) через свойственный ему интерфейс.

Слайд 60

Прокси / Суррогат

public interface IWebClient
{
void Request();
}
public class WebClient : IWebClient
{
public

Прокси / Суррогат public interface IWebClient { void Request(); } public class
WebClient() { ... }
public void Request() { ... }
}
public class ProxyClient : IWebClient // Прокси для WebClient
{
private WebClient fClient;
public void Request()
{
if (fClient == null)
fClient = new WebClient();
fClient.Request();
}
}

Слайд 61

Прокси / Суррогат

Прокси (Proxy) – легковесный объект-заместитель, перенаправляющий вызовы к замещаемому тяжеловесному

Прокси / Суррогат Прокси (Proxy) – легковесный объект-заместитель, перенаправляющий вызовы к замещаемому
объекту (обычно через сеть).

Слайд 62

Обертка (Wrapper) / Декоратор (Decorator)

Обертка (Wrapper) или Декоратор (Decorator) – объект-заместитель, содержащий

Обертка (Wrapper) / Декоратор (Decorator) Обертка (Wrapper) или Декоратор (Decorator) – объект-заместитель,
в себе замещаемый объект и предоставляющий, по сравнению с ним, новые функции.
Представьте, что у вас есть интерфейс IStream (поток) и стандартная реализация этого интерфейса для чтения и записи файла (вы ее используете для сохранения пользовательских настроек). Появилось требование, чтобы данные записывались в файл в сжатом виде.
Можно вставить логику сжатия данных в место, где используется IStream (непосредственно до вызова метода Write и сразу после вызова метода Read). Но в таком случае, работать со сжатыми файлами становится неудобно – код сжатия и разжатия приходится дублировать, поддерживать программу становится сложно. Можно модифицировать код методов Write и Read. А если он должен быть опциональным? А если другой команде нужен поток, но без сжатия?
Задача решается созданием промежуточного объекта – обертки / декоратора, содержащего внутри себя поток и выполняющего сжатие и разжатие данных.

Слайд 63

Обертка (Wrapper) / Декоратор (Decorator)

Обертка (Wrapper) или Декоратор (Decorator) – объект-заместитель, содержащий

Обертка (Wrapper) / Декоратор (Decorator) Обертка (Wrapper) или Декоратор (Decorator) – объект-заместитель,
в себе замещаемый объект и предоставляющий, по сравнению с ним, новые функции.

Слайд 64

Адаптер (Adapter)

Адаптер – объект, реализующий некоторый интерфейс путем обращения к другому объекту

Адаптер (Adapter) Адаптер – объект, реализующий некоторый интерфейс путем обращения к другому
через свойственный ему интерфейс.
Представьте, что у вас есть интерфейс IStream (поток) и реализация этого интерфейса для чтения файла. Вам нужно сделать так, чтобы другая реализация IStream читала из массива byte[]. Но массив byte[] не поддерживает интерфейс IStream. Так что же делать?
Представьте, что у вас есть интерфейс IConfig с методом string GetValue(string key) и реализация этого интерфейса для считывания из системного реестра значений по заданным ключам. Вам для тестирования нужно сделать так, чтобы другая реализация IConfig забирала значения из вашего Dictionary. Но Dictionary не поддерживает интерфейс IConfig. Так что же делать?
Задача решается созданием промежуточного объекта – адаптера, реализующего интерфейс IConfig (IStream) путем обращения к объекту Dictionary (массиву byte[]) через свойственный ему интерфейс.

Слайд 65

Адаптер

interface IConfig
{
string GetValue(string key);
}
class DictionaryConfig : IConfig // Адаптер для Dictionary

Адаптер interface IConfig { string GetValue(string key); } class DictionaryConfig : IConfig
string>
{
private Dictionary fDictionary;
public DictionaryConfig(Dictionary dictionary)
{
fDictionary = dictionary;
}
public string GetValue(string key)
{
return fDictionary[key];
}
}

Слайд 66

Адаптер

Адаптер – объект, реализующий некоторый интерфейс путем обращения к другому объекту через

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

Слайд 67

Компоновщик (Composite)

Компоновщик – объект, компонующий набор других однотипных объектов в одно целое.
public

Компоновщик (Composite) Компоновщик – объект, компонующий набор других однотипных объектов в одно
class CompositeStream : Stream // Компоновщик
{
private readonly Stream[] fStreams;
public CompositeStream(params Stream[] streams)
{
fStreams = streams;
}
public void CopyTo(Stream destination)
{
foreach (Stream stream in fStreams)
stream.CopyTo(destination);
}
}

Слайд 68

Компоновщик

Компоновщик – объект, компонующий набор других однотипных объектов в одно целое.

Компоновщик Компоновщик – объект, компонующий набор других однотипных объектов в одно целое.

Слайд 69

Мост (Bridge)

Мост – делегирование функциональности метода другому объекту через интерфейс, чтобы иметь

Мост (Bridge) Мост – делегирование функциональности метода другому объекту через интерфейс, чтобы
возможность независимо менять реализацию интерфейса.
public class Painter
{
private readonly IPaintDevice fDevice;
public Painter(IPaintDevice device)
{
fDevice = device;
}
public void DrawCircle(int x, int y, int radius)
{
fDevice.DrawCircle(x, y, radius);
}
}
public interface IPaintDevice
{
void DrawCircle(int x, int y, int radius);
}

Слайд 70

Мост

Мост через интерфейс создается к отдельной иерархии классов.
public class LowQualityPaintDevice : IPaintDevice
{

Мост Мост через интерфейс создается к отдельной иерархии классов. public class LowQualityPaintDevice
public void DrawCircle(int x, int y, int radius) { ... }
}
public class HighQualityPaintDevice : IPaintDevice
{
public void DrawCircle(int x, int y, int radius) { ... }
}
public static void TestBridge()
{
var painter1 = new Painter(new LowQualityPaintDevice());
painter1.DrawCircle(10, 10, 5);
var painter2 = new Painter(new HighQualityPaintDevice());
painter2.DrawCircle(10, 10, 5);
}

Слайд 71

Мост

Мост – делегирование функциональности метода другому объекту через интерфейс, чтобы иметь возможность

Мост Мост – делегирование функциональности метода другому объекту через интерфейс, чтобы иметь
независимо менять реализацию интерфейса.

Слайд 72

Наблюдатель (Observer/Listener)

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

Наблюдатель (Observer/Listener) Наблюдатель – объект, получающий уведомления от других объектов. Наблюдатель применяется
программирования, не имеющих процедурных переменных (делегатов, событий).

Слайд 73

Посетитель (Visitor)

Посетитель – объект, передаваемый другому объекту для вызова своих методов.
Например, в

Посетитель (Visitor) Посетитель – объект, передаваемый другому объекту для вызова своих методов.
библиотеке визуальных компонентов существует элементы пользовательского интерфейса (Controls), которые умеют нарисовать себя на какой-то поверхности (Canvas) в какой-то позиции (X, Y). В каждом производном от Control классе перекрыт виртуальный метод void Draw(Canvas canvas, int x, int y), который рисует элемент пользовательского интерфейса.
В окне (Form) существует список элементов пользовательского интерфейса. При перерисовке окна у каждого элемента пользовательского интерфейса вызывается метода Draw с передачей ему поверхности рисования окна (Form.Canvas).
Объект, передающий себя (или какой-то свой объект) другому объекту для вызова у себя методов, и называется посетителем.

Слайд 74

Фабричный метод (Factory Method)

Фабричный метод – виртуальный метод, создающий объект.

Фабричный метод (Factory Method) Фабричный метод – виртуальный метод, создающий объект.

Слайд 75

Фабрика классов (Factory)

Цель фабрики классов – создание объектов. Почему не создать его

Фабрика классов (Factory) Цель фабрики классов – создание объектов. Почему не создать
просто через new? – вот в чем вопрос. И здесь у каждого своя причина. Вот некоторые их них:
Проблема расширяемости конструктора. Вы хотите расширить класс, но для этого ему нужно передать в конструктор еще один интерфейс. Проблема в том, что этот класс инстанциируется во многих местах, и теперь вам придется «пофиксить» все эти места. Решение – использовать фабрику, которая будет создавать объекты этого типа. Современный подход – использование IoC контейнеров для данных целей.
Поговорим о С++. Проблема в том, что вы не можете создать объект класса просто потому, что он находится где-то в другой dll, и класс не экспортируется через __declspec(dllexport). Это обычно делается для расширяемого программирования (для плагинов). Обычно экспортируют лишь одну функцию, например GetFactory, которая возвращает IFactory, через которую вы можете создавать объекты тех или иных классов. В C# эту проблему можно обойти рефлексией, инстанциируя классы из другой dll через Activator.CreateInstance (очень распространенный подход).
Имя файла: Объектно-ориентированные-технологии-программирования-и-стандарты-проектирования.pptx
Количество просмотров: 41
Количество скачиваний: 0