Классы на Ls

VladSh

начинающий
Lotus team
11.12.2009
1 276
6
#1
Хоть оно понемножку и отмирает, но... предлагаю делиться в этой теме фишками, областью применения классов на LS.

Начну) Не факт, что скажу что-то новое, но вдруг кому-то окажется полезным.

<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">1. Наследование "ветвлением" и убогое использование через Variant.</div></div><div class="sp-body"><div class="sp-content">
Код:
Class ClassBase

Sub New(ND As NotesDocument)
End Sub

End Class


Class ClassUser As ClassBase

Sub New(ND As NotesDocument)
End Sub

Function MyFunction1()
MessageBox TypeName(Me) & "." & LSI_Info(2)
End Function

Sub UserSub()
MessageBox TypeName(Me) & "." & LSI_Info(2)
End Sub

End Class


Class ClassServer As ClassBase

Sub New(ND As NotesDocument)
End Sub

Function MyFunction1()
MessageBox TypeName(Me) & "." & LSI_Info(2)
End Function

Sub ServerSub()
MessageBox TypeName(Me) & "." & LSI_Info(2)
End Sub

End Class
Функция-Инициализатор:
Код:
Function InitClassObject(ND As NotesDocument) As ClassBase
Dim CB As ClassBase
Dim NS As New NotesSession

'Функция, собственно, и сделана для того, чтобы зашить эти ПРОВЕРКИ (их может быть море) внутрь,
'а также чтобы программист не думал где и как будет выполняться код, а просто обращался к методам и всё;
'использовать с помощью данного разделения лишь тот функционал, который нужен в данный момент.
If NS.IsOnServer Then
Set CB = New ClassServer(ND)		'Присваивание вполне нормально проходит...
Else
Set CB = New ClassUser(ND)			'Присваивание вполне нормально проходит...
End If

If Not CB Is Nothing Then		'Для уникумов, которе привыкли пихать в конструктор код, могущий привести к НЕинициализации объекта
Set InitClassObject = CB
End If
End Function
Ну и вызов с кнопки:
Код:
Sub Click(Source As Button)
Dim ND As NotesDocument
Dim CB As Variant
'...
Set CB = InitClassObject(ND)
If Not CB Is Nothing Then
'Variant всё схавает...:
Call CB.MyFunction1()	
'Call CB.UserSub()				'Опасно, потому не имеет право на жизнь!
Delete CB
End If
End Sub
Использование через Variant убогое потому, что:
- не позволяет отловить ошибки на стадии компиляции;
- не позволяет пользоваться окошком ShowProperties Domino Designer'а.

<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">2. Наследование "ветвлением" и НОРМАЛЬНОЕ использование через тип базового класса.</div></div><div class="sp-body"><div class="sp-content">Удобно, чтобы писать универсальный код, не задумываясь как и при каких условиях он будет выполняться.
Главное условие - совпадение (потому чёткое наследование!) Public-методов.


Немного меняем базовый класс:
Код:
Class ClassBase

Sub New(ND As NotesDocument)
End Sub

Public Function MyFunction1()
Call Me.ShowNoBaseMsg()
End Function

Private Sub ShowNoBaseMsg()
MessageBox "Данный метод " & TypeName(Me) & "." & LSI_Info(12) & " не предназначен для прямого вызова, его необходимо переопределить и вызывать из объекта класса-наследника!", 48, "Ошибка разработчика..."
End Sub

End Class
Код:
Sub Click(Source As Button)
Dim ND As NotesDocument
Dim CB As ClassBase
'...
Set CB = InitClassObject(ND)
If Not CB Is Nothing Then
Call CB.MyFunction1()			'Объявляли как ClassBase, а пользуемся как классом-наследником, не зная что за наследник!
'Call CB.UserSub()				'А в этом варианте не прокатит уже на стадии компиляции! Имеет право на жизнь только для Private-методов...
Delete CB
End If
End Sub
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">3. Последовательное наследование и выполнение части работы методами базового класса.</div></div><div class="sp-body"><div class="sp-content">Удобно, когда базовый класс может работать самостоятельно, а также наследуемый класс использует часть функционала базового.
Обычно такие классы разносятся по разным библиотекам, чтобы лишний раз не грузить память и каждый раз использовать только тот функционал, что нужен в данный момент.


Код:
Class ClassBE

Sub New()
End Sub

Function MyFunction1()
MessageBox TypeName(Me) & "." & LSI_Info(2)
End Function

End Class
Код:
Class ClassFE As ClassBE

Function MyFunction1()
'Собственная реализация
MessageBox TypeName(Me) & "." & LSI_Info(2)
'А также выполнение этого же метода базового класса
Me.MyFunction1 = ClassBE..MyFunction1()
End Function

End Class
Если в наследующей библиотеке использовать инициализатор, как в предыдущем примере, то дополнительно получаем возможность написания однотипного кода не задумываясь над тем, где и в каких код условиях будет выполяться.

4. Обязательность конструктора наследуемых классов.
В предыдущем примере мы видим, что конструктор в классе-наследнике отсуствует... Он обязателен только, если в него передаётся хоть один параметр. Если же нет, то на конструкторе можно сэкономить :)

5. Динамическое количество параметров в конструкторе.
Редко используемая, но интересная вещь.
 

Akupaka

А че я?.. О.о
04.10.2007
3 360
1
#2
5. Динамическое количество параметров в конструкторе.
"Ану-ка, давай-ка, плясать выхади!" (с) Ну, погоди!

Для наглядности приведу тут источник
<!--QuoteBegin-VladSh+-->
<span class="vbquote">(VladSh)</span><!--QuoteEBegin-->Намного привлекательней всё это становится, если ещё и менять типы параметров, - это можно сделать через Variant:
--------------------------
Код:
Class CBaseParent

Sub New(OBJ As Variant, pS As String)
End Sub

End Class


Class CChildeND As CBaseParent

Sub New(ND As NotesDocument, pS As String), CBaseParent(OBJ, pS)
End Sub

End Class


Class CChildeNDB As CBaseParent

Sub New(NDB As NotesDatabase, pS As String), CBaseParent(OBJ, pS)
End Sub

End Class
[/quote]

Внимание вопрос: что передается в конструктор родителя?
Class CChildeND As CBaseParent
Sub New(ND As NotesDocument, pS As String), CBaseParent(OBJ, pS)


Понятно, pS у нас есть одноименный в дочернем конструкторе, его же передадут и родителю, а OBJ - что за зверь? Откуда берется? Вообще, компилятор должен материться, что не определена! О.о
 
D

Darker

#4
5. Динамическое количество параметров в конструкторе.
а я думал как обычно объявляются необязательные параметры, оказывается вот оно как! Это замечательно! Жаль для методов\функций это не распространяется
 
D

Darker

#6
Sorry, видать не так понял
MessageBox ( message [ , [ buttons + icon + default + mode ] [ , boxTitle ] ] )
думал, речь об этом(только для конструктора)
 
13.03.2009
625
1
#7
Хоть оно понемножку и отмирает, но... предлагаю делиться в этой теме фишками, областью применения классов на LS.
вообще, фактически все паттерны gof, реализуются на ls.
приватный конструктор: http://codeby.net/ipb.html?s=&sh...st&p=147161
использование приватного конструктора позволяет, кроме прочего, реализовывать нормальные фабрики классов - без возможности прямого инстанцирования объекта клиентом. эта же конструкция может использоваца для создания перечислений( enumerations ).
 

VladSh

начинающий
Lotus team
11.12.2009
1 276
6
#8
Внимание вопрос: что передается в конструктор родителя?
Когда использовать из класса-наследника, то всегда передаётся EMPTY.
Эта штука нужна для возможности хранения логики в CBaseParent вне зависимости от типа базового объекта (тип NotesDatabase неудачно выбран, здесь лучше подошли бы самописные классы...), со всеми вытекающими из Variant... Это плохо, но возможность такая есть.

Вообще, компилятор должен материться, что не определена! О.о
Не матерится, и даже работает)))

<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Тогда подробнее (+)</div></div><div class="sp-body"><div class="sp-content">
Код:
Class CBaseParent
Private m_objBase As Variant

Sub New(OBJ As Variant, pS As String)
Call Me.SetBaseObject(OBJ)
Print TypeName(Me) & "." & LSI_Info(2) & "; TypeName(OBJ)=" & TypeName(OBJ)
End Sub

Private Sub SetBaseObject(OBJ As Variant)
If IsObject(OBJ) Then Set Me.m_objBase = OBJ
End Sub

End Class


Class CChildeNDB As CBaseParent

Sub New(NDB As NotesDatabase, pS As String), CBaseParent(OBJ, pS)
Call Me.SetBaseObject(NDB)
End Sub

End Class
И вызов:
Код:
Dim CChildeNDB As CChildeNDB
Dim NS As New NotesSession
Dim NDB As NotesDatabase

Set NDB = NS.CurrentDatabase

Set CChildeNDB = New CChildeNDB(NDB, pS$)
Так пойдёт? :)
 

TIA

:-)
Lotus team
15.05.2009
790
3
#10
Господа, про инициализацию конструктора базового класса в любимом хелпе же есть.

The argument list for the sub New of the base class does not match the argument list for the sub New of the derived class in number and data type of arguments; or you want to pass different arguments to the base class sub New than those passed to the derived class sub New.
 

Akupaka

А че я?.. О.о
04.10.2007
3 360
1
#11
TIA
Я это знаю, меня возмутило именно, что в родительский конструктор, который обязателен, передается непонятная сущность О.о
 

VladSh

начинающий
Lotus team
11.12.2009
1 276
6
#12
Akupaka
В родительском этот параметр описан как Variant, а он всё схавает.
 

Akupaka

А че я?.. О.о
04.10.2007
3 360
1
#17
Да я просто не использую эту хрень
И этот человек будет мне рассказывать "как плохо использовать связи между объектами"! )))
Ты вот сразу про это не мог уточнить? Я тут закипел, мир перевернулся, а все оказывается из-за отсутствия опции декларации!
 

VladSh

начинающий
Lotus team
11.12.2009
1 276
6
#18
И этот человек будет мне рассказывать "как плохо использовать связи между объектами"! )))
К связям между объектами Option Declare никак не относится.

все оказывается из-за отсутствия опции декларации!
Option Declare не имеет тут ни какого значения.
Я вставил его, единственное что пришлось сделать - положить в Declarations библиотеки строку
Код:
Private OBJ As Variant
Даже перекомпиливать вызовы не пришлось. Всё работает.
 

VladSh

начинающий
Lotus team
11.12.2009
1 276
6
#19
Пятница...

Разбирали паттерн State. Честно говоря, был удивлён, но для LS всё получилось:
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">код</div></div><div class="sp-body"><div class="sp-content">
Код:
%REM
Library patternState
Created Apr 1, 2011 by Владислав Сергеевич Шубников/LISSOFT
Description: проба паттерна State
%END REM
Option Public

Const MSGP_LN = "Lotus Notes..."
Const VAR_G1 = "%VAR1%"
Const MSGP_STATENAME = "Я " + VAR_G1 + " !"	'особо правильные пацаны хранят это в справочнике!


%REM
Function localize
Description: заодно поржём над локализацией и глобализацией)))
%END REM
Function localize(pStr As String) As String
localize = Replace(MSGP_STATENAME, VAR_G1, pStr)
End Function


%REM
Class 
Description: основной класс
%END REM
Class Pattern
Public state As StateBase

Function Init(pState As StateBase) As Boolean
Set Me.state = pState
End Function

Sub ChangeState()
Call Me.state.ChangeState(Me)
MsgBox localize(TypeName(Me.state)), 64, MSGP_LN	'а-я-яй! это плохо вызывать внешние функции из класса!))
End Sub

End Class


%REM
Class StateBase
Description: базовый класс для состояний
%END REM
Class StateBase

Public Sub ChangeState(pPattern As Pattern)
End Sub

End Class


Class State1 As StateBase

Public Sub ChangeState(pPattern As Pattern)
Dim st As New State2
Set pPattern.state = st
End Sub

End Class


Class State2 As StateBase

Public Sub ChangeState(pPattern As Pattern)
Dim st As New State1
Set pPattern.state = st
End Sub

End Class
Вызов:
Код:
Use "patternState"

Sub Click(Source As Button)
Dim pattern As New Pattern
Dim state As StateBase

Set state = New State1
Call pattern.Init(state)
Call pattern.ChangeState()
Call pattern.ChangeState()
End Sub
Штука получилась бомбовая!
Основная идея здесь не вбрасывание уже готового состояния извне, не изменение его извне или внутри основного класса, а изменение его изнутри самих классов-состояний, т.е. само состояние, в зависимости от каких-либо условий, может изменить себя или, другими словами состояние объекта может измениться вне его, но ему как бы всё равно...)

Пока не знаю, где это можно применить... /только игр нам на Лотусе не хватало ;D/
Друзья, предлагайте! ;)
 

VladSh

начинающий
Lotus team
11.12.2009
1 276
6
#20
Псевдо-Reflection - реализуем "вычисляемое имя метода"

Например, есть<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">класс с кучей методов</div></div><div class="sp-body"><div class="sp-content">
Код:
Class MegaClass
Sub addInteger(value As Integer)
Print value
End Sub

Sub addDouble(value As Double)
Print value
End Sub

Sub addString(value As String)
Print value
End Sub
End Class
иногда приходится делать такую неприятную штуку, как <div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">switch</div></div><div class="sp-body"><div class="sp-content">
Код:
Set oMegaClass = ...
'sTypeName и value передаются откуда-то извне
Select Case sTypeName
Case "Double":
Call oMegaClass.addDouble(value)
Case "Integer":
Call oMegaClass.addInteger(value)
Case "String":
Call oMegaClass.addString(value)
Case "...":
'...
End Select
Хотелось бы получать параметры и типы извне и производить добавление, как-то в цикле - одной строкой..
Можно вытворить "рефлексию" и на LS, правда от использования Variant уйти удалось не совсем.

Создаём интерфейс, унифицирующий имя метода, и клепаем от него классы, обрабатывающие соответствующий тип:
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Интерфейс и его классы-наследники</div></div><div class="sp-body"><div class="sp-content">
Код:
%REM
Class IReflectionMegaClass
Description: класс-интерфейс для наследников
%END REM
Class IReflectionMegaClass
Private oMegaClass As MegaClass

Sub New(oMegaClass As MegaClass)
Set Me.oMegaClass = oMegaClass
End Sub

Public Sub add(value As Variant)
End Sub
End Class

%REM
Class ReflectionMegaClassDouble
Description: организует перенаправление на метод, работающий с Double
%END REM
Class ReflectionMegaClassDouble As IReflectionMegaClass
Sub New(oMegaClass As MegaClass)
End Sub

Public Sub add(value As Variant)
Call Me.oMegaClass.addDouble(CDbl(value))
End Sub
End Class

%REM
Class ReflectionMegaClassInteger
Description: организует перенаправление на метод, работающий с Integer
%END REM
Class ReflectionMegaClassInteger As IReflectionMegaClass
Sub New(oMegaClass As MegaClass)
End Sub

Public Sub add(value As Variant)
Call Me.oMegaClass.addInteger(CInt(value))
End Sub
End Class

%REM
Class ReflectionMegaClassString
Description: организует перенаправление на метод, работающий со String
%END REM
Class ReflectionMegaClassString As IReflectionMegaClass
Sub New(oMegaClass As MegaClass)
End Sub

Public Sub add(value As Variant)
Call Me.oMegaClass.addString(CStr(value))
End Sub
End Class
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Класс-"обёртка" над этими классами-"типами", использующаяся снаружи</div></div><div class="sp-body"><div class="sp-content">
Код:
%REM
Class PseudoReflection
Description: работа с классом по типу обрабатываемой им переменной
%END REM
Class PseudoReflection
'список всех объектов, реализующих поведение метода add в соответствии с типом
Private lstTypeObjects List As IReflectionMegaClass

%REM
Sub New
Description: инициализируем все типы сразу
%END REM
Sub New()
Dim oMegaClass As New MegaClass()	'в принципе тоже можно передать параметром
Set Me.lstTypeObjects("Double") = New ReflectionMegaClassDouble(oMegaClass)
Set Me.lstTypeObjects("Integer") = New ReflectionMegaClassInteger(oMegaClass)
Set Me.lstTypeObjects("String") = New ReflectionMegaClassString(oMegaClass)
End Sub

%REM
Function getTypeObject
Description: возвращает наружу нужный объект по имени типа
%END REM
Public Function getTypeObject(tag As String) As IReflectionMegaClass
Set Me.getTypeObject = Me.lstTypeObjects(tag)
End Function

End Class
Ну и код использования:
Код:
Dim oReflection As New PseudoReflection
Call oReflection.getTypeObject("Double").add(56342.56869999)
Call oReflection.getTypeObject("Integer").add(21213)
Call oReflection.getTypeObject("String").add({Hello, LS-"reflection"!})

'использование в цикле, к которому мы через такие тернии стремились:
For ...
Call oReflection.getTypeObject(sTypeNameVariable).add(valueVariable)
Next
P.S. Удобно пользоваться при обработке больших объёмов данных; чем больше будет методов (в данном случае типов), тем больше будет выигрыш в скорости.

На Java, конечно, всё было бы гораздо проще)