Главная Учебники - Разные Лекции (разные) - часть 15
Элементы технологии программирования в примерах и упражнениях Уче6ное пособие А.П.Гагарин 2010г. СОДЕРЖАНИЕ
Введение 1. Элементы объектно-ориентированного программирования 1.1. Определение класса и пользование его экземплярами 1.2. Наследование 1.3. Полиморфизм 1.4. Свойства и события (методы объекта, делегаты) 1.5. Класс классов 2. Организация типовых оконных приложений 2.1. Оконные приложения в IDE Delphi c библиотекой VCL 2.2. Приложение Windows Forms в Microsoft Visual Studio 2008 для среды CLR на языке C# 2.3. Приложение Windows Forms в Microsoft Visual Studio 2008 для среды CLR на языке C++ 2.4. Приложение Windows Forms в Microsoft Visual Studio 2008 для среды Win32 3. Списки и таблицы 3.1. Прото-СУБД на основе класса TStringList и TStringGrid (библиотека VCL Delphi) 4. Разработка и верификация расчётных приложений 4.1. Спецификация программы uWert 4.2. Текст программы uWert 4.3. Доказательство корректности программы uWert 5. Организация многооконного интерфейса и диалога в приложениях 5.1. Многооконный интерфейс на основе сохраняемых форм 5.2. Диалог на основе динамически создаваемых форм 5.2.1. Проект диалоговой функции в Delphi 5.2.2. Проект диалоговой функции в Microsoft Visual Studio 2008 (CLR, язык C#) 5.2.3. Проект диалоговой функции в Microsoft Visual Studio 2008 (CLR, язык C++) 6. Проектирование классов 6.1. Основы модельно-ориентированного проектирования 6.2. Проект разработки компьютерной поддержки библиотеки Приложения. 1. Синтаксис и семантика класса в языках Object Pascal, C#, C++, Java 2. Основы языка UML Данное учебное пособие сложилось в ходе преподавания технологий программирования студентам 2-го курса, то есть студентам, заведомо не имеющим заметного опыта и широкого кругозора в области программирования. Поэтому пособие преследует до известной степени разнородные цели: ознакомить со сквозным процессом разработки программ в духе современного Software Engineering, ввести в использование современных интегральных сред программирования и просто поучить разработке программ, уровень сложности алгоритмизации в которых превышает уровень обычных, преимущественно тривиальных примеров. Первый раздел содержит примеры простейших приёмов объектно-ориентированного программирования, которые нужны для дальнейшего, но, как показывает опыт преподавания, усваиваются бывшими школьниками далеко не сразу. Второй раздел «Организация типовых оконных приложений» позволяет с полным пониманием приступить к использованию интегральных сред программирования Borland Delphi и Microsoft Visual Studio и служит иллюстрацией масштабного применения объектно-ориентированного стиля программирования. Третий раздел «Списки и таблицы» по замыслу должен помочь освоить важнейшие типы данных. В данной редакции пособия в этом разделе приведён пример использования класса TStrings. Этот пример позволяет подвести студентов к проблемам реализации реляционной модели данных в СУБД. Материал на ту же тему содержится и в пункте 6.2.4 раздела 6. Четвёртый раздел «Разработка и верификация расчётных приложений» содержит программу вычисления значения регулярного арифметического выражения и верификацию её путём формального доказательства. Пятый раздел «Организация многооконного интерфейса и диалога в приложениях» по содержанию и целям является развитием второго раздела. Шестой раздел демонстрирует проектирование АСУ библиотеки по методике, в целом соответствующей методике Rational Unified Process (RUP). Для документирования проекта используется язык UML, разрабатываемый силами Object Management Group (OMG). Полный исходный текст проектируемой программы вряд ли когда-нибудь войдёт в пособие из-за своего объёма. Приведён лишь его фрагмент, иллюстрирующий одно из ключевых проектных решений. Во всех разделах сформулированы упражнения, позволяющие практиковаться в проектировании, разработке и исследовании программ, отталкиваясь от приведённых примеров, то есть – на достаточно объёмном и сложном материале. Школьные и вузовские образовательные программы по информатике в настоящее время устроены в своём большинстве так, что студенты вначале знакомятся с языком Pascal в версии Turbo, а затем – в объектной (Object) версии. Поэтому вполне актуально проводить обучение современным представителям семейства C-подобных языков, соотнося учебный материал по этим языкам со знаниями языка Pascal, которые, с большой вероятностью, имеются у учащихся. Соответственно, пособие содержит тексты на языках Object Pascal (Delphi), C++ и С# и побуждает упражняться в понимании, переработке и доработке этих текстов. Характер изложения материала в каждом из разделов умышленно усложняется от начала к концу. Начальные части разделов легко доступны всем читателям, в том числе тем, кто знакомится с темой. Уровень изложения в конце разделов позволит быстро освоить их продвинутым читателям и составит полезную нагрузку для остальных. 1. ЭЛЕМЕНТЫ ОБЪЕКТНО-ОРИЕНТИРОВАННОГО ПРОГРАММИРОВАНИЯ
1.1. Определение класса и пользование его экземплярами Пример консольного приложения IDE Delphi, в котором определён класс A как наследник класса TComponent: program ClassTest01; {$APPTYPE CONSOLE} uses SysUtils, classes; type A = class(TComponent) public f: extended; procedure S; constructor create(AOwner:TComponent); end; procedure A.S; var x: integer; function Fact(n: extended):extended; begin if n=1 then result:=1 else result:=n*Fact(n-1) end; begin write('Proc S from class A calculates factorial of '); read(x); // f:=x; // write('is '+FloatToStr(Fact(f)); write('is '+FloatToStr(Fact(x)); read(x) end; constructor A.create(AOwner:TComponent); begin inherited create(AOwner) end; var x: A; begin x:=A.create(nil);дол x.S; end. Процедура S запрашивает число у оператора, вычисляет и выводит его факториал, как показано на следующем рисунке. Если в методе используются только методы данного класса, она может быть вызвана от переменной x типа А без порождения экземпляра класса. Однако если использовать, например, поля, как показано в закомментаренных строках примера, тогда экземпляр класса должен быть создан вызовом конструктора класса. 1.2. Наследование Если в программу ClassTest01, приведённую в п.1.1, ввести определение класса C, наследника класса A, type C = class(A) constructor create(AOwner:TComponent); end; то процедуру S можно вызвать от класса С: var x: С; begin x.S end. Однако если в класс C ввести объявление метода с именем S, type C = class(A) procedure S; constructor create(AOwner:TComponent); end; а в программу – соответствующее, например, такое определение: procedure C.S; var x: integer; begin writeln('Proc S from class C'); readln(x) end; то процедура C.S “экранирует” процедуру S класса A. При том же обращении к S будет вызвана процедура S из класса A: 1.3. Полиморфизм Полиморфизм обеспечивается динамическим связыванием переменной с классом присвоенного ей объекта. В следующем примере ClassTest02: program ClassTest02; {$APPTYPE CONSOLE} uses SysUtils, classes; type A = class(TComponent) procedure M; virtual; constructor create(AOwner:TComponent); end; type C = class(A) procedure M; override; constructor create(AOwner:TComponent); end; procedure A.M; var x: integer; begin writeln('Proc M from class A'); readln(x) end; constructor A.create(AOwner:TComponent); begin inherited create(AOwner) end; procedure C.M; var x: integer; begin writeln('Proc M from class C'); read(x) end; constructor C.create(AOwner:TComponent); begin inherited create(AOwner) end; var x: A;z: C; begin x:=A.create(nil); x.M; z:=C.create(nil); z.M; x:=z; x.M end. от переменной x вызывается метод M класса А, затем от переменной z M класса С, а после присвоения x значения (объекта) переменной z вызывается M класса C. Полиморфизм процедуры M обеспечивается директивой virtual в классе A и директивой override в классе C. 1.4. Свойства и события (методы объекта, делегаты) 1.5. Класс классов УПРАЖНЕНИЯ 1. Составить и выполнить аналогичные примеры в среде оконного приложения Borland Delphi. 2. Составить и выполнить аналогичные примеры в среде оконного приложения Microsoft Visual Studio 2008. 2.1. Оконные приложения в IDE Delphi c библиотекой VCL Пример 1. Создание заготовки проекта.
1) запустите IDE Delphi. Вы увидите пустую форму 2) нажмите функциональную клавишу F12. Вы увидите текст модуля: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} end. Этот модуль содержит объявление класса TForm1 - наследника класса Tform и глобальную переменную Form1 этого класса. Объявление класс TForm можно найти в модуле Forms, который связан с данным модулем в операторе uses. 3) вернитесь к графическому представлению формы, вновь нажав функциональную клавишу F12, наведите на неё курсор мыши и вызовите всплывающее меню нажатием правой клавиши; в этом меню активируйтеView
as
Text
. Вы увидите object Form1: TForm1 Left = 192 Top = 107 Width = 591 Height = 483 Caption = 'Form1' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13 end Этот текст представляет свойства класса TForm1 и их значения, которые автоматически установлены в приложении. При сохранении проекта этот текст образует файл Unit1.dfm, в то время, как Unit1 образует файл Unit1.pas. 4) активируйте в главном меню Project – View Source
. Вы увидите текст корневого сегмента программы program Project1; uses Forms, Unit1 in 'Unit1.pas' {Form1}; {$R *.res} begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end. Нетрудно заметить, что выполняются три метода класса TApplication, второй из которых создаёт экземпляр класса TForm1, определённый в модуле Unit1, и присваивает его там же определённой переменной Form1. Метод Run активирует окно, “скрывающееся ” в форме. Начало корневого сегмента служит точкой формы приложения. Поэтому рассмотренные предложения выполняются, если запустить приложение, например, нажатием функциональной клавиши F9. В результате будет создан экземпляр класса TForm1 и он будет показан на экране компьютера как форма. То есть, заготовка, предоставляемая программисту при запуске IDE является корректным, работоспособным приложением (хотя практически бессодержательным), и пользователь приложения фактически работает с экземпляром определённого в приложении класса. Пример 2.
Добавление в форму кнопки и обработка события от её нажатия.
1) перенесите с помощью мыши кнопку из палитры компонентов на форму: При этом в модуле Unit1 в определение класса TForm1 будет автоматически включено поле (включённое предложение подчёркнуто): unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) Button1: TButton;
private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} end. А в тексте, скрытом за формой (файл Unit1.dfm) появятся предложения, определяющие кнопку как объект и его свойства: object Form1: TForm1 Left = 192 Top = 107 Width = 287 Height = 211 Caption = 'Form1' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13 object Button1: TButton
Left = 64
Top = 72
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
end
end 2) дважды кликните по кнопке или по пустому полю строки Click в закладке “События” инспектора объектов (когда кнопка в фокусе!). В результате в Unit1 в определение класса включается заголовок обработчика события procedure Button1Click(Sender: TObject); а в раздел implementation включается заготовка тела этого метода: procedure TForm1.Button1Click(Sender: TObject); begin
end;
end.
В текст файла Unit1.dfm, в описание кнопки добавляется строка OnClick = Button1Click После введения программистом в тело обработчика предложений, задающих требуемую обработку события, приложение может быть запущено. Например, после ввода предложения ShowMessage(‘Привет!') запуска приложения и нажатия на кнопку на форму выдаётся соответствующее сообщение: УПРАЖНЕНИЯ 1. Добавьте в форму главное меню и обработайте события от активации его пунктов. 2.2. Приложение Windows Forms в Microsoft Visual Studio 2008 для среды CLR на языке C# Пример 1. Создание заготовки проекта.
3) запустите IDE Visual Studio 2008, 4) активируйте в главном меню File
– New
– Application
При создании нового проекта этого типа Visual Studio 2008 автоматически создает заготовку проекта в виде директории с именем проекта, содержащей: модуль имя_проекта
.csproj модуль Form1.cs модуль Form1.Designer.cs модуль Program.cs директория bin директория obj директория Properties. В директории Properties содержатся модули: AssemblyInfo.cs, Resources.Designer.cs, Resources.resx, Settings.Designer.cs, Settings.settings. В главном окне среды проектирования отображаются исходные тексты на языке C# (с расширением cs). Они отображаются либо автоматически при открытии проекта, либо могут быть выведены на экран с помощью главного меню Файл - Открыть – Файл … . Кроме того, в главном окне автоматически или по команде главного меню Вид-Конструктор показывается закладка Form1.cs[Конструктор], на которой представлен прототип главного окна (формы) проектируемого приложения. Модуль Program.cs, содержит статический класс Program с методом main(). Этот метод является первым, который начинает выполняться при запуске проектируемого приложения. Метод main() вызывает методы класса Application, определённого в пространстве имён System.Windows.Forms и содержащего, в частности, метод Run, запускающий обработку сообщений к окну. Окно (форма) создаётся тут же оператором new
как экземпляр класса Form1, который определён в модулях Form1.cs и Form1.Designer.cs проекта. Форма Form1 становится главной формой приложения. Далее приведён текст модуля Program.cs для проекта CSprim using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace CSprim { static class Program { /// <summary> /// Главная точка входа для приложения. /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } }} Определение класса Form1 и содержащие его модули создаются автоматически при создании проекта. Класс разделён на две части, первая из которых, Form1.cs, содержит конструктор этого класса, вызывающий функцию InitializeComponent, которая определена в модуле Form1.Designer.cs. Далее приведён текст модуля Form1.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace CSprim { public partial class Form1 : Form { public Form1() { InitializeComponent(); } } } Модуль Form1.Designer.cs содержит: определение поля components для коллекции компонентов, сопоставляемых с формой; эта коллекция определяется как пустая; тип поля – интрефейс Icontainer из пространства имён System.ComponentModel, объявляющий метод Dispose – расформирования коллекции и освобождение ресурсов, занятых её элементами; определение функции Dispose, которая освобождает ресурсы компонентов, входящих в коллекцию поля components, и ресурсы самой формы; . определение функции InitializeComponent, которая создает экземпляр класса Container (для компонентов) и присваивает его полю components, устанавливает шрифт для формы и её титульную надпись. Модуль Модуль Form1.Designer.cs: namespace CSprim { partial class Form1 { /// <summary> /// Требуется переменная конструктора. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Освободить все используемые ресурсы. /// </summary> /// <param name="disposing">истинно, если управляемый ресурс должен быть удален; иначе ложно.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Код, автоматически созданный конструктором форм Windows /// <summary> /// Обязательный метод для поддержки конструктора - не изменяйте /// содержимое данного метода при помощи редактора кода. /// </summary> private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Text = "Form1"; } #endregion } } Все эти построения осуществляются автоматически, заготовка приложения является корректным приложением, которое может быть запущено в режиме отладки в среде проектирования или независмо от среды отладки как модуль имя-проекта
.exe в директории Debug – поддиректории bin или obj директории проекта. При запуске приложения метод main() создает экземпляр класса Form1, вызывая оператором new
его конструктор, а тот вызывает функцию InitializeComponent(), которая заполняет форму, присваивает форме и попавшим на неё компонентам заданные параметры и выводит её на экран. Метод Run активизирует очередь сообщений к форме как окну. Пример 2. Добавление в форму кнопки и обработка события от её нажатия.
При разработке приложения на форму могут устанавливаться компоненты и вписываться обработчики событий, связанных с этитми компонентами. При этом в модуле Form1.cs автоматически формируются заготовки обработчиков событий, а в модуле Form1.Designer.cs – объявления компонентов как полей класса. В текст объявления функции InitalizeComponent(), также автоматически, вводятся предложения, обеспечивающие создания экземпляров компонентов формы при создании формы и присвоения им заданных значений свойств. Далее приведён текст модулей Form1.cs и Form1.Designer.cs после установки на форме экранной кнопки. При этом Form1.cs[Конструктор] приобретает следующий вид. Модуль Form1.cs: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace CSprim { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { MessageBox
.Show("
Привет
"
);
} } } MessageBox.Show("Привет"); которую вводит программист. В результате при нажатии на экранную кнопку на эуран выдаётся сообщение “Привет!”. Модуль Form1.Designer.cs (в него автоматически введены подчёркнутые строки): namespace CSprim { partial class Form1 { /// <summary> /// Требуется переменная конструктора. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Освободить все используемые ресурсы. /// </summary> /// <param name="disposing">истинно, если управляемый ресурс должен быть удален; иначе ложно.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Код, автоматически созданный конструктором форм Windows /// <summary> /// Обязательный метод для поддержки конструктора - не изменяйте /// содержимое данного метода при помощи редактора кода. /// </summary> private void InitializeComponent() { this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout(); //
// button1
//
this.button1.Location = new System.Drawing.Point(97, 94);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this
.button1.Click += new System.EventHandler(this.button1_Click);
// // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(292, 273); this.Controls.Add(this.button1); this.Name = "Form1"; this.Text = "Form1"; this.ResumeLayout(false); } #endregion private System.Windows.Forms.Button button1;
} } УПРАЖНЕНИЯ 1. Добавьте в форму главное меню и обработайте события от активации его пунктов. 2.3. Приложение Windows Forms в Microsoft Visual Studio 2008 для среды CLR на языке C++ 2.4. Приложение Windows Forms в Microsoft Visual Studio 2008 для среды Win32 // CppWin0101.cpp: определяет точку входа для приложения. // #include "stdafx.h" #include "CppWin0101.h" #define MAX_LOADSTRING 100 // Глобальные переменные: HINSTANCE hInst; // текущий экземпляр TCHAR szTitle[MAX_LOADSTRING]; // Текст строки заголовка TCHAR szWindowClass[MAX_LOADSTRING]; // имя класса главного окна // Отправить объявления функций, включенных в этот модуль кода: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: разместите код здесь. MSG msg; HACCEL hAccelTable; // Инициализация глобальных строк LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_CPPWIN0101, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // Выполнить инициализацию приложения: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CPPWIN0101)); // Цикл основного сообщения: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; } // // ФУНКЦИЯ: MyRegisterClass() // // НАЗНАЧЕНИЕ: регистрирует класс окна. // // КОММЕНТАРИИ: // // Эта функция и ее использование необходимы только в случае, если нужно, чтобы данный код // был совместим с системами Win32, не имеющими функции RegisterClassEx' // которая была добавлена в Windows 95. Вызов этой функции важен для того, // чтобы приложение получило "качественные" мелкие значки и установило связь // с ними. // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CPPWIN0101)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCE(IDC_CPPWIN0101); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex); } // // ФУНКЦИЯ: InitInstance(HINSTANCE, int) // // НАЗНАЧЕНИЕ: сохраняет обработку экземпляра и создает главное окно. // // КОММЕНТАРИИ: // // В данной функции дескриптор экземпляра сохраняется в глобальной переменной, а также // создается и выводится на экран главное окно программы. // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // Сохранить дескриптор экземпляра в глобальной переменной hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } // // ФУНКЦИЯ: WndProc(HWND, UINT, WPARAM, LPARAM) // // НАЗНАЧЕНИЕ: обрабатывает сообщения в главном окне. // // WM_COMMAND - обработка меню приложения // WM_PAINT -Закрасить главное окно // WM_DESTROY - ввести сообщение о выходе и вернуться. // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Разобрать выбор в меню: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: добавьте любой код отрисовки... EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // Обработчик сообщений для окна "О программе". INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; } УПРАЖНЕНИЯ 1. Добавьте в форму главное меню и обработайте события от активации его пунктов. 3. СПИСКИ И ТАБЛИЦЫ
3.1. Прото-СУБД на основе класса TStringList и TStringGrid (библиотека VCL Delphi) unit Toy; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Grids, Menus; type TFToy = class(TForm) Grid: TStringGrid; MainMenu1: TMainMenu; N1: TMenuItem; N2: TMenuItem; N3: TMenuItem; N4: TMenuItem; OpenDialog1: TOpenDialog; SaveDialog1: TSaveDialog; procedure FormCreate(Sender: TObject); procedure N3Click(Sender: TObject); procedure ListToGrid(l, g: integer); procedure GridToList(g, l: integer); procedure ShiftUp; procedure ShiftDown; procedure LoadGrid; procedure N4Click(Sender: TObject); procedure GridKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); private { Private declarations } public { Public declarations } end; var FToy: TFToy; sList1, sList2: TStringList; curl, curg: integer; vr:TGridRect; implementation {$R *.dfm} uses math; procedure TFToy.ShiftUp; var i: integer; begin if Grid.RowCount>1then for i:=2to Grid.RowCount-1 do begin Grid.cells[1,i-1]:=Grid.cells[1,i]; Grid.cells[2,i-1]:=Grid.cells[2,i] end; if sList1.Count-1=curl then begin Grid.cells[1,Grid.RowCount-1]:=''; Grid.cells[2,Grid.RowCount-1]:='' end else begin Grid.cells[1,Grid.RowCount-1]:=sList1[curl+1]; Grid.cells[2,Grid.RowCount-1]:=sList2[curl+1] end end; procedure TFToy.ShiftDown; var i: integer; begin if Grid.RowCount>1 then begin for i:=Grid.RowCount-2 downto 1 do begin Grid.cells[1,i+1]:=Grid.cells[1,i]; Grid.cells[2,i+1]:=Grid.cells[2,i] end; Grid.cells[1,i+1]:=sList1[curl-1]; Grid.cells[2,i+1]:=sList2[curl-1] end else begin Grid.cells[1,1]:=''; Grid.cells[2,1]:='' end end; procedure TFToy.GridToList(g, l: integer); begin if l>=sList1.Count then begin sList1.Add(Grid.cells[1,g]); sList2.Add(Grid.cells[2,g]) end else begin sList1[l]:=Grid.cells[1,g]; sList2[l]:=Grid.cells[2,g] end end; procedure TFToy.ListToGrid(l, g: integer); begin Grid.cells[1,g]:=sList1[l]; Grid.cells[2,g]:=sList2[l] end; procedure TFToy.LoadGrid; var i: integer; begin if sList1.Count>0 then begin for i:=1 to min(Grid.RowCount-1, sList1.Count)do ListToGrid(sList1.Count-min(Grid.RowCount-1, sList1.Count)+i-1,i); curl:=sList1.Count-1; curg:=i-1 end else begin curl:=0; curg:=1 end; Grid.Cells[0,curg]:=' * '; vr.left:=1;vr.right:=1; vr.Top:=curg; vr.bottom:=curg; Grid.Selection:=vr end; procedure TFToy.FormCreate(Sender: TObject); begin sList1:=TStringList.Create; sList2:=TStringList.Create; OpenDialog1.Execute; if not(OpenDialog1.FileName='') then begin sList1.LoadFromFile(OpenDialog1.FileName); sList2.LoadFromFile(copy(OpenDialog1.FileName,1,length(OpenDialog1.FileName)-4)+'1.txt');; LoadGrid end else begin Grid.Cells[0,1]:=' * '; curl:=0; curg:=1 end end; procedure TFToy.N3Click(Sender: TObject); begin SaveDialog1.Execute; if SaveDialog1.FileName='' then showmessage('Имя файла не определено!') else begin sList1.SaveToFile(SaveDialog1.FileName); sList2.SaveToFile(copy(SaveDialog1.FileName,1,length(SaveDialog1.FileName)-4)+'1.txt') end end; procedure TFToy.N4Click(Sender: TObject); begin close end; procedure TFToy.GridKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if key=vk_Down then begin GridToList(curg, curl); if curg=Grid.RowCount-1 then begin ShiftUp; curl:=curl+1 end else begin Grid.Cells[0,curg]:=''; curg:=curg+1; Grid.Cells[0,curg]:=' * '; curl:=curl+1 end; vr.left:=1;vr.right:=1; vr.Top:=curg-1; vr.bottom:=curg-1; Grid.Selection:=vr end; if key=vk_Up then begin GridToList(curg, curl); if curg=1 then if curl>0 then begin ShiftDown; curl:=curl-1 end else else begin Grid.Cells[0,curg]:=''; curg:=curg-1; Grid.Cells[0,curg]:=' * '; curl:=curl-1 end; vr.left:=1;vr.right:=1; vr.Top:=curg+1; vr.bottom:=curg+1; Grid.Selection:=vr end end; end. УПРАЖНЕНИЯ 1. Обеспечьте в прото-СУБД поиск путём явного перебора элементов базы данных. 2. Обеспечьте в прото-СУБД поиск путём использования метода IndexOf. 3. Экспериментально сравните быстродействие вариантов из упражнений 1 и 2. 4. РАЗРАБОТКИ И ВЕРИФИКАЦИЯ РАСЧЁТНЫХ ПРИЛОЖЕНИЙ
4.1. Спецификация программы uWert Программа uWert является экранным приложением, обеспечивающим вычисле-ние заданного пользователем регулярного арифметического выражения. Регулярное арифметическое выражение (РАВ) определяется в алфавите L десятичных цифр, знаков операций сложения +, вычитания - и умножения *, а также левой и правой круглой скобок по индукции: 1. Нумерал (здесь последовательность цифр без знака и без пробелов) является РАВ. 2. a) если строка X является РАВ, то строка (X) является РАВ; b) если строки X и Y являются РАВ, то строки X+Y, X-Y, X*Y являются РАВ. Нумералы представляют десятичные целые дчисла, результат вычисляется в соответствии с указанными знаками операций в соответствии с правилами обычной десятичной арифметики. Это приложение имеет простой графический пользовательский интерфейс: окно ввода регулярного арифметического выражения, окно результата и кнопка, нажатие на которую вызывает вычисление.
4.2. Текст программы uWert Программа отлажена на языке Object Pascal (Borland Delphi). В приведённом ниже тексте тела нескольких процедур перенумерованы для удобства дальнейших ссылок. Unit uWert; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) eExpression: TEdit; eValue: TEdit; Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; R = record va: integer; sgn: char; end; Stack = class(TComponent) private ar: array[1..100]of R; top: integer; protected procedure push(vr: R); function pop: R; public constructor create(AOwner: TComponent); destructor Destroy; function show(n:integer):R; property Depth: integer read top; end; LetterBefore=(lbNone, lbDig, lbSign, lbLBr, lbRBr); OperationMode=(omBOS, omNorm, omEOS); OperandNumber=(onFirst,onSecond); var Form1: TForm1; buf: Stack; lb: LetterBefore; implementation {$R *.dfm} constructor Stack.create(AOwner: TComponent); begin inherited create(AOwner); top:=0 end; destructor Stack.Destroy; begin inherited destroy end; procedure Stack.push(vr: R); begin if top<100 then begin top:=top+1; ar[top]:=vr end else end; function Stack.pop: R; begin if top>0 then begin result:=ar[top]; top:=top-1 end else raise Exception.Create('Stack is empty!') end; function Stack.show(n:integer):R; begin result:=ar[n] end; procedure TForm1.Button1Click(Sender: TObject); var i, n, fdig, ldig: integer; result: integer;wr: R; function SelectDigit: integer; begin //involve sign of the number if(fdig>2)and((eExpression.text[fdig-1]='-')or(eExpression.text[fdig-1]='+')) and((eExpression.text[fdig-2]='-')or(eExpression.text[fdig-2]='+') or(eExpression.text[fdig-2]='*')or(eExpression.text[fdig-2]='/') or(eExpression.text[fdig-2]='(')) or(fdig=2)and ((eExpression.text[1]='-')or(eExpression.text[1]='+')) then fdig:=fdig-1; result:=StrToInt(copy(eExpression.text, fdig, ldig-fdig+1)) end; function Calc(n,m: integer; s: char): integer; begin if s='+'then result:=n+m else result:=n-m end; procedure Alloc(v: integer;s: char);//sign upon digit var vp: integer; sp: char; begin 1: wr:=buf.pop; 2: if wr.sgn='*' 3: then if s='*'//sgn='*' 4: then wr.va:=wr.va*v 5: else begin //s='+' or '-' 6: vp:=wr.va*v; 7: wr:=buf.pop;//delete multiplicative cell 8: wr.va:=Calc(wr.va,vp,wr.sgn)//update last cell 9: end 10: else if s='*'//sgn='+' or '-' 11: then begin 12: buf.push(wr);//additive part is restored 13: wr.va:=v 14: end 15: else wr.va:=Calc(wr.va,v,wr.sgn); 16: wr.sgn:=s; 17: buf.push(wr) end; procedure Sab(c: char);//sign upon bracket var vp: integer; begin 1: wr:=buf.pop; 2: if wr.sgn='*' 3: then if not(c='*')//'+' or '-' 4: then begin 5: vp:=wr.va; 6: wr:=buf.pop;//delete bracket cell 7: wr.va:=Calc(wr.va,vp,wr.sgn)//update last cell 8: end; 9: wr.sgn:=c; 10: buf.push(wr) end; procedure spin(i: integer; c: char); var vp: integer; begin 1: case c of 2: '(': if(lb=lbNone)or(lb=lbSign)or(lb=lbLBr) 3: then begin 4: with wr do begin va:=0; sgn:= '+'end; 5: buf.push(wr);//create new additive cell 6: lb:=lbLBr; 7: end 8: else raise Exception.Create('Wrong sequence!'); 9: ')','F': begin 10: wr:=buf.pop; 11: if wr.sgn='*' 12: then begin //sgn='+' or '-' 13: if lb=lbDig then vp:=wr.va*SelectDigit 14: else vp:=wr.va; 15: wr:=buf.pop; 16: wr.va:=Calc(wr.va,vp,wr.sgn) 17: end 18: else if lb=lbDig then wr.va:=Calc(wr.va,SelectDigit,wr.sgn); 19: if c=')'then begin 20: vp:=wr.va; 21: wr:=buf.pop; 22: if wr.sgn='*'then wr.va:=wr.va*vp 23: else begin 24: buf.push(wr);//restore cell to create 25: wr.va:=vp;//cell for case of future 26: wr.sgn:='*'//multiplication 27: end; 28: lb:=lbRBr 29: end; 30: buf.push(wr) 31: end; 32: '+','-','*': 32: begin 33: if((c='*')or(c='/'))and((lb=lbNone)or(lb=lbLBr))then raise Exception.Create('wrong input digit'); 34: if lb=lbDig then Alloc(SelectDigit,c); 35: if lb=lbRBr then Sab(c); 36: lb:=lbSign 37: end; 38: '0','1','2','3','4','5','6','7','8','9': 39: begin 40: if(lb=lbNone)or(lb=lbSign)or(lb=lbLBr) 41: then begin 42: fdig:=i; 43: ldig:=i; 44: lb:=lbDig 45: end 46: else ldig:=i; 47: if lb=lbRBr then raise Exception.Create('Wrong sign sequence'+ ' '+IntToStr(i)); 48: end 49: end//case end; begin buf:=Stack.create(self); with wr do begin va:=0; sgn:= '+'end; buf.push(wr); lb:=lbNone; try for i:=1to length(eExpression.text)do spin(i, eExpression.text[i]); if(lb=lbDig)or(lb=lbRBr) then else raise Exception.Create('wrong end of input'+ ' '+IntToStr(i)); spin(i,'F'); eValue.text:=IntToStr(buf.pop.va); if buf.Depth>1then raise Exception.Create('wrong end of input!') except on E: Exception do begin showmessage(E.message);buf.Destroy end end end; end. 4.3. Доказательство корректности программы uWert Концепция программы состоит в следующем: 1) исходное РАВ обрабатывается в цикле литера за литерой по одной литере на каждом шаге цикла, без возвратов, 2) рабочая информация накапливается в стеке и переменных fdig, ldig и lb, 3) ячейка стека содержит промежуточный результат вычисления или его часть, 4) переменные fdig и ldig – это номера первой и последней литеры “текущего” нумерала, то есть нумерала, в пределах которого находится переменная цикла, 5) переменная lb хранит литеру, предшествующую “текущей”, то есть литере, номер которой в исходном РАВ совпадает со значением переменной цикла. Для более формального исследования программы и доказательства её корректности далее приведён ряд определений. “Мультипликативное окончание” М(X) для РАВ X определяется индукцией по построению X: I. База индукции. 1.1. Если X – нумерал, то M(X) = ‘’ (то есть, мультипликативное окончание отсутствует). 1.2. Если X = m*n , где m и n – нумералы, то M(X) = X. II. Шаг индукции: 2.1. М((X)) = (X), (то есть, РАВ (X) совпадает со своим мультипликативным окончанием). 2.2. M(Y+Z) = M(Z). 2.3. M(Y-Z) = M(Z). 2.4. M(Y*Z) = M(Y)*Z, если M(Z) = Z, иначе M(Y*Z) = M(Z). Очевидно, что если M(X) не пусто, то М(X) является РАВ. Мультипликативным называется РАВ, совпадающее со своим окончанием. Выражение X\Y определено, если только существует такое Z, что X=ZY и тогда X\Y = Z. Функция F(X) от регулярного арифметического выражения (РАВ) определяется по индукции: I. База индукции. Если ‘X’ - нумерал, то F(X) = StrToInt(‘X’). II. Шаг индукции: 2.1. Если X и Y являются РАВ, то F(X+Y) = F(X)+F(Y), F(X-Y) = F(X)-F(Y). 2.2. F(X*Y) = F(X)*F(Y), если M(X) = X и M(Y) = Y, иначе F(X*Y) = F(X*Y\M(Y))+F(M(Y)). F от пустой строки равно 0. Теорема 1
. Пусть
программа uWert (далее просто Программа) применяется к слову eExpression.Text = ‘AXB’, где X – РАВ, а B – строка, которая может быть пустой, и приступает к выполнению процедуры spin в цикле при i = length(‘A’)+1, причём: a) lb = lbNone, если A пуста, или lb = lbSign или lb = lbLBr, в противном случае, b) стек buf содержит последовательность ячеек r1, …, rk, и ячейка rk содержит число n и знак операции ‘+’ или ‘-’. Тогда
на шаге i = length(‘AX’) цикла на выходе процедуры spin установится одно из следующих состояний. Случай А.
lb = lbDig, переменные fdig и ldig указывают начало и, соответственно, конец нумерала m, завершающего X, стек buf содержит последовательность ячеек r1, …, rk, и ячейка rk содержит число n + F(X\m) и прежний знак операции ‘+’ или ‘-’. Случай В.
lb = lbDig, переменные fdig и ldig указывают начало и, соответственно, конец нумерала m, завершающего X, стек buf содержит последовательность ячеек r1, …, rk, rk+1, ячейка rk содержит число n + F(X\S) и прежний знак операции, а rk+1 содержит число F(S\m) и знак операции ‘*’, при этом S – мультипликативное окончание X. Если X – мультипликативное РАВ, то F(X\S)=0. Случай
С.
lb = lbRBr, стек buf содержит последовательность ячеек r1, …, rk, rk+1, ячейка rk содержит число n+ F(X\S) и прежний знак операции, а rk+1 содержит число F(S) и знак операции ‘*’, при этом S – мультипликативное окончание X. Если X – мультипликативное РАВ, то F(X\S)=0. Доказательство. Проводится индукцией по построению X как РАВ. I. База индукции. Пусть X - нумерал (то есть последовательность цифр без знака и без пробелов). Тогда Программа выполнит length(‘X’) шагов цикла, в которых будут работать строки 39 - 48 процедуры spin, переменные fdig и ldig установится на начало и, соответственно, конец строки X, а lb будет устанавливаться равным lbDig на всех шагах до шага length(‘X’) включительно. Стек не изменяется. То есть, реализуется случай А. II. Шаг индукции. 2.1. Пусть X = (Y), где Y – РАВ. Тогда в результате выполнения строк 2 – 7 процедуры spin в стек будет добавлена ячейка rk+1, содержащая число 0 и знак ‘+’, и, таким образом, перед следующим выполнение шага цикла для Y установятся условия данной леммы, причём lb = lbLBr, B = ‘)B’. По предположению индукции Программа выполнит length(Y) шагов цикла и на последнем из них на выходе из процедуры spin установится состояние, соответствующее одному из 3 указанных случаев. Пусть реализован случай А. Тогда на следующем шаге цикла в результате выполнения строк 18 – 26 к числу в ячейке rk+1 будет прибавлено число m и установлен знак операции ‘*’. Эта ячейка будет содержать F(‘X). В строке 28 переменной lb будет присвоено значение lbRBr, то есть, в данном случае при обработке (X) реализуется случай C. Пусть реализован случай В. Тогда на следующем шаге цикла в результате выполнения строк 13, 15 и 16 ячейка rk+2 снимается со стека, число из ячейки rk+2 умножается на число m, со стека снимается ячейка rk+1 и произведение складывается с её содержимым. Затем в строке 21 снимается ячейка rk, но поскольку в ней указана аддитивная операция, в строке 24 она возвращается в стек, в строке 30 в стек вводится ячейка rk+1, а строки 25 и 26 обеспечивают в ней вычисленную в строке 16 сумму и знак умножения. Таким образом, в данном случае при обработке (X) реализуется случай C. Пусть реализован случай C. Тогда на следующем шаге цикла в результате выполнения строки 10 ячейка rk+2 снимается со стека, и число из неё в строке 14 временно сохраняется. Затем в строке 15 со стека снимается ячейка rk+1, и в строке 16 число из ячейки rk+1 складывается с сохранённым числом – результатом обработки X и в строке 20 временно сохраняется. Затем в строке 21 со стека снимается ячейка rk, но поскольку в ней указана аддитивная операция, в строке 24 она возвращается в стек, в строке 30 в стек вводится ячейка rk+1, а строки 25 и 26 обеспечивают в ней сохранённое число и знак умножения. Таким образом, в данном случае при обработке (X) реализуется случай C. 2.2. Пусть X= YwZ, где Y и Z – РАВ, а w – ‘+’ или ‘-’. Тогда применим индукционное предположение к Y и рассмотрим случаи. Пусть реализован случай А. Тогда на следующем шаге цикла в результате выполнения строки 35 вызывается процедура Alloc. В ней в строке 15 число, выделенное значениями переменных fdig и ldig, складывается с числом в ячейке rk. В результате число в ячейке rk становится равным n+F(X). Строки 1, 16 и 17 обеспечивают занесение его в эту ячейку Переменная lb устанавливается равной lbSign. Пусть реализован случай B. Тогда на следующем шаге цикла в результате выполнения строки 35 вызывается процедура Alloc. В ней в строке 6 число, выделенное значениями переменных fdig и ldig, умножается на число в ячейке rk+1, а в строках 7 и 8 складывается с числом из ячейки rk. Строки 1, 16 и 17 обеспечивают занесение суммы в эту ячейку и установку в ней w как знака операции. В результате число в ячейке rk становится равным n+F(X). Переменная lb устанавливается равной lbSign. Пусть реализован случай С. Тогда на следующем шаге цикла в результате выполнения строки 36 вызывается процедура Sab. В ней в строке 7 число, сформированное в ячейке rk, складывается с числом или вычитается из числа, сформированного в ячейке rk+1. В результате число в ячейке rk становится равным n+F(X). Переменная lb устанавливается равной lbSign. Далее Программа применяется к Z и по предположению индукции Программа переходит к одному из случаев, определённых в формулировке леммы. Пусть при обработке Z реализован случай А. Пусть при обработке Z реализован случай B. Тогда cтек имеет rk+1 ячеек. Переменная lb будет равна lbDig. Ячейка rk содержит число n+F(Y)+F(Z\M(Y\m)) = n+F(Y+Z\M(Y\m) в обозначениях случая А. Ячейка rk+1 содержит число F(M(Z\m)) = F(M(Y+Z\m)). То есть, при обработке Y+Z реализован случай B. Пусть при обработке Z реализован случай С. Тогда cтек имеет rk+1 ячеек. Переменная lb будет равна lbRBr. Ячейка rk содержит число n+F(Y)+F(Z\M(Y)) = n+F(Y+Z\M(Y) в обозначениях случая А. Ячейка rk+1 содержит число F(M(Z)) = F(M(Y+Z)). То есть, при обработке Y+Z реализован случай С. 2.3. Пусть X=Y*Z. Рассмотрим представление (разложение) X = Y*Z\M(Y*Z)+M(Y*Z). Если обе части разложения не пусты, то применим к ним предположение индукции п.2.2 теоремы. Рассмотрим случай, когда M(Y*Z) пусто. Тогда существуют такое РАВ Q и число m, что Y*Z =Q+m. Применим к ним предположение индукции п.2.2 теоремы. Рассмотрим случай, когда Y*Z\M(Y*Z) пусто, то есть, Y*Z – мультипликативный РАВ. Тогда применим Программу к Y, как в п.2.2. Тогда после обработки Y* Программа придёт в следующее состояние. Переменная lb = lbSign, стек содержит rk+1 ячеек, в ячейке rk содержится исходное число n. Ячейка rk+1 содержит число F(X), а знак является знаком операции умножения. Рассмотрим применение в этих условиях Программы к Z индукцией по построению Z. I. База индукции. 1.1. Пусть Z является нумералом m. Тогда на выходе из его обработки переменные fdig и ldig указывают начало и, соответственно, конец этого нумерала. Стек содержит те же rk+1 ячеек. Число в ячейке rk+1 F(X) = F(Y) = F(Y*m\m) = F(X\m). То есть, выполняются условия случая B. 1.2. Пусть Z является (Y). Тогда нетрудно убедиться, как в п.2.1 выше, после обработки Программой такого Z установятся условия случая C. Число в ячейке rk+1 F(X) = F(Y)*F(Z)=D(Y*Z). II. Шаг индукции. Пусть X=Y*Z*m. 2.1. Пусть X=Y*Z*m. По предположению индукции после обработки Y*Z установятся условия теоремы. Применяя рассуждения п.1.1, можно убедиться, что после обработки m установятся условия случая B. Число в ячейке rk+1 F(X\m) = F(Y*Z*m\m) = F(Y)*F(Z*m\m). 2.2. Пусть X=Y*Z*(U). По предположению индукции после обработки Y*Z установятся условия теоремы. Применяя рассуждения п.1.2, можно убедиться, что после обработки (U) установятся условия случая C. Число в ячейке rk+1 F(X) = F(Y*Z*(U)) = F(Y)*F(Z)*F(U)) = F(Y)*F(Z*(U)). Теорема доказана. Следствие.
Если программу uWert применить к РАВ X, то в качестве результата будет получено число F(X). Доказательство. Пусть Программа применяется к РАВ X. Нетрудно убедиться, что при входе в цикл выполняются условия Теоремы 1, причём стек содержит одну ячейку, то есть, k=1, n=0 и z=’+’. Поэтому Программа выйдет из цикла и перейдёт к процедуре spin(i, ‘F’) , установив один из 3 случаев состояния, указанных в Теореме. Рассмотрим действия процедуры spin(i, ‘F’) в каждом из этих случаев. Случай А. В строке 18 число в ячейке 1 складывается с числом в конце X. В результате это число становится равным F(X). Случай B. Стек содержит 2 ячейки. Строка 13 умножает число из ячейке 2 на число в конце X. Затем в строках 15 и 16 произведение складывается с числом в ячейке 1, а ячейка 2 выводится из стека. В результате единственная ячейка стека содержит F(X). Случай C. Стек содержит 2 ячейки. Строка 14 временно сохраняет число из ячейки 2. Затем в строках 15 и 16 оно складывается с числом в ячейке 1, а ячейка 2 выводится из стека. В результате единственная ячейка стека содержит F(X). Число из ячейки стека выводится в окно eValue. Что и требовалось доказать. УПРАЖНЕНИЯ 1. Оцените конструктивную сложность программы по M.Холстеду. 2. Оцените вычислительную сложность программы. 5. ОРГАНИЗАЦИЯ МНОГООКОННОГО ИНТЕРФЕЙСА И ДИАЛОГА В ПРИЛОЖЕНИЯХ
5.1. Многооконный интерфейс на основе сохраняемых форм 5.2. Диалог на основе динамически создаваемых форм 5.2.1. Проект диалоговой функции в Delphi unit CyrDialog; interface //////////////////////////////////////////////////////////////////////////////// //* Модуль обеспечивает диалог приложения с пользователем //* и для этой цели предоставляет функцию Rudial, аналогичную //* функции MessageDLG. Отличие состоит в том, что Rudial //* показывает кнопки с русскязычными надписями. uses classes, forms, dialogs, stdctrls, controls; type TDForm = class(TCustomForm)end; function Rudial(const Msg: string; DlgType: TMsgDlgType; Buttons: TMsgDlgButtons; HelpCtx: Longint): Word; var vform: TDForm; implementation uses math, graphics, windows, sysutils; function Rudial(const Msg: string; DlgType: TMsgDlgType; Buttons: TMsgDlgButtons; HelpCtx: Longint): Word; var vbut: TButton; vt: TLabel; i, n: integer; ab: array [1..11]of string; mr: array [1..11]of TModalResult; begin n:=0; if mbOK in Buttons then begin inc(n); ab[n]:='О''КЭЙ'; mr[n]:=mrOK end; if mbYes in Buttons then begin inc(n); ab[n]:='ДА'; mr[n]:=mrYES end; if mbNO in Buttons then begin inc(n); ab[n]:='НЕТ'; mr[n]:=mrNO end; if mbCancel in Buttons then begin inc(n); ab[n]:='ОТМЕНА'; mr[n]:=mrCancel end; if mbAbort in Buttons then begin inc(n); ab[n]:='БАСТА'; mr[n]:=mrAbort end; if mbRetry in Buttons then begin inc(n); ab[n]:='ПОВТОР'; mr[n]:=mrRetry end; if mbIgnore in Buttons then begin inc(n); ab[n]:='НУ И ЧТО'; mr[n]:=mrIgnore end; if mbAll in Buttons then begin inc(n); ab[n]:='ВСЕ'; mr[n]:=mrALL end; if mbNoTOALL in Buttons then begin inc(n); ab[n]:='НИКАКИЕ'; mr[n]:=mrALL+1 end; if mbYEStoALL in Buttons then begin inc(n); ab[n]:='ВСЕ'; mr[n]:=mrYEStoALL end; try vform:=TDForm.CreateNew(Application); with vform do begin Height:=110; BorderStyle:=bsDialog; caption:='Ответьте нажатием кнопки.'; position:=poScreenCenter; BorderWidth:=3; FormStyle:=fsStayOnTop; vt:=TLabel.Create(vform); vt.parent:=vform; vt.AutoSize:=true; vt.caption:=msg; vt.Font.Style:=[fsBold]; Width:=max(vt.width+50,70*n+24*(n-1)+50); vt.top:=10; vt.left:=(width-vt.width)div 2; for i:=1to n do begin vbut:=TButton.Create(vform); with vbut do begin parent:=vform; top:=40; width:=70; height:=23; left:=(vform.width-70*n-20*(n-1))div 2+(i-1)*(70+20); Caption:=ab[i]; font.style:=[fsBold]; ModalResult:=mr[i] // onClick:=CliRu end end; result:=ShowModal; FreeAndNil(vform) end finally if not(vform=nil)then vform.free end end; {procedure TDForm.CliRu(sender: TObject); begin TForm(TComponent(sender).owner).close end;} end. 5.2.2. Проект диалоговой функции в Microsoft Visual Studio 2008 (CLR, язык C#) В среде проектирования включение дополнительных форм в проект обеспечивается в главном меню командами Проект – Добавить форму Windows …. В результате открывается окно выбора элемента, в котором уже выбран пункт “Форма Windows Form”. Пользователю достаточно нажать экранную кнопку Добавить. В это же окно можно попасть, активируя другие подпункты пункта главного меню Проект, начинающиеся со cлова Добавить. В проект будет добавлены файлы с именем новой формы и расширением cs и Designer.cs. К новой форме можно обращаться при запуске приложения, выполнив предложения: System.Windows.Forms.Form vbox;//объявление переменной vbox = new NBox();//создание экземпляра формы. Далее её можно сделать, например, сделать видимой на равне с главной формой предложением: vbox.Show (); или видимой в модальном режиме, то есть в режиме вызова (с приостановкой активности вызывающей формы: vbox.ShowDialog(); Кроме того форму можно определить и использовать без “дизайнера”, то есть отдельного модуля, обеспечивающего хранение содержимого формы и заполнение её при запуске. Для этого достаточно определить соответствующий класс и вызывать его конструктор, а заполнение формы обеспечивать по месту этого вызова.Такой случай показан в нижеследующем приложении. Главная форма: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace WinForm12 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { NBox.PseudoFun(); // this.Close(); } } } Наряду с главной формой Form1 в проекте имеется модуль NewBox.cs: using System; using System.Windows.Forms; //using NUnit.Framework; namespace WinForm12 { public class NBox : Form { public static void PseudoFun() { // MessageBox.Show("bingo!"); System.Windows.Forms.Form vbox; vbox = new NBox(); vbox.Name = "NBox"; System.Windows.Forms.Button vbut; vbut = new System.Windows.Forms.Button(); // vbut.Show(); vbut.Location = new System.Drawing.Point(100, 100); vbut.Size = new System.Drawing.Size(100,30); vbut.Text = "ЖМИ"; vbox.Controls.Add(vbut); // vbut.Click += new EventHandler(NBox.vbut_Click); vbut.Click += NBox.vbut_Click; vbox.ShowDialog(); } public void Main() { MessageBox.Show("bingo!"); } public NBox() { InitializeForm(); } private void InitializeForm() { this.Text = "Nbox"; } private static void vbut_Click(object sender, EventArgs e) { // System.Windows.Forms.Button lbut; // lbut = sender as Button; // System.Windows.Forms.Form lbox; // lbox = lbut.Parent as Form; // MessageBox.Show(lbox.Text); // lbox.Close(); ((sender as Button).Parent as Form).Close(); } } } В этом модуле содержится класс NBox, определяющий форму и включающий в себя статический метод PseudoFun, который создаёт экземпляр формы NBox, заносит на неё кнопку и обработчик её нажатия и вызывает форму в модальном режиме. using System; using System.Windows.Forms; namespace WinForm12 { public class NBox : Form { public static void PseudoFun() { System.Windows.Forms.Form vbox; vbox = new NBox(); vbox.Name = "NBox"; System.Windows.Forms.Button vbut; vbut = new System.Windows.Forms.Button(); vbut.Location = new System.Drawing.Point(100, 100); vbut.Size = new System.Drawing.Size(100,30); vbut.Text = "ЖМИ"; vbox.Controls.Add(vbut); // vbut.Click += new EventHandler(NBox.vbut_Click); vbut.Click += NBox.vbut_Click; vbox.ShowDialog(); } public NBox() { InitializeForm(); } private void InitializeForm() { this.Text = "Nbox"; } private static void vbut_Click(object sender, EventArgs e) { // System.Windows.Forms.Button lbut; // lbut = sender as Button; // System.Windows.Forms.Form lbox; // lbox = lbut.Parent as Form; // lbox.Close(); ((sender as Button).Parent as Form).Close(); } } } 5.2.3. Проект диалоговой функции в Microsoft Visual Studio 2008 (CLR, язык C++) Приложение, аналогичное последнему, рассмотренному в предыдущем разделе, можно построить в той же среде проектирования, но для языка C++. В этом случае проект содержит главный файл с расширением cpp, а именно: CLRWin02.cpp: главный файл проекта. #include "stdafx.h" #include "TestForm.h" using namespace CLRWin02; int nn=25; [STAThreadAttribute] int main(array<System::String ^> ^args) { // Включение визуальных эффектов Windows XP до создания каких-либо элементов управления Application::EnableVisualStyles(); Application::SetCompatibleTextRenderingDefault(false); // Создание главного окна и его запуск Application::Run(gcnew TestForm()); return 0; } Модуль NewBox.h, содержащий определение формы, которая динамически создавается и вызывается в модальном режиме из модуля TestForm.h. #pragma once #include "NewBox.h" namespace CLRWin02 { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; /// <summary> /// Сводка для Form1 /// /// Внимание! При изменении имени этого класса необходимо также изменить /// свойство имени файла ресурсов ("Resource File Name") для средства компиляции управляемого ресурса, /// связанного со всеми файлами с расширением .resx, от которых зависит данный класс. В противном случае, /// конструкторы не смогут правильно работать с локализованными /// ресурсами, сопоставленными данной форме. /// </summary> public ref class TestForm : public System::Windows::Forms::Form { public: TestForm(void) { InitializeComponent(); // //TODO: добавьте код конструктора // } protected: /// <summary> /// Освободить все используемые ресурсы. /// </summary> ~TestForm() { if (components) { delete components; } } private: System::Windows::Forms::MenuStrip^ menuStrip1; protected: private: System::Windows::Forms::ToolStripMenuItem^ testToolStripMenuItem; private: /// <summary> /// Требуется переменная конструктора. /// </summary> System::ComponentModel::Container ^components; #pragma region Windows Form Designer generated code /// <summary> /// Обязательный метод для поддержки конструктора - не изменяйте /// содержимое данного метода при помощи редактора кода. /// </summary> void InitializeComponent(void) { this->menuStrip1 = (gcnew System::Windows::Forms::MenuStrip()); this->testToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem()); this->menuStrip1->SuspendLayout(); this->SuspendLayout(); // // menuStrip1 // this->menuStrip1->Items->AddRange(gcnew cli::array< System::Windows::Forms::ToolStripItem^ >(1) {this->testToolStripMenuItem}); this->menuStrip1->Location = System::Drawing::Point(0, 0); this->menuStrip1->Name = L"menuStrip1"; this->menuStrip1->Size = System::Drawing::Size(292, 24); this->menuStrip1->TabIndex = 0; this->menuStrip1->Text = L"menuStrip1"; // // testToolStripMenuItem // this->testToolStripMenuItem->Name = L"testToolStripMenuItem"; this->testToolStripMenuItem->Size = System::Drawing::Size(40, 20); this->testToolStripMenuItem->Text = L"Test"; this->testToolStripMenuItem->Click += gcnew System::EventHandler(this, &TestForm::testToolStripMenuItem_Click); // // TestForm // this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(292, 266); this->Controls->Add(this->menuStrip1); this->MainMenuStrip = this->menuStrip1; this->Name = L"TestForm"; this->Text = L"TestForm"; this->menuStrip1->ResumeLayout(false); this->menuStrip1->PerformLayout(); this->ResumeLayout(false); this->PerformLayout(); } #pragma endregion private: System::Void testToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { PseudoFun(); }; }; } Модуль TestForm.h содержит определение класса TestForm, содержащего пункт в главном меню, при активировании которого вызывается функция PseudoFun(), определённая в модуле NewBox. Модуль NewBox: namespace CLRWin02 { using namespace System::Windows::Forms; static void vbut_Click(System::Object^ sender, System::EventArgs^ e) { dynamic_cast< System::Windows::Forms::Form^ >(dynamic_cast< System::Windows::Forms::Button^ >(sender)->Parent)->Close(); }; void PseudoFun() { System::Windows::Forms::Form ^vForm2; vForm2 = gcnew System::Windows::Forms::Form(); vForm2->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; vForm2->ClientSize = System::Drawing::Size(400, 200); vForm2->Name = L"NBox"; vForm2->Text = L"Кириллизованный MessageBox"; System::Windows::Forms::Button^ vbut; vbut = (gcnew System::Windows::Forms::Button()); vForm2->Controls->Add(vbut); vbut->Location = System::Drawing::Point(100, 100); vbut->Size = System::Drawing::Size(100, 30); vbut->Text = L"ЖМИ"; vbut->UseVisualStyleBackColor = true; vbut->Click += gcnew System::EventHandler(vbut_Click); vForm2->ShowDialog(); } } Легко заметить, что в случае языка C++, не допускающего определение класса частями в нескольких модулях (как со спецификатором partial в языке C#l), фрагмент приложения, порождаемый средой проектирования автоматически и инициирующий форму, помещается в том же модуле, в котором находится определение класса, в данном случае – в модуле TestForm.h. В проект входят вспомогательные модули: stdafx.cpp: исходный файл, содержащий только стандартные включаемые модули // CLRWin02.pch будет предкомпилированным заголовком // stdafx.obj будет содержать предварительно откомпилированные сведения о типе #include "stdafx.h" stdafx.h: включаемый файл для стандартных системных включаемых файлов // или включаемых файлов для конкретного проекта, которые часто используются, но // не часто изменяются #pragma once TestForm.resx, app.aps, app.ico, CLRWin02.vcproj, а также AssemblyInfo.cpp #include "stdafx.h" using namespace System; using namespace System::Reflection; using namespace System::Runtime::CompilerServices; using namespace System::Runtime::InteropServices; using namespace System::Security::Permissions; // // Общие сведения об этой сборке предоставляются следующим набором // атрибутов. Отредактируйте значения этих атрибутов, чтобы изменить // общие сведения об этой сборке. // [assembly:AssemblyTitleAttribute("CLRWin02")]; [assembly:AssemblyDescriptionAttribute("")]; [assembly:AssemblyConfigurationAttribute("")]; [assembly:AssemblyCompanyAttribute("Microsoft")]; [assembly:AssemblyProductAttribute("CLRWin02")]; [assembly:AssemblyCopyrightAttribute("Copyright (c) Microsoft 2010")]; [assembly:AssemblyTrademarkAttribute("")]; [assembly:AssemblyCultureAttribute("")]; // // Сведения о версии сборки состоят из следующих четырех значений: // // Основной номер версии // Дополнительный номер версии // Номер построения // Редакция // // Можно задать все значения или принять номер построения и номер редакции по умолчанию, // используя "*", как показано ниже: [assembly:AssemblyVersionAttribute("1.0.*")]; [assembly:ComVisible(false)]; [assembly:CLSCompliantAttribute(true)]; [assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)]; Главная форма и вызванное диалоговое окно выглядят на экране следующим образом; 6. ПРОЕКТИРОВАНИЕ КЛАССОВ
6.1. Основы модельно-ориентированного проектирования Откуда берутся объекты в АСУ и бизнес-приложениях? В настоящее время систематический подход к объектно-ориентированному проектированию представлен, главным образом, так называемым, MDA (model-driven architecture), или МОП (модельно-ориентированным проектированием). Основоположник – Rational Rose, подхватившей более 10 лет назад наработки OMG. Идея MDA: проектирование включает в себя создание имитационной модели реальной системы ведётся на основе этой модели, и в неё же встраивается проектируемое компьютерное приложение для замены или развития её определённых функций. Модельно-ориентированное и, соответственно, объектно-ориентированное проектирование реализуются в конкретных организационных формах в рамках определённых концепций жизненного цикла программ. Относительно независимым от этих форм и концепций является перечень “дисциплин” – видов деятельности, составляющих процесс создания программы (специальные виды технологических процессов): бизнес-моделирование (business modeling), работа с требованиями (анализ требований requirements), проектирование (design), реализация, тестирование, развёртывание, конфигурирование, управление изменениями, управление проектом, связь с окружением. Дисциплины характеризуются своими результатами – “артефактами”. Основным артефактом бизнес-моделирования является имитационная модель реальной системы. Артефакты анализа требований: виденье (как бы ТЗ), описание (модель) “прецедентов”, словарь, спецификация требований, функциональных и прочих. Артефакты анализа требований составляются на естественном языке, но, начиная с некоторого момента, желательно использование графики. Основным является использование нотации UML. Графика и текст дополняют друг друга. Модель реальной системы отображает основные объекты моделируемой системы, их свойства и отношения. Для этой модели характерно отражение статических аспектов системы. Динамический аспект системы отражается, главным образом, в описании прецедентов и функциональных требований. Прецедент (use case). Основной прецедент и другие. Важным элементом бизнес-моделирования и анализа требований является определение границы системы, в модель должна определять внутренние объекты и внешние (по отношению к этой границе), с которыми они взаимодействуют. Этап проектирования начинается принятием решения о месте создаваемой компьютерной системы в составе реальной системы. В общем случае одни объекты реальной системы ликвидируются без следа, другие преобразуются, входя внутрь компьютерной системы, третьи изменяют свои связи и свойства. В результате принятия этого решения должны быть переработаны вышеуказанные артефакты. Последующие решения связаны с уточнением состава объектов уже внутри компьютерной системы, детализацией свойств и операций (“методов”), выбором компьютерной платформы, операционной среды и языка (системы) программирования для реализации. Все эти мероприятия создают необходимые предпосылки для перехода от проектирования к реализации. UML предоставляет графические средства для составления “диаграмм”: диаграммы прецедентов, диаграммы классов, диаграмм взаимодействия, а именно: диаграммы последовательностей (sequence diagram) и диаграммы кооперации (collaboration diagram), в частности, системной диаграммы последовательностей. UML доступен в разных системах, в частности, в Microsoft Visio, Rational Rose, Delphi, начиная с издания 8. 6.2. Проект разработки компьютерной поддержки библиотеки Далее в качестве примера рассматривается разработка компьютерной поддержки библиотеки. 6.2.1. Начальная фаза проектирования В соответствии с рекомендациями Унифицированного процесса проектирования (UP) [1] во время начальной фазы проектирования в качестве рабочих материалов («артефактов») разрабатываются диаграмма концептуальных классов и диаграмма прецедентов, приведённые ниже. Диаграмма концептуальных классов отражает условия разработки, состоящие в том, что библиотека не оснащена компьютерной поддержкой, но имеется каталог элементов хранения (ЭХ) и реестр читателей, и то и другое на карточках. Масштаб библиотеки невелик: она обходится одним библиотекарем. На диаграмме концептуальных классов отражены только операции по выдаче и возврату публикаций, операции, связанные с прецедентами “Регистрация читателя” и “Комплектация библиотеки”, не показаны, чтобы избежать излишней густоты связей. Читатель может указать эти связи на отдельных экземплярах диаграммы концептуальных классов в качестве упражнения. Наименование ролей и ассоциаций даны в таком объёме, чтобы избегать повторений и переполнения пространства диаграммы. Далее один из прецедентов представлен в табличной форме. Описание прецедента отражают фактический порядок осуществления заказов на получение публикации в библиотеке. В частности, пользователь имеет самостоятельный доступ к каталогу для поиска публикаций. Каталог не содержит информации о выдаче публикаций, и читатель может её получить только в ходе заказа. Диаграмма концептуальных классов проекта поддержки библиотеки Диаграмма прецедентов проекта поддержки библиотеки Прецедент из проекта поддержки библиотеки Имя прецедента Выполнение заказа Исполнители Читатель и библиотекарь Описание Осн
. Читатель передает библиотекарю информацию, идентифицирующую себя и публикацию, которую читатель хотел бы получить. Библиотекарь убеждается по реестру, что читатель зарегистрирован в библиотеке и экземпляр требуемой публикации может быть выдан. Библиотекарь передает экземпляр требуемой публикации и фиксирует факт выдачи. A1.
Читатель не зарегистрирован в библиотеке. Заказ отклоняется. A2.
Читатель зарегистрирован, но заказанная публикация выдана. Заказ отклоняется Предусловия Читатель реализовал прецедент «Справка о наличии» и узнал, что искомая публикация входит в библиотечный фонд. Постусловия Если читатель получил заказанную публикацию, то в карточке читателя отмечается факт выдачи. В данном случае, Осн. – основная последовательность действий (основной сценарий). A1-A2. Альтернативные сценарии. УПРАЖНЕНИЯ. 1. Описать остальные прецеденты проекта 6.2.2.Первые проектные решения В рамках следующей фазы проектирования принимается решение о границах проектируемой компьютерной поддержки и её месте в библиотеке. Оно состоит в том, что картотечные каталог и реестр заменяются компьютерной системой, которая обеспечивает ведение двух баз данных – по фонду публикаций и контингенту читателей. Читатель имеет прямой доступ к фонду публикаций для поиска и чтения. Все функции оперативного изменения содержимого баз данных осуществляет библиотекарь. Это решение фиксируется в уточненных описаниях прецедентов (в табличной и графической формах) и диаграммах проектных классов и системных взаимодействий. Далее представлены первичная версия диаграммы проектных классов, диаграммы системных последовательностей основного сценария прецедента «Выполнение заказа» и соответствующей диаграммы кооперации. Эти диаграммы представляют интерфейс между пользователем – библиотекарем и проектируемой компьютерной системой и оставляют открытыми многие детали этого интерфейса равно, как и вопросы его реализации внутри системы. Первичная диаграмма проектных классов Диаграмма системных последовательностей для прецедента «Выполнение заказа» Диаграмма кооперации (системная) для сценария «Выполнение заказа» 6.2.3. Ключевые элементы реализации В ходе реализации принятых решений складывается вполне типовая ситуация, когда информационная система (ИС) должна обеспечивать учёт действий пользователя с некоторым множеством (фондом) однотипных объектов (элементов фонда), например, менеджера с проектами, в рассматриваемом случае - библиотекаря с экземплярами печатных изданий и читателями. Обобщённая концептуальная диаграмма классов в этой ситуации показана на Рис.1.
Рисунок 1. Концептуальная диаграмма классов для типового фрагмента проекта Единственный атрибут элемента – состояние – обобщает всевозможные состояния моделируемого объекта, например, книга может быть выдана, проект – приостановлен, читатель - зарегистрирован. Ассоциация “действует” обобщает всевозможные действия пользователя, изменяющие состояния объекта, а также “Включение объекта в фонд” и “Изъятие объекта из фонда”. Диаграмма последовательностей для системных операций представлена на Рис.2, а соответствующая диаграмма кооперации – на Рис.3. Рисунок 2. Диаграмма последовательностей системных операций для типового фрагмента проекта Рисунок 3. Диаграмма кооперации системных операций для типового фрагмента проекта Диаграмма классов проекта строится на основе концептуальной диаграммы классов и диаграмм взаимодействия. При этом определяется набор классов, которые исполняют системные операции, показанные в диаграммах взаимодействия, и служат прототипами классов исходного языка программирования, на котором записывается проект. Первичными кандидатами в классы проекта являются классы концептуальной диаграммы. Простейшее и вполне удовлетворительное решение этой задачи состоит в назначении всех системных операций классу “Фонд” и реализации их в виде методов соответствующего класса в программе. Выбор в языке программирования способа представления “Элементов”, входящих в “Фонд” определяется следующими требованиями, вытекающими из определения системных операций: · в “Фонд” может входить любое (в частности, пустое) конечное множество однотипных “Элементов”, · необходимы элементарные операции для добавления, удаления и выборки “Элемента” по некоторому признаку. В языке C# этим требованиям удовлетворяют классы List(T) и ArrayList. Функциональность этих классов можно передать классу “Фонд”, реализовав его как их наследника или введя в “Фонд” атрибут типа ArrayList или List(T). Для реализации концептуального класса “Элемент” можно выбрать типы Struct или Class. На Рис.4 показана диаграмма классов для первого варианта проекта (Варианта А), в котором “Фонд” реализуется как наследник класса List(T). Рисунок 4. Диаграмма классов для первого варианта фрагмента проекта Соответствующие диаграмма последовательностей и диаграмма кооперации показаны на Рис.5 и 6. Рисунок 5. Диаграмма последовательностей для первого варианта типового фрагмента проекта Рисунок 6. Диаграмма кооперации для первого варианта типового фрагмента проекта В этом варианте проекта установлено, что элементы фона идентифицируются строкой и сам пользователь заботится об уникальности элементов. Хотя на диаграммах показаны только те операции типа List(T), которые необходимы для реализации выбранных системных операций, и не показаны базовые операции, необходимые для реализации пользовательского интерфейса, класс “Фонд” оказался функционально перегружен. Поэтому предложен второй вариант (Вариант Б), в котором введен класс “Список элементов”, и “Фонд” связан с экземпляром этого класса как с элементом своей конструкции. Кроме того, введен класс “Фасад”, который реализует связь с пользователем на базе формы Соответствующие диаграмма классов и диаграмма последовательностей показаны на Рис.7 и 8. Рисунок 7. Диаграмма классов для второго варианта проекта Рисунок 8.Диаграмма последовательностей для второго варианта проекта (операции «Включить» и «Показать»). Рисунок 9.Диаграмма последовательностей для второго варианта проекта (операции “Ввести” и “Исключить”). 6.2.4. Программная реализация на языке C++ Приведённые выше диаграммы могут быть конкретизированы на языке C++ в среде Microsoft Visual Studio в виде версии CLR следующим образом (проект CppLab02). Класс Fasade, отвечающий за взаимодействие с оператором, определён в модуле (файле) Fasade.h – наследнике класса Form. Экземпляр класса Fasade как главная форма приложения обеспечивает визуальный интерфейс с оператором, когда приложение запущено. Далее показан вид этой формы. Пункты главного меню представляют собой операции, которые пользователь активирует, когда работает с фондом. «Включить» создает элемент фонда со свойствами, значения которых пользователь должен перед операцией установить в окнах «Идентификатор» и «Состояние». «Показать» обеспечивает поиск элемента фонда по значению, установленному в окне «Идентификатор», и вывод в свойства State этого элемента в окно «Состояние» на форме. «Ввести» ищет элемент по «Идентификатору» и присваивает свойству State найденного элемента значение из окна «Состояние». «Исключить» находит элемент фонда по значению, установленному в окне «Идентификатор», и удаляет его из фонда. Если поиск в этих операциях не приводит к успеху, то в окне «Состояние» устанавливается значение -3. Значение окна «Идентификатор» должно быть десятичным числом. Операции реализуются обработчиками событий Click соответствующих пунктов меню. Их тексты нетрудно найти в листинге файла Fasade.h, приведённом далее. #pragma once #include "Fond.h" namespace CppLab02 { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Collections::Generic; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; /// <summary> /// Сводка для Fasade /// /// Внимание! При изменении имени этого класса необходимо также изменить /// свойство имени файла ресурсов ("Resource File Name") для средства компиляции управляемого ресурса, /// связанного со всеми файлами с расширением .resx, от которых зависит данный класс. В противном случае, /// конструкторы не смогут правильно работать с локализованными /// ресурсами, сопоставленными данной форме. /// </summary> public ref class Fasade : public System::Windows::Forms::Form { private: CppLab02::_Fond^ vFond; public: Fasade(void) { InitializeComponent(); // //TODO: добавьте код конструктора // vFond = gcnew _Fond(); } protected: /// <summary> /// Освободить все используемые ресурсы. /// </summary> ~Fasade() { if (components) { delete components; } } private: System::Windows::Forms::MenuStrip^ menuStrip1; protected: private: System::Windows::Forms::ToolStripMenuItem^ включитьToolStripMenuItem; private: System::Windows::Forms::ToolStripMenuItem^ показатьToolStripMenuItem; private: System::Windows::Forms::ToolStripMenuItem^ ввестиToolStripMenuItem; private: System::Windows::Forms::ToolStripMenuItem^ исключитьToolStripMenuItem; private: System::Windows::Forms::TextBox^ id; private: System::Windows::Forms::TextBox^ State; private: System::Windows::Forms::Label^ label1; private: System::Windows::Forms::Label^ label2; private: /// <summary> /// Требуется переменная конструктора. /// </summary> System::ComponentModel::Container ^components; #pragma region Windows Form Designer generated code /// <summary> /// Обязательный метод для поддержки конструктора - не изменяйте /// содержимое данного метода при помощи редактора кода. /// </summary> void InitializeComponent(void) { this->menuStrip1 = (gcnew System::Windows::Forms::MenuStrip()); this->включитьToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem()); this->показатьToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem()); this->ввестиToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem()); this->исключитьToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem()); this->id = (gcnew System::Windows::Forms::TextBox()); this->State = (gcnew System::Windows::Forms::TextBox()); this->label1 = (gcnew System::Windows::Forms::Label()); this->label2 = (gcnew System::Windows::Forms::Label()); this->menuStrip1->SuspendLayout(); this->SuspendLayout(); // // menuStrip1 // this->menuStrip1->Items->AddRange(gcnew cli::array< System::Windows::Forms::ToolStripItem^ >(4) {this->включитьToolStripMenuItem, this->показатьToolStripMenuItem, this->ввестиToolStripMenuItem, this->исключитьToolStripMenuItem}); this->menuStrip1->Location = System::Drawing::Point(0, 0); this->menuStrip1->Name = L"menuStrip1"; this->menuStrip1->Size = System::Drawing::Size(292, 24); this->menuStrip1->TabIndex = 0; this->menuStrip1->Text = L"menuStrip1"; // // включитьToolStripMenuItem // this->включитьToolStripMenuItem->Name = L"включитьToolStripMenuItem"; this->включитьToolStripMenuItem->Size = System::Drawing::Size(70, 20); this->включитьToolStripMenuItem->Text = L"Включить"; this->включитьToolStripMenuItem->Click += gcnew System::EventHandler(this, &Fasade::включитьToolStripMenuItem_Click); // // показатьToolStripMenuItem // this->показатьToolStripMenuItem->Name = L"показатьToolStripMenuItem"; this->показатьToolStripMenuItem->Size = System::Drawing::Size(67, 20); this->показатьToolStripMenuItem->Text = L"Показать"; this->показатьToolStripMenuItem->Click += gcnew System::EventHandler(this, &Fasade::показатьToolStripMenuItem_Click); // // ввестиToolStripMenuItem // this->ввестиToolStripMenuItem->Name = L"ввестиToolStripMenuItem"; this->ввестиToolStripMenuItem->Size = System::Drawing::Size(54, 20); this->ввестиToolStripMenuItem->Text = L"Ввести"; this->ввестиToolStripMenuItem->Click += gcnew System::EventHandler(this, &Fasade::ввестиToolStripMenuItem_Click); // // исключитьToolStripMenuItem // this->исключитьToolStripMenuItem->Name = L"исключитьToolStripMenuItem"; this->исключитьToolStripMenuItem->Size = System::Drawing::Size(76, 20); this->исключитьToolStripMenuItem->Text = L"Исключить"; this->исключитьToolStripMenuItem->Click += gcnew System::EventHandler(this, &Fasade::исключитьToolStripMenuItem_Click); // // id // this->id->Location = System::Drawing::Point(38, 82); this->id->Name = L"id"; this->id->Size = System::Drawing::Size(100, 20); this->id->TabIndex = 1; // // State // this->State->Location = System::Drawing::Point(40, 135); this->State->Name = L"State"; this->State->Size = System::Drawing::Size(100, 20); this->State->TabIndex = 2; // // label1 // this->label1->AutoSize = true; this->label1->Location = System::Drawing::Point(35, 66); this->label1->Name = L"label1"; this->label1->Size = System::Drawing::Size(87, 13); this->label1->TabIndex = 3; this->label1->Text = L"Идентификатор"; // // label2 // this->label2->AutoSize = true; this->label2->Location = System::Drawing::Point(37, 119); this->label2->Name = L"label2"; this->label2->Size = System::Drawing::Size(61, 13); this->label2->TabIndex = 4; this->label2->Text = L"Состояние"; // // Fasade // this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(292, 266); this->Controls->Add(this->label2); this->Controls->Add(this->label1); this->Controls->Add(this->State); this->Controls->Add(this->id); this->Controls->Add(this->menuStrip1); this->MainMenuStrip = this->menuStrip1; this->Name = L"Fasade"; this->Text = L"Работа с фондом"; this->menuStrip1->ResumeLayout(false); this->menuStrip1->PerformLayout(); this->ResumeLayout(false); this->PerformLayout(); } #pragma endregion private: System::Void включитьToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { vFond->Include(this->State, this->id->Text); }; private: System::Void показатьToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { vFond->Show(this->State, this->id->Text); }; private: System::Void ввестиToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { vFond->Write(this->State, this->id->Text); }; private: System::Void исключитьToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { vFond->Remove(this->State, this->id->Text); }; }; } В этих обработчиках вызываются соответствующие методы класса _Fond в соответствии с диаграммой на рис.8 и 9. Класс _Fond определён в модуле (файле) Fond.h, листинг которого приведён далее. #pragma once #include "Fasade.h" namespace CppLab02 { using namespace System; using namespace System::ComponentModel; using namespace System::Collections::Generic; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing;
|