Программная инженерия Зарецкий Дмитрий Викторович Email: dmitryzar@stbpwr.com Программа курса Программное обеспечение: природа и качество. Классификация ПО. Требования и показательные качества ПО. Виды систем. Принципы инженерии ПО. Основные понятия объектноориентированного программирования. Объекты, сообщения, интерфейсы, классы, полиморфизм. Обзор языков, платформ, технологий и средств разработки. .NET Framework и J2EE – сравнительный анализ. Основы CORBA, DCOM, EJB, .NET Remoting. Реляционные и объектно-ориентированные базы данных. Интернет приложения, веб сервисы. Средства управления исходным кодом (CVS, Subversion, SourceSafe) Стратегии распределенных вычислений. Связи. Процессы. Синхронизация. Репликация. Отказоустойчивость. Распределенные системы объектов, файлов, документов. Распределенные системы согласования. Проектирование, спецификация. Составление технических заданий. Методы сбора информации. UML. Use case диаграммы. Диаграммы классов, объектов, компонентов, состояний, последовательностей, действий. ORM. CASE инструменты. Спецификации асинхронных систем. Логические, алгебраические спецификации. Концептуальный, логический и физический дизайн. Прототипирование. Паттерны проектирования. Процесс разработки программного обеспечения. Структура проектной группы. Стадии проекта. Нотации оформления кода. cамодокументированность кода. Рефакторинг. Экстремальное программирование. Безопасность кода. Принципы построения GUI. Ведение параллельных версий. Компиляция и отладка ПО. Оптимизация. Тестирование. Верификация. Методы тестирования (методы “черного и белого ящика”) Планы тестирования проекта, компонентов. Модели анализа. Автоматизированные регрессивные тесты. Тестирование классов и иерархий классов. Тестирование распределенных объектов. Тестирование интернет приложений. Внедрение продукта. Разработка пользовательской документации. Создание программы установки. Обучение пользователей, поддержка ПО. Повторное использование и переносимость ПО, реинженерия. Управление. Планирование проекта. Анализ рисков. Оценка затрат. Внутренняя проектная документация. Командная работа, распределение ролей и ответственности (централизованный, децентрализованный и смешанный тип управления). Диаграммы Ганта. Графики PERT. Контроль качества. Анализ текущего состояния проекта. Стабильность/нестабильность проекта, построение графиков интенсивности возникновения и устранения ошибок. Средства поддержки управления проектом. Модель развития функциональных возможностей CMM. Литература Мартин Фаулер Архитектура корпоративных программых приложений. Москва. Изд. Вильямс 2004. Карло Гецци, Мехди Джазайери, Дино Мандриоли. Основы инженерии программного обеспечения. Санкт-Петербург БХВ 2005. Э. Таненбаум, М. ван Стеен. Распределенные системы. Принципы и парадигмы. Изд. Питер 2004. Джон Макгрегор, Девид Сайкс Тестирование объектноориентированного программного обеспечения. 2002 Beck. Test-Driven Development: By Example. Addison-Wisley, 2003. Эд Салливан. Время-деньги. Microsoft Press, Русская редакция. Москва 2002. Бек, К. Экстремальное программирование. Библиотека программиста. СПб.: Питер, 2002. Ссылки Microsoft Solution Framework White Paper June 2002, http://www.microsoft.com/msf/ Object Management Group’s Unified Modeling Language http://www.omg.org/uml/ Русскоязычный сайт программистов http://www.rsdn.ru Сайт посвященный вопросам программирования под .NET http://www.gotdotnet.ru Жизненный цикл программного обеспечения. Анализ и спецификация требований. Planning. Проектирование. Developing. Кодирование. Stabilizing. Тестирование. Deploying. Сборка. Поставка и эксплуатация. Envisioning. Качества программного обеспечения Внутренние и внешние качества, связь между ними. Оценка продукта. Оценка процесса создания ПО. Корректность (correctness) – соответствие требованиям функциональной спецификации Надежность (reliability) – вероятность того, что программное обеспечение будет работать как ожидалось в течении определенного интервала времени. Надежность Корректность Устойчивость(stability) – правильное поведение программы даже в обстоятельствах не предусмотренных в спецификации Производительность (productivity). Эффективность (performance). Подходы к расчету производительности системы: 1. 2. 3. Измерение Анализ Имитационное моделирование. Удобство. Дружественный интерфейс. Верифицируемость. Рассмотреть влияние пользовательского интерфейса (GUI) на надежность. Сопровождаемость В среднем затраты на сопровождение превышают 60% от общей стоимости программного обеспечения. Категории сопровождения Корректирующее – устранение ошибок. Настраивающее – адаптация приложения к изменениям среды. Усовершенствующее – изменение ПО с целью улучшения некоторых из его качеств Проблема унаследованного ПО. Необходимость проведение обратного инжиниринга и реинженеринга. Ремонтопригодность (repairability) – возможность устранения дефектов приемлемыми усилиями. Способность к эволюции. Повторная применимость. Переносимость (portability) – возможность работать в различных системах. Понятность. Интероперабельность (interoperability) – возможность ПО сотрудничать с другими системами. Продуктивность. Как повторная применимость может влиять на надежность продуктов? Как можно использовать наследование для увеличения повторной применимости? Какова связь повторной применимости и переносимости? Как можно применить переносимость для web страниц? Что можно сказать о способности к эволюции открытых систем? Можно ли использовать подсчет количества строк для измерения продуктивности. Своевременность ПО Функциональность Требования пользователя Реальные возможности системы T0 T1 T2 T3 Наиболее часто встречающиеся проблемы в разработке ПО. Классификация ПО. Информационные системы. Хранение, поиск, извлечение данных. Системы реального времени. Должны реагировать на событие в течении строго предопределенного периода времени. Распределенные системы. Располагаются на нескольких независимых или частично независимых компьютерах. Встроенные системы. ПО является одним из множества компонентов, часто не имеют GUI. Охарактеризуйте такие качества как тестируемость, целостность, легкость в использовании, изучаемость. Приведите примеры. Оценить преимущества и недостатки повторной используемости методов в применении к программному процессу. Какова роль протоколов TCP/IP в интероперабельности? Можно ли оценивать процесс создания ПО с точки зрения интероперабельности? Принципы создания ПО. Принципы Методы и приемы Методологии Инструментальные средства Строгость и формальность. Разделение задач. Преимущества и недостатки (избыточность, взаимоблокировки). Модульность. Проектирование снизу вверх и сверху вниз. Преимущества модульности Разложимость – возможность разложить систему на простые части. Компонуемость – возможность собрать систему из готовых модулей. Возможность интерпретировать систему как состоящую из простых частей. Возможность модифицировать систему путем модификации небольшого количества частей. Внутримодульная и межмодульная связность. А Б Какие могут быть причины низкой внутримодульной связности? Как их устранить? Какие могут быть причины высокой межмодульной связности? Как их устранить? Абстракция Выделяем существенные аспекты явления и игнорируем его подробности. Переменные в языках программирования можно рассматривать как абстракции адресов ячеек памяти. От каких подробностей абстрагируются переменные языков программирования? Каковы преимущества и недостатки использования такой абстракции? Модель жизненного цикла ПО является абстракцией программного процесса. Почему? Какие абстракции полезны конечному пользователю, проектировщику и специалисту по поддержке приложений? Предусмотрение изменений. Принцип позднего связывания. Общность. Каждый раз, когда надо решить какую-либо проблему, постарайтесь сосредоточится на решении более общей проблемы, которая может быть скрыта за рассматриваемой задачей. Инкрементность. Итерационный процесс разработки. Способ получения раннего отклика от клиента. Какое взаимоотношение между обобщением и предусмотрением изменений? Какое взаимоотношение между общностью и абстракцией? Какое взаимоотношение между формальностью и предусмотрением изменений? Парадигмы программирования Будем говорить, что язык поддерживает данный стиль программирования, если он предоставляет средства, которые делают использование стиля удобным (достаточно простым, надежным и эффективным) Язык не поддерживает технику программирования, если для написания соответсвующей программы требуются чрезмерные усилия или мастерство. Литература Бьерн Страуструп Язык программирования С++. Москва. Binom 2000. Джеффри Рихтер Программирование на платформе .NET Framework. Брюс Эккель Философия JAVA. СПб. Питер, 2000. Процедурное программирование Реши, какие требуются процедуры; используй наилучшие доступные алгоритмы. Пример. double sqrt(double arg) { //text } void f() { double root2 = sqrt(2); } Модульное программирование Реши, какие требуются модули; разбей программу так, чтобы скрыть данные в модулях. Пример. Реализовать стек, удовлетворяющий следующим требованиям: 1. Предоставить пользовательский интерфейс для стека (например, функции push() и pop(), помещающий данные в стек и извлекающий их оттуда) 2. Гарантировать, что представление стека доступно только через этот пользовательский интерфейс. 3. Обеспечить инициализацию стека до первого использования. namespace Stack { //интерфейс void push(char); char pop(); } void f() { Stack::push(‘c’); if(Stack::pop()!=‘c’) error (“Такое невозможно”); } namespace Stack{ //реализация const int max_size = 200; char v[max_size]; int top = 0; void push(char c){/*Поместить в стек.*/} char pop() {/*Извлечь из стека.*/} } Раздельная компиляция stack.h Интерфейс стека user.cpp #include “stack.h” Использование стека stack.cpp #include “stack.h” Использование стека Модули, определяющие типы namespace Stack { struct Rep; typedef Rep& stack; stack create(); void destroy(stack s); void push(stack s, char с); char pop(stack s); } void f() { Stack::stack s1 = Stack::create(); //Создать новый стек Stack::stack s2 = Stack::create(); //и еще один Stack::push(s1,’c’); Stack::push(s2,’k’); If(Stack::pop(s1)!=‘c’) throw BadPop(); If(Stack::pop(s2)!=‘k’) throw BadPop(); Stack::destroy(s1); Stack::destroy(s2); } namespace Stack{ struct Rep{ char v[max_size]; int top; }; const int max = 16; Rep stacks[max]; … } Объектно-ориентированное программирование Реши, какие требуются классы; обеспечь полный набор операций для каждого класса; явно вырази общность через наследование. Инкапсуляция – объединение в единое целое данных и алгоритмов обработки этих данных. Наследование – свойство объектов порождать своих потомков. Полиморфизм – свойство родственных объектов (имеющих общего родителя) решать схожие по смыслу проблемы разными способами. Классический пример для случая отображения различных фигур: Shape А. Диаграмма классов. #move() #rotate(in angle : int) #draw() Circle Polygon -radius -ribCount +AddRib() В. Объединение классов в единый пакет. Figures Shape Circle Polygon Определение наследников class Circle extends Shape {…} // java class Circle: Shape {…} // C# Пример создания общих методов для всех наследников: void rotate(Shape s, int angle) { s.rotate(angle); s.draw(); } Circle c = new Circle(); Polygon p = new Polygon(); rotate(c,60); rotate(p,40); Можно ли при создании класса Circle воспользоваться реализацией из класса Shape не применяя наследование? Если возможно, то как? Всегда ли использование наследования несет только преимущества? Приведите примеры возможного использования наследования. Более сложный пример использования наследования. Party Employee ProductClerk +EditProductData() -FirstName -LastName -Address -Phone -BirthDate +GetName() +GetAge() +Hire() +Create() +Delete() +Update() Customer -AccountNumber -Name -Organization -UserID -Password +GetAddressList() +AddAddress() +ValidateSignature() SalesManager Organization +Approves discount between 15% to 20%() +Applies discounts up to 20%() +Creates Orders() SalesRepresentative +Applies 15% discount() +Request up to 20% discount() +Creates Orders() +Takes Phone Orders() -Name -Address -Phone -PrimaryContact +AddContact() +UpdateContact() +DeleteContact() Множественное наследование В отличии от C++ для Java и .NET множественное наследование от обычных классов не поддерживается. Оно заменяется на одиночное наследование от обычного класса и множественное от интерфейсов. Реfлизация серверного класса в NET Remoting. MarshalByRefObject «interface» IApplicationServer +GetServerName() : string +GetSession(in login : string, in password : string) : ISession ApplicationServer -addToLog() -checkUser() «interface» ISession +GetRegistry() +GetObject() +UpdateObject() Модификаторы доступа Private – доступен только методам в определяющем типе и вложенных в него типах. Protected – доступен только методам в этом типе или одном из производных типов. Public – доступен всем методам. Нестандартные модификаторы доступа и атрибуты в разных языках: internal, friend, abstract, sealed, final, readonly Атрибут static (Shared) – означает, что поле частично определяет состояние типа, а не объекта. Члены класса Константа – идентификатор, определяющий некую постоянную величину. public const Int32 MaxEntiriesInList = 50; Поле – неизменяемое или изменяемое значение, определяющее состояние типа или объекта (статическое или нестатическое) public static readonly Random = new Random(); static Int32 numberOfWrites = 0; public readonly String pathName =“Untitled”; public FileStream fs; Конструктор экземпляра – метод, используемый для установки в корректное начальное состояние экземпляров полей нового объекта. class SomeType{ Int32 x; String s; public SomeType(){/* Общая часть */} public SomeType(Int32 x):this(){ this.x = x; } public SomeType(String s):this(){ this.s = s; } } Конструктор типа (статический конструктор, конструктор класса, инициализатор типа) – метод, используемый для установки в корректное начальное состояние статических полей типа. class SomeType { static Int32 x = 5; static SomeType() { x = 10; } } Чему будет равна переменнная x после обращения к классу SomeType? Метод – функция, выполняющая операции, которые изменяют или опрашивают состояние типа(статический метод) или объекта (экземплярный метод). public static Boolean ReferenceEquals(Object, objAObject objB) static void SetVal(out Int32 v) {…} void AddVal(ref Int32 v) {…} static Int32 Add(params Int32[] values) {…} Перегрузка оператора – метод, определяющий, что нужно проделать с объектом, при применении к нему оператора. Class Complex { public static Complex operator+(Complex c1, Complex c2) {…} } Оператор преобразования – метод, определяющий как неявно/явно привести объект из одного типа в другой. Class Rational { public Rational(Int32 numerator) {…} public Rational(Single value) {…} public Int32 ToInt32() {…} public Single ToSingle() {…} //Неявно создает Rational из Int32 и Single public static implicit operator Rational(Int32 numerator) { return new Rational(numerator); } public static implicit operator Rational(Single value) { return new Rational(value); } //Явно возвращает объет типа Int32, полученный из Rational public static explicit operator Int32(Rational r) { return r.ToInt32(); } //Явно возвращает объет типа Single, полученный из Rational public static explicit operator Single(Rational r) { return r.ToSingle(); } } Пример использования: Rational r1 = 5; //Неявное приведение Int32 к Rational Rational r2 = 1.202e18; //Неявное приведение Single к Rational Int32 x = (Int32) r1; //Явное приведение Rational к Int32 Single s = (Single) r1; //Явное приведение Rational к Single Свойство – метод, который позволяет применить простой синтаксис для установки или получения части логического состояния типа или объекта, не нарушая это состояние. public class Employee { private String _Name; private Int32 _Age; //Ключевое слово value идентифицирует //новое значение public String Name { get { return _Name; } set { _Name = value; } } public Int32 Age { get { return _Age; } set { if (value < 0) throw new OutOfRageException(); _Age = value; } } } Событие (статическое, экземплярное) – механизм, который позволяет типу/объекту посылать уведомление слушающему типу/объекту. Объявление события в C# public delegate void DataChangedDelegate ( EventValueChanged val); public event DataChangedDelegate DataChanged; if(DataChanged !=null ) DataChanged(null); //вызов ((IForm)_tableForm).DataChanged += new DataChangedDelegate( ControlConstructorListForm_DataChanged); Пример подписки на события компонетов Swing (java) JTextField name = new JTextField(25); Class NameL implements ActionListner {…} name.addActionListner(new NameL()); Исключения Компактное размещение кода, выполняющего очистку, и его гарантированное исполнение. Централизованное хранение кода, имеющего дело с исключительными ситуациями. Облегчение поиска и исправления ошибок в коде. Следует отметить, что при разработке API Win32 и COM Microsoft отказались от использования исключений для уведомления вызывающего кода о сбоях. Вопрос: Какая методика используется в API Win32 и COM? void SomeMethod() { try { } catch (InvalidCastException) { } catch (NullReferenceException) { } catch (Exception e) { throw; } catch { throw; } finally { } } Обобщенное программирование Реши, какие требуются алгоритмы; параметризируй их так, чтобы они могли работать со множеством подходящих типов и структур данных. Шаблоны в C++ template<class T> class Stack { T * v; int max_size; int top; public: Stack (int s); ~Stack(); void push(T); T pop (); } Префикс template<class T> делает T параметром объявления, которому этот префикс предшествует. Функции-члены можно определить следующим образом: template<class T> void Stack<T>::push(T c) { if (top == max_size) throw Overflow (); v[top] = c; top ++; } Использование стека: Stack<char> sc (200); Stack<complex> scplx(30); sc.push(‘c’); Scplx.push (complex(1,2)); Generic в C# 2.0 List<int> items = new List<int> (); items.Add(1); items.Add(2); items[1]++; int I = items[1]; //Объявление class List<ItemType> {…} Generic в java 1.5 List<String> items = new ArrayList<String> (); items.Add(“some text”); String str = items.get(0); //Объявление class GenericList<E> {…} Последовательности и связные списки …