Главная      Учебники - Разные     Лекции (разные) - часть 15

 

Поиск            

 

Спецификация программы uWert > Текст программы uWert Доказательство корректности программы uWert Организация многооконного интерфейса и диалога в приложениях

 

             

Спецификация программы uWert > Текст программы uWert Доказательство корректности программы uWert Организация многооконного интерфейса и диалога в приложениях

Элементы технологии программирования

в примерах и упражнениях

Уче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. ОРГАНИЗАЦИЯ ТИПОВЫХ ОКОННЫХ ПРИЛОЖЕНИЙ

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) активируйте в главном меню FileNewApplication

При создании нового проекта этого типа 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(" Привет " );

}

}

}

Обработчик button_Click формируется автоматически, кроме подчёркнутой строки

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 реализован случай А.

Тогда переменные fdig и ldig указывают начало и, соответственно, конец нумерала m, завершающего Z и, следовательно, строки Y+Z. Переменная lb будет равна lbDig. Стек имеет rk ячеек, и последняя из них содержит n+F(Y)+F(Z\m) = n+F(Y+Z\m). То есть, при обработке Y+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;