wetchのブログ

他人に見られることを想定していない書き散らかし独習ノート.物理学とかVBAとか.

インターフェースを使っていらんプロパティを隠ぺいする

今回はこの記事の続き。
https://wetch.hatenablog.com/entry/2018/11/24/145716

標準モジュールからは見えないけど同じクラスの別インスタンスからは見える、都合のいいメソッドの書き方を探していると、こちらにヒントが。
thom.hateblo.jp
インターフェースを使うとそういうことができると書いてある。けど書き方は書いてないので自分で四苦八苦して、なんとかできそうなのでまとめとこうと思う。

自分が作りたいのは物理量クラスなんだけど、まずは練習として分数クラスでやってみた。どうせ物理量クラスでも使うし。
このクラスでやりたいことは、

  • 内部変数としては、分数なので分子と分母(ともにLong型)を持つ。
  • 標準モジュールでは分子、分母に直接アクセスできないようにする。
  • でもクラスモジュール内では直接アクセスする必要がある。

ということ。

例えば足し算をするために、標準モジュールではこう書きたい:

sub 分数sample()
    Dim a as IFraction, b as IFraction, sum as IFraction 
    ' IFractionは分数クラスのインターフェース
    Set a = New Fraction
    Set b = New Fraction
    Set sum = New Fraction

    a.SetFraction(1, 2) ' a = 1/2
    b.SetFraction(1, 3) ' b = 1/3
    
    set sum=a.plus(b) 'sum=a+b
    Debug.Print sum.toString
end sub

で、インターフェースIFractionではこう書けばいいのかな?

Public Sub SetFraction(n As Long, d As Long)
End Sub

Public Function Plus(x As IFraction) As IFraction
End Function

Public Function toString() As String
End Function

これで動くように、具象クラスFractionを書く必要がある。最初はこう書いてみた。

Implements IFraction

Private nume_ As Long
Private deno_ As Long

Public Property Get Numerator() As Long
    Numerator = nume_
End Property
Public Property Let Numerator(n As Long)
    nume_ = n
End Property

Public Property Get Denominator() As Long
    Denominator = deno_
End Property
Public Property Let Denominator(d As Long)
    deno_ = d
End Property

Public Sub IFraction_SetFraction(n As Long, d As Long)
    nume_ = n
    deno_ = d
End Sub

Public Function IFraction_Plus(x As IFraction) As IFraction
    IFraction_Plus.Numerator = nume_ * x.Denominator + deno_ * x.Numerator
    IFraction_Plus.Denominator = deno_ * x.Denominator
End Function

Private Function IFraction_toString() As String
    IFraction_toString = nume_ & " / " & deno_
End Function

ところがこれでは動かない。IFraction_Plusのところで、xはIFraction型なのでNumeratorやDenominatorのプロパティが使えない。かと言って

Public Function IFraction_Plus(x As Fraction) As IFraction

とxをFraction型で宣言したら、インターフェースでIFraction型で宣言したのと不整合だって叱られる。
ここで四苦八苦して、ようやく閃いたのが次の書きかた:

Public Function IFraction_Plus(ix As IFraction) As IFraction
    Dim x As Fraction, plus As Fraction
    Set x = ix
    Set plus = New Fraction
    
    plus.Numerator = nume_ * x.Denominator + deno_ * x.Numerator
    plus.Denominator = deno_ * x.Denominator

    Set IFraction_Plus = plus
End Function

いったんIFraction型で引数ixを取って、内部でFractiong型の別の変数xに格納し直した。
出力のほうも、最後に出力するIFraction型とは別にFraction型変数plusで計算をする。

とりあえずこれでちゃんと計算してるからいいのかな・・・?
あくまで自己流なので、正しいやり方が別にあるのかもしれないけど、これで進めてみる。