ук 03.002.01 2011
DESCRIPTION
TRANSCRIPT
УК 03.002.01-2011 Учебный курс. Обучение.
Контрактное программирование.
Базовые понятия
Контрактная модель программирования
Б. Мейер Eiffel
Объект, предоставляющий ресурсы, называется сервером.
Объект, использующий ресурсы другого объекта, называется клиентом.
(КН 02.002-1999)
• Метафора условий и ответственности бизнес-контрактов.
• Контракт определяет ответственность объекта – поведение за которое он отвечает.
Инвариант – логическое условие, значение которого не меняется.
• Asserts (жесткое падение)
• Тройка Хоара {pred} Action {post}
• Логика программирования
• Корректность программы
• Автоматическое тестирование
Предусловия – логическое условие, истинное до выполнения операции.
Постусловия – логическое условие, истинное после выполнения операции.
• Нарушение инварианта – нарушает контракт абстракции
• Нарушено предусловие – клиент не соблюдает свои обязательства, и сервер не может выполнить операцию правильно
• Нарушено постусловие – свои обязательства нарушил сервер
• При нарушении какого-либо условия возбуждается исключительная ситуация (ПР 01.003)
• Конструктор используется для установки инвариантов класса (ПР 02.009)!!!
Пример 1: Выделение класса
private FtpWebResponse _MakeRequest(string relativePath, string method, byte[] data)
{
FtpWebRequest ftpWebRequest = (FtpWebRequest)WebRequest.Create(GetPath(relativePath));
ftpWebRequest.Credentials = _credentials;
ftpWebRequest.Method = method;
ftpWebRequest.UsePassive = usePassive;
ftpWebRequest.ServicePoint.ConnectionLimit = 10;
if (data != null && data.Length > 0)
{
ftpWebRequest.ContentLength = data.Length;
using (Stream requestStream = ftpWebRequest.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
requestStream.Close();
}
}
return (FtpWebResponse)ftpWebRequest.GetResponse();
}
• Требуется время на то, чтобы понять суть метода
• Нельзя повторно использовать для других типов запросов
• Название запутывает (MakeRequest, а надо MakeResponse)
• Смешение разных уровней абстракции
• Как следствие, несколько разных отвественностей, а значит потенциальные проблемы с замкнутостью
• Непонятно – что делать с ситуацией, когда тип запроса подразумевает данные в ответе, а их нет
class Path
{
string normailizePath;
public Path(string relativePath)
{
normalizePath = GetPath(relativePath);
}
private void GetPath(string unnormalizedString)
{
…
}
public override string ToString()
{
return normalizePath;
}
}
private FtpWebResponse _MakeRequest(Path path, string method, byte[] data)
{
FtpWebRequest ftpWebRequest = (FtpWebRequest)WebRequest.Create(path.ToString());
ftpWebRequest.Credentials = _credentials;
ftpWebRequest.Method = method;
ftpWebRequest.UsePassive = usePassive;
ftpWebRequest.ServicePoint.ConnectionLimit = 10;
if (data != null && data.Length > 0)
{
ftpWebRequest.ContentLength = data.Length;
using (Stream requestStream = ftpWebRequest.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
requestStream.Close();
}
}
return (FtpWebResponse)ftpWebRequest.GetResponse();
}
class Data
{ byte[] data;
public Data(byte[] exData)
{
if (null == exData)
throw ArgumentNullException();
if(0 == exData.Length)
throw new SomeException();
data = exData;
}
public byte[] Body
{
get
{
return data;
}
}
public int Length
{
get
{
return data.Length;
}
}
}
private FtpWebResponse _MakeRequest(Path path, string method , Data data)
{
FtpWebRequest ftpWebRequest = (FtpWebRequest)WebRequest.Create(path.ToString());
ftpWebRequest.Credentials = _credentials;
ftpWebRequest.Method = method;
ftpWebRequest.UsePassive = usePassive;
ftpWebRequest.ServicePoint.ConnectionLimit = 10;
if (data != null)
{
ftpWebRequest.ContentLength = data.Length;
using (Stream requestStream = ftpWebRequest.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
requestStream.Close();
}
}
return (FtpWebResponse)ftpWebRequest.GetResponse();
}
private FtpWebResponse _MakeRequest(Path path, string method, Data data)
{
FtpWebRequest ftpWebRequest = (FtpWebRequest)WebRequest.Create(path.ToString());
ftpWebRequest.Credentials = _credentials;
ftpWebRequest.Method = method;
ftpWebRequest.UsePassive = usePassive;
ftpWebRequest.ServicePoint.ConnectionLimit = 10;
if (data != null)
{
ftpWebRequest.ContentLength = data.Length;
using (Stream requestStream = ftpWebRequest.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
requestStream.Close();
}
}
return (FtpWebResponse)ftpWebRequest.GetResponse();
}
private FtpWebResponse _MakeRequest(Path path, IResponseMethod method, Data data)
{
ResponseBuilder builder = new FTPResponseBuilder(this);
builder.Path = path;
method.Apply(builder);
if (data != null)
builder. Data = data;
return builder.GetResponse();
}
interface IResponseMethod
{
void Apply(IMethodHandler handler);
}
Interface IResponseHandler
{
void Handle(string method);
}
• Фабрика билдеров
• ConnectionLimit из файла конфигурации
• Ограничить методы, соответствующим протоколом
private WebResponse _MakeResponse(Path path,
IResponseMethod method)
{
ResponseBuilder builder = new FTPResponseBuilder(this);
builder.Path = path;
method.Apply(builder);
return builder.GetResponse();
}
interface IResponseMethod
{
void Apply(IMethodHandler handler);
}
Interface IResponseBuilder
{
void Handle(string method);
Data Data
{
set;
}
}
class ResposeWithBody: IResponseMethod
{
IResponseMethod method;
Data data;
public ResponseWithBody(IResponseMethod m, Data d)
{
if (null == d)
throw
ArgumentNullException();
method = m;
}
public override void Apply(ResponseBuilder builder)
{
method.Apply(builder);
builder.Data = data;
}
}
Контракт метода в ООП
• возможные типы входных данных и их значение;
• типы возвращаемых данных и их значение;
• условия возникновения исключений, их типы и значения;
• присутствие побочного эффекта метода;
• предусловия, которые могут быть ослаблены (но не усилены) в подклассах (УК 02.001-2011, Принцип подстановки Лисков);
• постусловия, которые могут быть усилены (но не ослаблены) в подклассах (УК 02.001-2011, Принцип подстановки Лисков);
• инварианты, которые могут быть усилены (но не ослаблены) в подклассах;
• (иногда) качественные характеристики, такие как гарантии производительности, например, временная сложность или сложность по памяти
RAII
RAII (Resource allocation is initialization) – выделение ресурса есть инициализация
• Адаптер для ресурса – класс, реализующий требуемое от ресурса поведение
• Конструктор используется для выделения ресурса
• Деструктор для освобождения
• Инвариант адаптера – ресурс выделен и находится в работоспособном состоянии
RAII в .Net
• Неуправляемые ресурсы
• IDisposable
• Инструкция using
using vs. try-finally
using (A a = new A())
{
}
try
{
A a = new A();
…
}
finally
{
a.Dispose();
}
Пример 2: Курсор Песочные часы
…
void f()
{
Cursor previous = Cursor.Current;
Cursor.Set(Cursor.Wait);
try
{
throw …
}
finally
{
Cursor.Set(previous);
}
if (…)
{
Cursor.Set(previous);
return file2;
}
f (…)
{
Cursor.Set(previous);
return file;
}
}
class MyCursor: IDisposable
{
Cursor previous;
public MyCursor()
{
Cursor previous = Cursor.Current;
Cursor.Set(Cursor.Wait);
}
public void Dispose() { Dispose(true); }
void Dispose(bool disposed)
{
….
Cursor.Set(previous);
…
}
}
…
void f()
{
using (MyCursor cursor = new MyCursor)
{
throw …
if (…)
{ return file2; }
if (…)
{ return file; }
}
}
delegate D(); class CursorExtender { public static void WaitingAction(D d) { Cursor previous = Cursor.Current; Cursor.Set(Cursor.Wait); try { d(); } finally { Cursor.Set(previous); } } } void f() { Cursor.WaitingAction( new delegate{ { throw … if (…) { return file2; } if (…) { return file; } } ); }
Что дальше
• Юнит-тестирование
• Интеграционные тесты
• Приемочные тесты
Вопросы?