この特集のトップページへ
Chapter 5:ビジネスロジック層の構築

head2.gif 5.8.2 ネットワークでの動作を前提とした設計
 以上はCOM+の基本的な項目であるが,COM+をビジネスロジックとして扱うときには,個々のクライアントからネットワークを通じてCOMオブジェクトが呼び出されることになる。そのため,COM+の基本的な項目以外に,ネットワークでの動作を前提とした設計にすることも忘れてはならない。ネットワークでの動作を前提とした場合,次の4つの事項に気を付けるようにする。

1)エラートラップは的確に

 ネットワークを通じてCOMオブジェクトのメソッドを呼び出したりプロパティを参照したりする場合,ネットワークの障害によって実行時エラーが発生することもある。たとえば,次のようなメソッドがあるとする。

Public Function add(a As Long, b As Long) As Long
    add = a + b
End Function

 addというメソッドは,引数aと引数bの和を返すメソッドである。どう考えても,このメソッドはエラーを発生する余地はない(もっとも,演算のオーバーフローはあり得るが)。よって,このメソッドを呼び出す場合にエラーのトラップは不要に見える。しかし,ネットワークを通じて呼び出す場合には,ネットワークの障害によって実行時エラーが発生することがある。すなわち,リモートにあるCOMオブジェクトのメソッドを呼び出したりプロパティを参照したりする場合には,必ず実行時エラーのトラップ(On Error Resume Next構文やOn Error Goto構文)を含める必要がある。

 

2)大量の引数は渡さない

 あたりまえのことだが,メソッドやプロパティの引数に大量のデータを与えるべきではない。当然,その分だけ引数の引き渡しにかかる時間が長くなるので,速度は低下する。細かいことだが,整数の引き渡しをするときには,常にLong型を使うのではなく,0〜255の範囲でよければByte型を,-32767〜32768の範囲でよければInteger型を,それぞれ用いるのが理想的である。Long型は4バイト消費するのに対し,Byte型は1バイト,Integer型は2バイトですむからである。もっとも,そのような細かいところは,よほどの高パフォーマンスを追求しない限り考えなくてもよい。しかし,大量の文字列を渡したり,配列を渡したりするようなときには,頭の片隅にでもとどめておいたほうがよいだろう。たとえば,値を受け取ったCOMクライアント,メソッド,プロパティが文字列の一部しか利用しないのであれば,その部分だけを切り出して渡したり,配列中の特定の要素しか使わないのであれば,その要素だけを引き渡すといった工夫をするとよい。

 

3)値を返す必要がなければByVal渡しを使う

 Visual Basicでプロシージャ(メソッドやプロパティ)を実装する場合,引数の渡し方として,ByRef渡しとByVal渡しの2種類がある。これは,引数のまえにByRefキーワードやByValキーワードを記述することで指定する。

  【ByRef渡し】
Function プロシージャ名(ByRef 変数名 As , ) As 
  【ByVal渡し】
Function プロシージャ名(ByVal 変数名 As , ) As 

 Visual Basicでは,引数をプロシージャに記述する場合,ByRefキーワードもByValキーワードも書かなければ,ByRef渡しとみなされる。一般に,ByRef渡しは「参照渡し」,ByVal渡しは「値渡し」と呼ばれる。

 参照渡しでプロシージャを呼び出すときには,データが含まれたメモリの領域を指し示す場所がプロシージャに渡される。よって,プロシージャ側で実際にデータを読み書きする場合には,呼び出し元のメモリを参照することになる。一方,値渡しでは,引数が格納している値そのものがプロシージャに渡される。

 参照渡しの場合には,メモリの領域を指す場所が渡されるので,プロシージャはその値を書き換えて,呼び出し元に渡すことができる。しかし,値渡しの場合には,その値を変更しても,プロシージャの呼び出し元には伝わらない。この関係を図示すると,Fig.5-60のようになる。

Fig.5-60 参照渡しと値渡し
fig5_60.gif

 では,COMオブジェクトのメソッドの呼び出しは,参照渡しと値渡しのどちらがよいのだろうか。結論からいえば,「Chapter 2 Component Object Model」で作成したCOMコンポーネントのようにネットワークを経由しないでアクセスするものは,引数を参照渡ししたほうが高速である。なぜなら,値渡しのときには値を取り出して渡す必要があるのに対し,参照渡しの場合には,変数が占めているメモリ領域の場所のみを渡せばよいからである。特に,引数が指しているメモリ領域が多い場合(たとえば,巨大な配列など)には,その差が顕著に表れる。

 しかし,COMクライアントとCOMオブジェクトが別のコンピュータで動作するような場合には,値渡しのほうが効率がよい。参照渡しの場合には,メモリの領域を指す場所がプロシージャに渡されるわけだが,別のコンピュータにある場合は,当然ながら,そのメモリ領域を直接参照することはできない。そこで,実際に値を取得するため,プロキシとスタブを経由して値の問い合わせが発生する。つまり,値を取得するためには,Fig.5-61に示すようにネットワークを3回通ることになるのだ。

Fig.5-61 別のコンピュータで動作しているCOMオブジェクトに参照渡しで引数を渡した場合
fig5_61.gif

 一方,値渡しでデータを渡した場合には,メソッドの呼び出しと同時にその値も一緒に送られるため,1回の呼び出しですむ。よって,リモートで呼び出される可能性のあるCOMオブジェクトのメソッドやプロパティの引数は,値を書き換えてCOMクライアントに返す必要性がなければByVal渡しにすることが望ましい。

 

4)メソッドやプロパティの呼び出しは最小限の回数にする

 メソッドやプロパティの呼び出しは,最小限の回数に抑えたい。たとえば,次のケースを考えてみよう。

  1. 【COMクライアント】
    Set obj = CreateObject("プログラムID")
    obj.A = 10
    obj.B = 20
    MsgBox obj.Result
    Set obj = Nothing
    
  2. 【COMコンポーネント】
    Private localA As Long, localB As Long
    Public Property Let A(ByVal value As Long)
        localA = value
    End Property
    Public Property Let B(ByVal value As Long)
        localB = value
    End Sub
    Public Function Result() As Long
        Result = localA + localB
    End Function
    

 この例は,AプロパティとBプロパティにそれぞれ値をセットし,その和をResultメソッドで取得するというものである。この方法は,Aプロパティの設定,Bプロパティの設定,Resultメソッドの呼び出し,という3回の呼び出しが必要となるので効率が悪い。できれば,次のように1つのメソッドの呼び出しにまとめるべきである。

  1. 【COMクライアント】
    Set obj = CreateObject("プログラムID")
    MsgBox obj.Result(10, 20)
    Set obj = Nothing
    
  2. 【COMコンポーネント】
    Public Function Result(ByVal a As Long, ByVal b As Long)
        Result = a + b
    End Function
    

 こうすれば,1つのメソッドを呼び出すだけですむため,ネットワークを何度も往復しなくてすむ。この例は単純な例であり,わざわざ例示したような実装をする人は,まずいないだろう。ただ,ここでいいたいことは,プロパティとして一度値を設定しておいてから,その結果を得るためにメソッドを呼び出すのではなく,メソッドで利用する値は,メソッドの引数として引き渡してしまったほうが効率がよいという点にある。また,こうしておけば,COMオブジェクトの内部変数に値を保持する必要がなくなり,ジャストインタイムアクティベータによって非アクティブ化されたときに変数が保持している値が失われてしまうという問題も,いくらか解決できる。

 もっとも,いつでもプロパティを利用せずに引数として値を渡したほうがよいとは限らない。同じ値を何度も利用するCOMオブジェクトの場合には,プロパティとして先に渡してCOMオブジェクト内の変数に格納するような実装にしたほうが効率がよくなるケースもある。

prevpg.gif Chapter 5-2 15/16 nextpg.gif