2024/07/07

クラス化に挑戦: #13)クラス機能の追加

前回までの Google マップ - Place API を利用する機能をクラス化しました。スマホ用の Nomad と組み合わせると、現在地を中心に近隣を検索できるようになります(HCL Nomad で GPS 座標の取得)。最寄りのお店や施設を検索するなどさまざまな利用方法が考えられますね。

このような機能を実現すると、情報として現在位置からの距離が欲しくなります。そこで、地点情報を表す Location クラスに距離を計算する機能を追加します。


作成するメソッド

作成するメソッドの使い方は次の通りとします。

   dDistance = oLoc1.CalcDistance(oLoc2)

oLoc1、oLoc2 は座標を表す Location クラスのインスタンスです。距離を計算するメソッド CalcDistance を実行すると 2 点間の距離を計算して返す機能となります。


距離の計算

緯度経度で表される座標は球面を前提としています。そのため距離に変換するためには、地球の大きさなどを利用して計算する必要があります。

先輩ブロガーの中野さんが下記の記事で関数を公開されています。この記事で紹介されている distance という関数を流用させていただきます。

Nomad で GPS を使う

LotusScript で記述されているので、そのまま使えます。ありがたいですね!


クラスの修正と確認

コード量の多いクラスではありませんので、クラス全体のコードを掲載します。

赤字の部分がオリジナルの追加コードです。青字の部分がブログから拝借した関数なのですが、クラス内部の関数として使用していますので private 宣言だけ追加しています。

Public Class Location
   Private zdLatitude As Double '緯度
   Private zdLongitude As Double '経度

   Public Sub New(ByVal vdLatitude As Double, ByVal vdLongitude As Double)
      zdLatitude = vdLatitude
      zdLongitude = vdLongitude
   End Sub

   Public Property Get Latitude As Double
      Latitude = zdLatitude
   End Property

   Public Property Get Longitude As Double
      Longitude = zdLongitude
   End Property


   Public Function CalcDistance(voDestination As Location) As Double
      CalcDistance = distance(zdLatitude, zdLongitude, voDestination.Latitude, voDestination.Longitude)
   End Function

   Private
Function distance( lat1 As Double, lng1 As Double, lat2 As Double, lng2 As Double ) As Double
      Dim radLat1 As Double, radLng1 As Double
      Dim radLat2 As Double, radLng2 As Double
      Dim aveLat As Double, aveLng As Double
      Dim c As Double
      Const r = 6378137.0
'赤道半径

      '円弧の長さを扱うため、角度(緯度経度)をラジアンへ変換
      c = 180 / PI
      radLat1 = lat1 / c
      radLng1 = lng1 / c
      radLat2 = lat2 / c
      radLng2 = lng2 / c

      aveLat = ( radLat1 - radLat2 ) / 2
      aveLng = ( radLng1 - radLng2 ) / 2

      distance = r * 2 * ASin( Sqr( Sin( aveLat ) ^ 2 + Cos( radLat1 ) * Cos( radLat2 ) * Sin( aveLng ) ^ 2 ) )
   End Function
End Class


動作検証として簡単単なエージェントを作成します。

Option Declare
Use "lsGoogleMAP"

Sub Initialize
   Dim oLoc1 As Location
   Dim oLoc2 As Location

   Set oLoc1 = New Location(34.68374, 135.49698)
   Set oLoc2 = New Location(34.68354, 135.49616)

    MsgBox CStr(Format(oLoc1.CalcDistance(oLoc2), "0.00")) & "m"
End Sub

適当な 2 つの座標 oLoc1 と oLoc2 を用意し、CalcDistance メソッドを実行します。これで 2 地点間の距離が求められますので、それをメッセージボックスで表示しています。


まとめ

クラスに機能(メソッド)を追加する方法を紹介しました。以前作成したクラスに具体的な機能を追加したので、イメージしやすかったと思います。この作業を材料に、クラス化の効果について整理します。

例えば Location クラスとして提供した機能を通常の関数群として開発したとします。追加機能である CalcDistance 関数は、スクリプトライブラリ内に Public な関数として定義が必要です。スクリプトライブラリを利用するプログラムからアクセスできる必要があるからですね。

Public な関数はプログラム全体で一意である必要があります。もし、このスクリプトライブラリを利用するエージェントやフォームで別の CalcDistance 関数が存在すると、エラーとなります。このライブラリを利用するには、ライブラリ外で名称を変える調整(修正)が発生することになります。

クラス化した場合は oLoc1.CalcDistance(oLoc2) のような記述になります。通常の関数とはスコープが違うので、文法エラーにはなりません。

このように、クラス化しておくと既存プログラムに対する影響を低減させる効果があります。また、この二次的な効果としてメソッドやプロパティ名をシンプルにできる効果があります。クラス内で名称が一意であればよく、別のクラスで同名があっても別扱いになるからですね。

クラス化したほうがより部品化が進めやすくなるということですね。


前回 クラス化に挑戦


2024/06/30

DXL のヘルプ はじめました!

今年は、DXL の理解を進めることをメインの目標にしてノーツに取り組んでいます。このブログでも、昨年掲載した『DXL ことはじめ』にはじまり、『DXL Step-by-Step』の連載は現在も継続中です。

DXL はドキュメントなどの情報が少ないため、連載の材料収集はトライ&エラーで収集しています。わかったことは都度記事にまとめていますが、一覧性がなく、必要なものを探し出しにくい状態になっています。

そこで、DXL のノードや属性をまとめたサイトを作ることにしました。


題して『出直し!! ヘルプ DXL ノード編』です!


出直し!! ヘルプ DXL ノード編

今年1月に公開した @関数編 の DXL 版というところですね。右の『出直し!! ヘルプ』メニューからアクセスしてください。

左の一覧はノード名順のインデックス、右側は機能別にノードをまとめて表示しています。


このヘルプでは、ノードを主軸に、

  • 上位のノード(Contained by)
  • 下位のノード(Contains)
  • 属性(Attributes)
  • 当ブログ内の関連記事

をまとめています。


乞うご期待!

もう少しまとまってからの公開を考えていたのですが、まとめる情報量がお余りに多くいつまでたってもゴールが見えないこと、すでに多くの情報が集まったことから公開することとしました。

書きかけの項目や不足しているノードが多数あり、まだまだ未完成な状態です。今後の調査で分かったことも日々追記して、少しずつ充実させていきます。発展途上であることをあらかじめご了承ください。


DXL を使えば、LotusScript の限界を超えることができます。興味のある方は、冒頭に紹介した連載とこの『出直し!! ヘルプ DXL ノード編』でチャレンジください。


すでに DXL で開発している超レアリティが高い方にも参考になるようまとめていく予定です。参考になれば幸いです!


2024/06/29

DXL Step-by-Step:#36)罫線の設定 - 境界線のスタイル

罫線の設定は色や線の種類のように表全体に適用される設定と罫線の幅のようにセル単位で指定できる設定があります。今回は前者の境界線スタイルの設定についてまとめます。


境界線のスタイル

下図のようにスタイルを「エンボス1」に設定し、色を緑に設定した表を作成します。

この表を DXL に変換すると 2 つの属性 cellborderstyle と cellbordercolor が設定されます。設定の詳細は次の通りです。

属性 設定値 補足
cellborderstyle (なし) 塗りつぶし
'ridge' エンボス1
'groove' エンボス2
cellbordercolor 設定については #22)文字の装飾 の文字色の設定と同様


サンプルプログラム

前回作成したプログラムに境界線のスタイルをセットする部分を追加します。

今回は属性を 2 つ追加するだけなので単純です。関数化するほどでもないので、メインルーチン内でセットしています。

Function xSetDXL(vdprs As NotesDOMParser)
         ・・・
   '列幅の設定
   Call xSetDXL_colwidth(denTbl, 1, DXL_CMToInch(3))
   Call xSetDXL_colwidth(denTbl, 2, "70%")
   Call xSetDXL_colwidth(denTbl, 3, "30%")
   Call xSetDXL_colwidth(denTbl, 4, "30%")

   '境界線のスタイル
   Call denTbl.SetAttribute("cellborderstyle", "ridge")
   Call denTbl.SetAttribute("cellbordercolor", "green")


   '表後の段落追加
   Call xSetDXL_par(ddn, denRT)
End Function

実行すると、境界線のスタイルと色の設定ができていることが確認できます。

このプログラムで作成される DXL は次の通りです。


まとめ

第 32 回から 5 回にわたり、表の全体にかかわる設定をまとめました。通常のリッチテキストクラスでは操作できない表のプロパティ設定がいくつもありましたね。

次回からは表のセルについてまとめます。セルの設定でも、ノーツクライアントから設定できる操作はほとんど実現できます。DXL 面白いですね。


前回 DXL Step-by-Step


2024/06/28

DXL Step-by-Step:#35)表の列幅の設定

前回ご紹介した通り、表の列幅は tablecolumn ノードの width 属性で設定します。今回はこの列幅を設定するプログラムを作成します。

tablecolumn ノードは table ノードの子ノードとして存在し、表の列数だけ存在しました。

これまで作成したプログラム(#32 を参照)に組み込むとすれば、表を作成する部分で設定すると簡単に実現できます。

例えば以下のサンプルでは、1 列目を固定幅(3cm)に設定しています。

Function xSetDXL_table(_
      ・・・

   '表の作成
   Set den = vddn.CreateElementNode("table")
   Set denTbl = vdenRT.AppendChild(den)

   '列定義の作成
   For iCol = 1 To viCols
      Set den = vddn.CreateElementNode("tablecolumn")
      If iCol = 1 Then
         Call den.SetAttribute("width", DXL_CMToInch(3))
      End If

      Call denTbl.AppendChild(den)
   Next

   '行の作成
      ・・・
End Function

このプログラムで 1 列目の tablecolumn ノードは次のようになります。

<tablecolumn width='1.1811in' />


tablecolumn ノードを取得するには

ただ、この方法だと、表を新規で作成(=列を新規で作成)するタイミングでしか設定できません。そこで、今回は、別途作成した表や既存の表に対して列幅を設定する方法を考えます。

必要となる機能は、

  1. table ノードの子ノードから tablecolumn ノードを探す
  2. 特定の列(n 番目)の tablecolumn ノードを取得する

となります。


NotesDOMElementNode クラスには GetElementsByTagName メソッドが定義されています。このメソッドを使えばノード名称で配下のノードを検索できます。ただ、この命令は子ノードだけでなく、孫ノードなど配下のすべてのサブノードが対象となります。そこで子ノードだけを対象にした関数 GetChildByName_Nth を自作します(lsDXL ライブラリに作成)。

引数は 3 つで、検索するノード(親ノード)、ノードの名称、何個目か となっています。発見できたらそのノード、見つからない場合は Nothing を返します。

Public Function GetChildByName_Nth(_
               vdenParent As NotesDOMElementNode, _
               ByVal vsNodeName As String, _
               ByVal viIndex As Integer) As NotesDOMElementNode
   Dim i As Integer
   Dim dn As NotesDOMNode

   Set GetChildByName_Nth = Nothing

   i = 0
   Set dn = vdenParent.FirstChild
   Do Until dn.Isnull
      If dn.NodeType = DOMNODETYPE_ELEMENT_NODE Then
         If dn.NodeName = vsNodeName Then
            i = i + 1
            If i = viIndex Then
               Set GetChildByName_Nth = dn
               Exit Function
            End If
         End If
      End If
      Set dn = dn.NextSibling
   Loop
End Function

仕組みは単純で FirstChild と NextSibling プロパティを使用して子ノードを順に取得してチェックしているだけです。万一、子ノードが textnode など NotesDOMElementNode でない場合に備え、汎用的な NotesDOMNode のオブジェクト変数を使用しています。


既存の列幅の設定

GetChildByName_Nth 関数の準備ができたら、これを使用して列幅を設定する関数を作成します。引数は 3 つで table ノード、列番号、列幅の設定となっています(エージェント内に作成)。

Function xSetDXL_colwidth(vdenTbl As NotesDOMElementNode, _
                                        ByVal viCol As Integer, ByVal vsWidth As String)
   Dim denCol As NotesDOMElementNode

   Set denCol = GetChildByName_Nth(vdenTbl, "tablecolumn", viCol)
   If Not(denCol Is Nothing) Then
      Call denCol.SetAttribute("width", vsWidth)
   End If
End Function

関数内では指定した列番号の tablecolumn ノードを取得。取得できたら列幅 width 属性をセットします。


メインルーチンを修正して、列幅を設定します。今回は、1 列目を 3cm で固定、2 列目を 70%、3 列目を 30% としています。

Function xSetDXL(vdprs As NotesDOMParser)
            ・・・
   '表の追加
   Set denTbl = xSetDXL_table(ddn, denRT, 3, 3)

   '間隔の設定
   Call xSetDXL_table_spcing(denTbl, 0.2, 0.5)

   '列幅の設定
   Call xSetDXL_colwidth(denTbl, 1, DXL_CMToInch(3))
   Call xSetDXL_colwidth(denTbl, 2, "70%")
   Call xSetDXL_colwidth(denTbl, 3, "30%")


   '表後の段落追加
   Call xSetDXL_par(ddn, denRT)
End Function

なお、今回のプログラムでは列の設定効果がわかりやすいよう作成する表を 3 列に変更しています。ご注意ください。


実行すると列の幅が設定された表が作成されます。実行結果と生成される DXL は以下の通りです。



前回 DXL Step-by-Step 次回


2024/06/25

アプリケーションカタログ にフォルダ別ビューを追加

先日まで数回にわたりアプリケーションカタログを紹介しました。さまざまな情報が集約されており、アプリケーションの管理に活用できそうですね。

サーバ内のアプリケーション(nsf)は通常、利用部門やアプリの種類などに応じてフォルダ分けして整理している場合が多いと思います。このような場合フォルダからアプリを探すことも多いですよね。カタログアプリでは、[アプリケーション]-[サーバー]のビューがそれに近いといえます。

ただ、このビュー、タイトル列でソートされて、アプリの配置からは探せません。そこで、今回は、アプリケーションカタログにフォルダ別のビューを追加してみましょう。


ビューの準備

上記ビュー『アプリケーション¥サーバー | ByServer』を複製してベースとします。デザイナーからビューをコピペすると次のような確認メッセージが表示されます。これは、コピーしたビューがテンプレートから引き継がれるべきか確認しています。独自のビューでテンプレートにはないので、[いいえ]を選択します。

複製されたビューは次の通り、「更新時に再設計/設計の置換を禁止する」にチェックが入ります。これで、設計置換やデザインタスクなどで、ビューが更新されることを抑制できます。

具体的な作業に入る前に、ビューの名前を更新しておきましょう。


フォルダでカテゴライズ

今回はフォルダでカテゴライズすることから、ファイルパスをファイル名とパスに分割します。

まずは、ファイル名です。ファイル列に次の式をセットします(バックスラッシュに変換されないよう¥は全角文字を使用しています)。

xLst := @Explode(Pathname; "¥¥");
@Subset(xLst; -1)

この式は @Explode を使って ¥ マークで分離したリスト値を作成します。そのリストから @Subset で一番後ろの要素を取得しています。これでフォルダ階層に関係なくファイル名だけを取得しています。


次にフォルダ名です。ファイル列の左に新しい列を作成し、次の式をセットします。

xLst := @Explode(Pathname; "¥¥");
xIdx := @Elements(xLst);
@If(xIdx = 1;
   "";
   @Implode(@Subset(xLst; @xIdx-1); "¥¥")
)

ファイル列と同様のテクニックを使用していますが、ファイル名を除いた部分を取得することから、@Elements を使用して要素数を取得、1 引くことでファイル名を除いています。この結果を @Implode で ¥ 区切りの文字列に戻し、フォルダ名としています。

なお、ルート(フォルダがない)場合はエラーとなるので、@If でエラー処理をしています。


フォルダ別のカテゴライズ

ここまでできたら、フォルダ列とファイル列を昇順にソートします。

そして、フォルダ列をカテゴライズします。カテゴリ列は ¥ を階層化して表示する機能があります。これを利用すると、以下のようにフォルダ階層に応じて、階層の深さが違うカテゴリを 1 列で実現できます。

ただ、サーバ直下のアプリは『(カテゴリ無し)』となっていて、格好が悪いですね。これを回避するためのカテゴリの第一階層をサーバ名に変更します。

xSrv := @Name([Abbreviate]; Server);
xLst := xSrv:@Explode(Pathname; "¥¥");
xIdx := @Elements(xLst);
xFol := @Subset(xLst;@Elements(xLst)-1);
@Implode(xFol; "¥¥")

今回の式では ¥ で分離したリスト値 xLst を作成する時点で先頭の要素にサーバ名を足しています。こうすることで、要素数が 1 となることはないので、エラー処理を省略しています。

ここまでの対応でビューは次のようになります。

ルート直下のアプリケーションが多くそれでも見づらい部分があります。これについてはテンプレート(.ntf)を表示しない、カタログの表示対象から外すなどの対応が必要かと思います。


まとめ

今回は、カタログにビューを追加する作業を行い、テンプレートから更新しない方法と ¥ でカテゴリを階層化する方法を解説しました。

なお、今回は事例として標準テンプレートの設計を変更しました。ビューの追加だけですので、影響はないと判断しています。とは言え、標準テンプレートをカスタマイズした場合、メーカサポートの対象外となることもあります。お使いの環境に適用される際には、リスクとベネフィットを十分にご検討の上、ご自身の責任で実施ください。


2024/06/23

DXL Step-by-Step:#34)表の列幅のしくみ

今回は、表の列幅の設定についてまとめます。


列幅の設定

ノーツクライアントでは表のプロパティで列幅を設定できます。設定は固定幅と可変幅に大別できます。固定幅の場合は、『固定幅』にチェックを入れて、幅を指定します。

可変幅の場合は、『固定幅』のチェックを外し、幅を指定します。画面上の表全体の幅からの相対的な幅となるので、画面のサイズが変われば伸縮します。


列幅の設定と DXL

2 行 3 列の表(マージンに合わせる設定)を作成し、1 列目の幅を 2 cmで固定します。残り 2 列は可変幅のまま、サイズを適当に調整します。

この表を DXL に変換し、列の定義 tablecolumn ノードを確認すると次のようになります。

固定幅の場合は、長さを明示的に指定し、可変幅の場合は % で指定されています。マージンの設定などと同じ仕組みですね。ただ、列幅の場合には、可変幅の合計が 100% となる仕様のようです。当たり前ではありますが...

列幅の設定 tablecolumn ノードの width 属性は次の通りとなります。

属性 設定値 補足
width '2.7875in' など 固定幅として幅を長さ(インチ)で指定
'33%' など 可変幅として割合(%)で指定
表内の可変幅の合計が 100% となる

また、前回 紹介した表の幅の設定(table ノードの widthtype 属性)で固定幅となっている場合には、列幅も固定幅だけとなります。DXL で表を操作する際には、設定のつじつまが合うように注意が必要ですね。


前回 DXL Step-by-Step 次回


2024/06/21

アプリカタログのカテゴリを活用しよう!

前回作成したアプリケーションカタログには、カテゴリ別というビューが存在します。

数が少ないので、サーバ内のすべてのアプリケーションが表示されているわけではなさそうです。このビューの設計をデザイナーで開き確認します。細かな点はさておき、Categories フィールドが 空 でない文書を表示していることがわかります。

カタログ内の文書では、以下の項目の部分が Categories フィールドとなります。

では、この値の出所を確認しましょう。そのアプリケーションのプロパティを確認すると、カテゴリ欄にセットされていました。Catalog タスクで情報を収集する際、このプロパティの値をカテゴリとして記録しているということですね。


改めて、カテゴリ別というビューの1列目をデザイナーで確認します。列式は次の通りです。

カンマとセミコロンで分離してカテゴリ化するしているようです。そこで、ヘルプDBのカテゴリを修正します(確認を兼ねていますので、分離記号をあえてそろえていません)。

ついでにその他のヘルプ系のDBのプロパティも同様に修正し、Catalog タスクを実行します。すると、次のような結果となり、複数のカテゴリにヘルプDBが表示されます。


この機能を利用すれは、利用者の部門や事業所、アプリケーションの種類など、さまざまな切り口でアプリケーションを分類・整理できます。複数のカテゴリに対応しているあたりが、かゆいところに手が届いていていいですね。


2024/06/19

アプリケーションカタログ とは?

Domino サーバ内のアプリケーションデータベースをどのように管理されてますでしょうか? 

Domino サーバには標準でサーバ内のアプリケーションを管理する機能が存在します。今回は、『アプリケーションカタログ』というその機能を紹介します。

なお、nsf をデータベースと呼んできた時代には、『データベースカタログ』と呼んでいました。


アプリケーションカタログの概要

Domino サーバ内には、catalog.nsf というアプリケーションが存在します。これを開くとサーバ内のアプリケーションの一覧が確認できます。例えば、[データベース名]を開くと名称でカテゴライズされたビューが開きます。


文書を開くとデータベースの様々な情報が確認できます。


まず、アプリケーション タブには、名称だけでなく、配置されているサーバ名、ファイル名、レプリカIDまで記録されています。ほかには、文書数やDBのファイルサイズ、作成日などもあります。

タブの最下部には、使用状況が表示されています。

また、アクセス制御リストタブには ACL の設定が列挙されています。

アプリケーションに関する情報が集約されて記録されていますので、さまざまな用途で利用できそうですね。


カタログ作成の仕組み

アプリケーションカタログを作成するには、サーバ上で Catalog タスクを実行します。実行するとそのサーバ内にあるすべてのデータベースがカタログに登録されます。

もし、サーバに catalog.nsf が存在しない場合には、 catalog.ntf から自動的に作成されます。

Domino サーバのデフォルトでは、午前 1 時に Catalog タスクが実行されます。これは、サーバの notes.ini に以下の行が記述されているからです。

ServerTasksAt1=Catalog,Design


カタログの表示

Catalog タスクは、メールやテンプレートをを含む、サーバ内のすべてのアプリケーションの情報を収集して記録します。

ただし、 プロパティの[設計]タブで『データベースカタログに表示』にチェックされているものだけがビューに表示される仕組みになっています。

この設定をうまく利用すると、一般利用者通常使用するアプリだけをリストアップすることができます。しかも、データ収集はサーバが自動で行ってくれますので便利ですね。


まとめ

カタログ文書を開くと上部に[開く]ボタンがあります。クリックするとそのアプリケーションが開きます。うまく使えば、アプリの一覧をお手軽に作成できそうですね。


2024/06/17

すべての LotusScript をリコンパイルは ”すべて” ではない !?

このブログでは、汎用的な関数など共通の関数やクラスなどはスクリプトライブラリで管理し、各アプリケーションではそれを利用する方法をお薦めしています。プログラムの独立性の向上や分業化など開発効率が上がると考えているからです。

今回はスクリプトライブラリを使用した環境で、気がついた障害(?)と注意点についてまとめます。


すべての LotusScript をリコンパイル

Domino デザイナーのメニューには[ツール]-[すべての LotusScript をリコンパイル]があります。一般的に、スクリプトライブラリに変更があった場合など、LotusScript のプログラム全体をチェックしたい場合に利用します。実行すると開発中のアプリケーション内のすべての LotusScript に対して文法的な整合性チェックを行います。

ライブラリの新バージョンがリリースされてそれを開発中のアプリに組み込んだ時、ライブラリ内の関数のインタフェース(引数など)を修正した場合に利用すると便利な機能です。


発見した問題と再現方法

先日、この『すべての LotusScript をリコンパイル』という機能が、すべてのコードを対象にしていないという問題を発見しました。

まずは、現象の再現です。

次のような単純なスクリプトライブラリとそれを利用するビューのアクションボタンを作成します。


プリビューして、ボタンをクリックすると ”テスト” とメッセージが表示されます。当たり前ですが...

この状態で、スクリプトライブラリを下記の通り、引数を削除して、メッセージを固定の文字列に変更して保存します。

Option Declare

Public Function ShowMsg()
   MsgBox "テスト", 64
End Function

この状態で「すべての LotusScript をリコンパイル」を実行します。引数が合わないので文法エラーとなるべきところなのですが、正常に終了してしまいます。

ただ、コンパイルが成功したからと言って、ノーツからそのボタンを押すとエラーが発生します。

エラーメッセージには、引数が正しくないことと関数名が表示されていますので、この現象はコンパイルの機能に問題があるということになりますね。


問題の詳細と対策

調査した結果この現象は、Notes 6 のころから発生している既知の問題(問題報告番号 GFLY5VWM3C)で、現在(14.0)でも発生するそうです。詳細が以下のサポート情報に掲載されていますのでご確認ください。

すべての LotusScript をリコンパイルを実施してもアクションボタンの LotusScript が精査されない

Workaround として、エラーが出るべき設計要素をデザイナーで編集し、再保存するタイミングでエラーが検知されるので取り除く、と紹介されています。設計数が多い時には少々つらい対策ですね。

現時点で開発者が注意すべきは「すべての LotusScript をリコンパイル」機能を過信しないことになります。今回事例にあげた引数の変更以外に、サポート情報にある関数名の不一致なども検知できないようです。アプリ利用時に文法エラーのような基本的なエラーに出くわした場合、この問題を疑ってみるべきかもしれませんね。

ちなみに、修正は『問題の重要度やリクエスト数など複数の要素で勘案される』そうです。開発ツールとして恥ずかしいレベルの問題と思うので、勘案している場合じゃないぐらい重要度は高いと感じますけどね...


2024/06/08

クラス化に挑戦: #12)クラス化作業のまとめ

前回まで 6 回にわたり、Google マップ の Place API の検索機能を題材にクラス化する作業を体験しました。結果を振り返りながら、クラス化の作業について整理しましょう。

改めて、今回作成したライブラリをデザイナーで確認してみます。全体を眺めると見えてくることがあります。

カプセル化

まず、最初に感じるのはスッキリ感ですね。

クラスを使用しない開発の場合、ライブラリに関数が増えてくると、煩雑になり、必要なものを探すのに苦労するようになります。名称順に並んではくれますが、機能的関連性はわかりません。名称を工夫する必要が出てきますが、数が多くなると悩ましくなってきます。

クラスは階層化して表示されます。そして、その階層の中にはクラスに関係があるものが集約されています。整理された状態になっており、気持ちがいいですね。

このように集約してまとめることをオブジェクト指向の世界では『カプセル化』というようです。


インターフェースの明確化と隠蔽

デザイナーでは Private な関数や変数には黄色の■がアイコン表示されています。これが付いていないものが Public となります。このマークを見れば、インターフェースとなるプロパティやメソッドがわかるということですね。

インターフェースを明確にするのは、不用意にデータにアクセスさせないなど、クラスが持つ機能やデータを安全に利用することが目的です。

プロパティで入力チェックを行い合格したデータだけを採用(メンバ変数にセット)したり、内部で利用するだけの関数を Private 化するなど、クラスの利用者が想定外の操作をしないようにして、安全性を高めます。このような考え方を『隠蔽』といいます。


命名規則

インターフェースとなるプロパティやメソッドは Count や Search など簡潔で端的な名称としています。クラス内に作成するメンバ変数や Private な関数には接頭文字として z をつけており、一覧で下の方に集まるようにしています。

結果的に、インターフェースが上に集まり目立つということですね。

複数のクラスで使用しそうな汎用的な関数(xGetElementByName_Obj や xGetLocation)はクラス外で定義されています。クラス外であることがわかるよう接頭文字は x としています。

関数の命名を少し工夫するだけで、さらにわかりやすいプログラムになりますね。


デバッガとクラス

クラスを利用した開発でデバッガを利用するには注意が必要です。

まずは不便な点、下図のイベント欄です。

クラスの名称や実行中の関数などが表示されず Declarations と表示されています。実際には NearBySearch クラスの Search メソッドを実行中なのですが表示されていません。

ステップ実行して確認するだけであれば、あまり気になりませんが、クラス内の Private 関数にブレークポイントをセットする場合など、クラス内を横断的に見たいときには、その場所を探すが大変です。

イベント欄にクラスを表示して、クラスを選択したら右にクラス内の関数を表示するなど、改善してほしいですね。

Notes 8 以降、スタンダード版のデザイナーが提供されるようになって、クラスが階層化して表示されるなど、クラス開発の効率がずいぶん上がりました。ただ、デバッガの機能は進化がなく旧来のままとなっています。機能強化が待たれますね。


デバッガの変数のタブは見方に少し注意がが必要です。下図は NearBySearch クラスの Search メソッドを実行中の、変数タブの状態です。

関数内の変数に混ざって、ME という変数があります。これが、Search メソッドを実行してるクラスのインスタンス(自分自身)を表します。中を確認すると、クラスのメンバ変数とその値が確認できます。NotesDocument などノーツオブジェクトクラスの場合はプロパティの値が見えますが、ユーザ定義クラスではプロパティとしては確認できません。

なお、3 列目の型には NEARBYSEARCH とクラス名が表示されています。また、zoLocation のようにメンバ変数内にユーザ定義クラスのインスタンスがある場合、そのメンバ変数がさらに階層化されて表示されます。


前回 クラス化に挑戦