OOP в Visual FoxPro® 6.0 Дроздов Михаил Компания «ИВС Софт»

advertisement
OOP в Visual FoxPro® 6.0
(основные понятия
и инструментальные средства среды разработки)
Дроздов Михаил
Компания «ИВС Софт»
My Page: http://vfpdmur.narod.ru/
ICS Page: http://ics.perm.ru/
mailto:Drozdov@ics.perm.su
Определение класса: свойства, методы и события 1(3)
• Объект: совокупность данных и функций реализуется в
виде класса.
• В классе определяются: свойства, методы, и события.
– событие - реакция на внешнее воздействие, предопределённая
классом (нельзя создавать новые). Код, помещённый в событие
выполняется при возникновении соответствующего события.
Ряд, событие может быть возбуждено программно.
– свойство - это VFP переменная (массив), определённая в
классе.
– метод - процедура-функция, определённая в классе.
• Свойства, и методы могут быть добавлены/удалены из
класса, однако нет возможности удаления свойств и
методов, «предопределённых классом» и
«унаследованных».
Определение класса: область видимости свойств и методов 2(3)
• Область видимость свойств и методов задаётся как
Public, Protected или Hidden при их определении в
классе:
– Public - доступно извне, внутри и в производных данного класса.
– Protected - доступно внутри и в производных данного класса.
– Hidden - только внутри данного класса.
• Класс существует в виде определения (DEFINE CLASS
… AS ...) в коде prg-файла или в библиотеке классов
(vcx-файле), структура последнего совпадает с dbfфайлом:
– *.vcx - аналог dbf
– *.vct - аналог fpt
• Доступные извне (public) свойства, методы и события,
определяют внешний интерфейс класса.
• Определения не могут быть вложенными, т.е. Не может быть
определён класс внутри класса или метод внутри метода.
Определение класса:
понятия инкапсуляции, наследования, полиморфизма 3(3)
VFP классы обладают указанными в заголовке свойствами, каждое
из которых означает следующее:
• Инкапсуляция (encapsulation) - сокрытие части свойств и методов,
для внешнего использования.
• Наследование (inheritance) - на основе одного класса может быть
создан другой, содержащий в себе все «открытые для него» методы
и свойства первого.
• Полиморфизм (polymorphism) - грубо говоря, возможность
существования в разных классах одинаково названных методов, но
выполняющих различные функции. Данное свойство не
поддерживается в VFP напрямую, поскольку не существует в VFP
понятия перегружаемой (virtual) функции, однако благодаря
наличию команды NODEFAULT и функции DODEFAULT() вполне
возможно смоделировать это свойство. (следует также отметить, что
указанная возможность будет «работать» только в VFP классах и не
будет в ActiveX компонентах)
Контроль доступа к свойствам класса:
методы Access и Assigne
•
•
•
В VFP включены два определяемые пользователем метода,
обеспечивающих контроль типа чтения-запись любого свойства класса
(«стражи» свойства).
Access - метод, вызываемый при попытки использования свойства,
например, сохранение значения свойства некоторой переменной.
Assigne - метод, вызываемый при попытки изменения свойства, например,
при попытке присвоение значению свойства нового значения. Очевидно,
что этот метод не будет никогда вызываться для свойства Read only (только
для чтения)
Могут быть созданы как в режиме проектирования, так и непосредственно
из кода.
PROCEDURE MyPropName_ASSIGN
LPARAMETERS NewValueForMyProp
…
ENDPROC
•
•
Поскольку метод Assigne получают новое значение свойства через
параметр, то логика, заключённая в код этого метода определяет, в конечном
счёте: будет ли присвоено новое значение этому свойству класса
Даёт возможность писать реализацию, для полностью открытого
интерфейса, тем не менее защищая значения свойств.
Использование класса: создание объекта 1(1)
•
•
•
Ссылку нужно где-то хранить: На основании определения (описания) класса
создаётся экземпляр класса, или объект класса. Ссылка на полученный
экземпляр класса обычно сохраняется в некоторую область памяти: это
может быть глобальная/локальная переменная или свойство некоторого
другого объекта (созданная либо явно Вами или неявно в режиме Designe).
Экземпляр класса (или объект) может быть создан явно в режиме
выполнения (runtime) или неявно при проектировании (designe)
При явном создать экземпляр класса с использованием функции
CreateObject() или метода AddObject(), так или иначе, необходимы два
действия [Visible = .F.]:
– обеспечить «видимость» соответствующей библиотеки класса SET CLASS LIB
TO...
– выполнить функцию oMyObj = CreateObject(“MyClassLib.MyClassInLib”[, ...]) или
This.AddObject(“oMyObj”, “MyClassLib.MyClassInLib”[,...])
•
•
При явном создать экземпляр класса с использованием функции NewObject()
первое действие излишне, поскольку библиотека явно указывается в
параметрах этой функции [Visible = .F.].
При создании объекта в режиме проектирования библиотека будет
определена неявно, а свойство Visible Вы можете настроить в окне Properties
Использование класса: уничтожение объекта 2(3)
• Ссылку нужно где-то хранить: На основании определения (описания)
класса создаётся экземпляр класса, или объект класса. Ссылка на
полученный экземпляр класса обычно сохраняется в некоторую
область памяти: это может быть глобальная/локальная переменная или
свойство некоторого другого объекта (созданная либо явно Вами или
неявно в режиме Designe).
• Следите за уничтожением Ваших объектов:
– Уничтожение переменной, содержащей ссылку на экземпляр класса,
приводит к разрушению соответствующего экземпляра объекта. Для
локальной переменной это будет происходить при «закрытии» её «области
видимости».
– Однако, если такая ссылка храниться как свойство некоторого другого
объекта, то последний не может быть уничтожен до тех пор, пока не
будут разрушены все его свойства-ссылки-на-экземпляры-объектов
(Например, This.oMyObj = NULL обычно в событии Destroy этого
класса).
– Обычно не требуется никаких действий с Вашей стороны, для
уничтожения экземпляров объектов, добавленных в экземпляр класса в
Designe или RunTime с помощью This.AddObject(...)
Использование класса: доступ к объектам 3(3)
• Внутри объекта: Адресация осуществляется с использованием таких
ключевых слов как This, ThisForm, ThisFormSet, а также Parent. В
режиме Designe, для правильной адресации на объект лучше
использовать Object List... (пункт меню, возникающего по нажатию
правой клавиши мыши) Например,
WITH This.Parent.Parent
.ActiveCell(1, .ActiveColumn)
ENDWITH
• Извне объекта: Для адресации к свойствам/методам/событиям извне
объекта, так или иначе, необходимо знать место хранения объектной
ссылки на этот объект.
CLEAR goMayObj
PUBLIC goMayObj
goMayObj = CreateObject(…)
…
goMayObj.MyMethod()
LOCAL lnCntForn
ACTIVATE SCREEN
CLEAR
FOR lnCntForn = 1 TO _SCREEN.FormCount
? _SCREEN.Forms(lnCntForn).Name
ENDFOR
Базовые классы VFP и некоторые особенности обработки событий
•
Control (исключая Custom)
– не имеет метода AddObject()
– все добавляемые свойства
защищённые
•
Container:
– все добавляемые объекты «открыты»
•
•
•
События Container происходят
независимо от событий Control Это
правило распространяется и на
элементы Grid, т.е. каждый элемент
«работает» независимо... И наоборот, в
групповых элементах по событию на
элементе отрабатывает событие
группового элемента, но только в том
случае, если нет кода в событии
элемента.
При наследовании объектов контроль:
какой код надо выполнить идёт от
производных к родительским.
Лучший способ узнать, что
происходит - «Журнал событий»
Условные подгруппы (или категории) классов
Не визуальных:
• Класс-оболочка (покрытия или интерфейсные «преобразователи»)
• Класс-менеджер (управление другими классами «надзиратели»)
• Бизнес-класс (логика обработки данных или моделирование
предметной области)
Визуальные:
• Одиночные элементы управления
• Групповые элементы управления
• Контейнеры
• Экранные формы
• Панели инструментов
Object
Events
Data environment
Form set
Form
Data environment cursor(s)
Data environment
Objects 1
Form
Form set
Form set
Form
Object1 2
Form
Object1
Object1
Object1
Object1
Object2 3
Object2
Object2
Object2
Object2
Form
Form
Object 5
Form
Form set
Data environment
Data environment
Data environment cursor(s)
BeforeOpenTables
Load
Load
Init
Init
Init
Init
Init
Activate
Activate
When
GotFocus
GotFocus
Message
Valid 3
LostFocus
When
GotFocus
Message
Valid 4
LostFocus
QueryUnload
Destroy
Destroy
Unload
Unload
AfterCloseTables
Destroy
Destroy
1 Для каждого объекта от самого внутреннего до самого внешнего
2 Первый объект в tab-последовательности
3 Следующий объект получает фокус
4 Поскольку объект теряет фокус
5 Для каждого объекта от самого внешнего к самому внутреннему
Некоторые выводы из анализа последовательности создания элементов
• Элемент, включённый в Control нельзя «расширить» (это можно
сделать только за счёт родительского класса этого элемента).
• Обращение к контейнеру из метода Init() включённого в него объекта
недопустимо (поскольку контейнер ещё не создан).
• И наоборот, всю «настройку» включённых в контейнер компонент,
следует производить в событии Init() контейнера (когда все
включённые в него элементы уже существуют).
• Из методов/событий формы нет возможности управлять открытием
таблиц в DataEnvironment.
• Все установки для данных (SET DELETED ON, ...) в Private
DataSession следует производить в событии
MyForm.DataEnvironment.BeforOpenTables() (когда данные ещё не
открывались)
• Какую-либо настройку данных, с которыми работают элементы,
включённые в форму, следует выполнить в событии Load() формы
(пока эти объекты ещё не создавались).
• Наличие класса Container позволяет применить модульный подход при
программировании с использованием классов, - так называемое
агрегирование
Именование переменных 1(2)
Область видимости:
Типы переменных:
Типы полей:
Scope
l
p
g
t
Description
Local
Private (default)
Public (global)
Parameter
Example
lnCounter
pnStatus
gnOldRecno
tnRecNo
Type
a
c
y
d
t
b
f
l
n
o
u
Description
Array
Character
Currency
Date
Datetime
Double
Float
Logical
Numeric
Object
Unknown
Example
aMonths
cLastName
yCurrentValue
dBirthDay
tLastModified
bValue
fInterest
lFlag
nCounter
oEmployee
uReturnValue
Type
c
d
t
b
f
g
l
m
y
n
I
Description
Character
Date
Datetime
Double
Float
General
Logical
Memo
Currency
Numeric
Integer
Example
Customer.cLastName
Customer.dBirthDay
Customer.tLastMod
Customer.bRate
Customer.fValue
Customer.gPicture
Customer.lSellMail
Customer.mComments
Customer.yYearTDate
Customer.nItems
Customer.iCustID
Именование переменных 1(2)
Элементы/Классы:
Prefix
acd
chk
cbo
cmd
cmg
cnt
ctl
<user-defined>
edt
frm
frs
grd
grc
grh
hpl
img
lbl
lin
lst
olb
ole
opt
opg
pag
pgf
prj
sep
shp
spn
txt
tmr
tbr
Object
ActiveDoc
CheckBox
ComboBox
CommandButton
CommandGroup
Container
Control
Custom
EditBox
Form
FormSet
Grid
Column
Header
HyperLink
Image
Label
Line
ListBox
OLEBoundControl
OLE
OptionButton
OptionGroup
Page
PageFrame
ProjectHook
Separator
Shape
Spinner
TextBox
Timer
ToolBar
Example
acdHomePage
chkReadOnly
cboEnglish
cmdCancel
cmgChoices
cntMoverList
ctlFileList
user-defined
edtTextArea
frmFileOpen
frsDataEntry
grdPrices
grcCurrentPrice
grhTotalInventory
hplHomeURL
imgIcon
lblHelpMessage
linVertical
lstPolicyCodes
olbObject1
oleObject1
optFrench
opgType
pagDataUpdate
pgfLeft
prjBuildAll
sepToolSection1
shpCircle
spnValues
txtGetText
tmrAlarm
tbrEditReport
Доступ к классам
Из проекта приложения: … или пункт меню Tools\Class Browser[Component Gallery]
Манипулирование классами из Project Manager
Добавить библиотеку
в проект
Добавить новый класс
как производный
от указанного:
Название производного
Выбор библиотеки
и название базового класса
В какую библиотеку записываем
«Перетащив и отпустив»:
Вы можете скопировать класс
из одной библиотеки в другую,
как в рамках одного проекта ,
так и между проектами
Предупреждение:
Все библиотеки, используемых классов,
должны быть помещены в проект
при неявном использовании классов.
Class Browser
+CTRL
Копирование
Перемещение
класса
Типы файлов:
Фильтр
названия
класса
DO (_BROWSER) WITH […]
Class Browser: панель команд
•
•
•
•
•
•
•
•
Class Browser <-> Component Gallery
Открыть новый файл
Добавить новый файл
Просмотр исходного кода класса
Найти класс
Создать новый класс
Переименовать класс
У активного класса
– переопределить родительский класс на выбранный
• Уничтожить записи, помеченные как удалённые
Команды работы с классами в библиотеках классов
• Создание библиотеки: CREATE CLASSLIB ClassLibraryName
• Создание класса: CREATE CLASS ClassName | ? [OF
ClassLibraryName1 | ?] [AS cBaseClassName [FROM
ClassLibraryName2]] [NOWAIT]
• Добавление/копирование: ADD CLASS ClassName [OF
ClassLibraryName1] TO ClassLibraryName2 [OVERWRITE]
• Удаление класса: REMOVE CLASS ClassName OF ClassLibraryName
• Переименование класса: RENAME CLASS ClassName1 OF
ClassLibraryName TO ClassName2
Для перемещения следует последовательно выполнить
сначала копирование, затем удаление
DEFINE CLASS ClassName1 AS ParentClass [OLEPUBLIC]
[[PROTECTED | HIDDEN PropertyName1, PropertyName2 ...]
[Object.]PropertyName = eExpression ...]
[ADD OBJECT [PROTECTED] ObjectName AS ClassName2 [NOINIT]
[WITH cPropertylist]]...
[[PROTECTED | HIDDEN] FUNCTION | PROCEDURE Name[_ACCESS | _ASSIGN]
| THIS_ACCESS [NODEFAULT]
cStatements
[ENDFUNC | ENDPROC]]...
ENDDEFINE
Предупреждения:
•
•
•
•
•
•
Класс, помещённый в проект компилируется целиком и отсутствует возможность
отключить (Exclude) от компилирования некоторого подмножества классов такой
библиотеки. Т.е., если библиотека содержит классы, не используемые непосредственно в
данном приложении, они всё равно помещаются в код исполняемого файла. Поэтому
следует тщательно планировать состав библиотек. При использовании библиотек, не
помещённых в проект, необходимо их присутствие на диске во время выполнения.
Нужно очень сильно подумать прежде чем решиться на переименование и тем более
удаление классов из библиотек. При переименовании Class Browser учтёт это для всех
производных классов, открытых в данный момент в нём, но есть ли гарантия, что во
время переименования точно все библиотеки, использующие пер именуемый класс
открыты? Заведите себе за правило: всегда создавать дополнительную резервную копию
перед попыткой выполнения переименования и/или удаления класса. Если всё-таки горе
случилось, вспомните, что библиотека храниться в виде обычного dbf-файла.
Не допускайте перемещения библиотек и используемых в них ресурсов из одних
каталогов в другие, помните о межбиблиотечных связях, и используемых внешних
элементов (ресурсов): иконок, include-файлов, и т.д, которые станут «без определённого
места жительства».
Следует исключит какое-либо копирование классов из одной библиотеки в другую.
Поскольку, в конечном счёте, это приведёт к потере контроля над используемыми
классами. Используйте технику создания производных классов, особенно классов,
входящих в состав FFC, никогда не изменяйте их непосредственно. Помните, что
Microsoft регулярно обновляет эту библиотеку, выпуская её новы версии.
Регулярно выполняйте резервное копирование Ваши библиотек.
Выделите должность администратора библиотек, который бы отвечал за их рабочее
состояние.
Галерея компонент
Перемещение
компонента
реакция зависит
от типа компонента
•добавляет компонент
•добавляет библиотеку
•открывает на ред.
DO (_GALLERY) - из кода
Галерея компонент
Галерея компонент
Галерея компонент
Редактор классов (Class Designer)
Редактор классов (Class Designer)
Редактор классов (Class Designer)
Список команд и функций, связанных с ООП
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
@ ... CLASS Command
_WIZARD System Memory Variable
ACLASS( ) Function
ADATABASES( ) Function
ADBOBJECTS( ) Function
ADD CLASS Command
AddProperty Method
AGETCLASS( ) Function
AINSTANCE( ) Function
AMEMBERS( ) Function
AMOUSEOBJ( ) Function
ASELOBJ( ) Function
AVCXCLASSES( ) Function
_BUILDER System Memory Variable
COMCLASSINFO( ) Function
COMPOBJ( ) Function
CREATE CLASS Command
CREATE CLASSLIB Command
CREATEOBJECT( ) Function
DEFINE CLASS Command
DISPLAY OBJECTS Command
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
GETOBJECT( ) Function
GETHOST( ) Function
ISHOSTED( ) Function
LIST OBJECTS Command
LOADPICTURE( ) Function
MODIFY CLASS Command
NEWOBJECT( ) Function
NewObject Method
RELEASE CLASSLIB Command
REMOVE CLASS Command
RENAME CLASS Command
SAVEPICTURE( ) Function
_SCREEN System Memory Variable
SET CLASSLIB Command
SET OLEOBJECT Command
WITH ... ENDWITH Command
Download