- PR -

VBにおいて動的にクラスを設定する方法

投稿者投稿内容
甕星
ぬし
会議室デビュー日: 2003/03/07
投稿数: 1185
お住まい・勤務地: 湖の見える丘の上
投稿日時: 2007-05-09 17:17
引用:

さとるさんの書き込み (2007-05-09 16:38) より:
 当初、eval関数を使用して動的にコードを作成することで対応しようと考えていたのですが、それが使えないことが午前中に発覚したため、大慌てでこちらに書き込みさせていただいた次第です。最初の書き込みで意味不明な箇所があったことをお詫びいたします。


evalって、JScript.NETにしかないんですよね。でもってHELPを見る限りJScript.NETのevalで望んでいる事が実現できそうなんですけど、クラスのインスタンスを生成できなかったんでしょうか?

#そのためにJScript.NETを採用したいとは思わないけど・・・・
masa
大ベテラン
会議室デビュー日: 2004/10/28
投稿数: 161
投稿日時: 2007-05-09 20:49
引用:

さとるさんの書き込み (2007-05-09 16:38) より:

 文字列介入の必要は、DBから動的に使用するクラスを引き出す仕組みだからです。
 クラスが持つ計算式はそれぞれが複雑で独立している上、今後とも追加していく可能性が濃厚である、さらに計算式を利用する人がユーザごとに違う状況であるため、今回のような仕様になったということです。




計算クラスの追加やメンテナンスをさとるさんが行うのでしょうか。
各使用者が派生クラスを作ってプラグインのように追加していくのでしょうか。

自由に計算クラスがどんどん増えていく状況ではなく、
システム側から必要時に提供していくのであれば
いわゆるファクトリークラスを用意したほうがよいような気もします。
インスタンス化処理自体はメソッドの中に隠蔽しておいたほうがよさそうですよね。

public class CalculatorFactory

 public function CreateCalculator(name as string) as clsSuperClass

  if ( name = "clsSubClassA" ) then
   return new clsSubClassA()
  else if ( name = "clsSubClassB" ) then
   return new clsSubClassB()
  else if ( name = "clsSubClassC" ) then
   return new clsSubClassC()
  else
   'それ以外の場合、例外を投げるか nothing を返すか
  end if

 end function

end class

文字列ではなく列挙体としてもよいかもしれませんね。
定数であることには変わりはありませんが。

public enum CalculationMode
  Unknown = 0,
  clsSubClassA = 1,
  clsSubClassB = 2,
  clsSubClassC = 3,
end enum

 public function CreateCalculator(mode as CalculationMode) as clsSuperClass

  switch ( mode )
   case CalculationMode.clsSubClassA:
    return new clsSubClassA()
   case CalculationMode.clsSubClassB:
    return new clsSubClassB()
   case CalculationMode.clsSubClassC:
    return new clsSubClassC()
   default:
    'それ以外の場合、例外を投げるか nothing を返すか
  end switch

  ※ VB.NET って select case だったかも。。




個人的には if の羅列が随時増えていくのはあまり好きではないので、

public class CalcuratorFactory

 public sub New()
  AddDefaultActivators()
 end sub

 private sub AddDefaultActivators()
  AddActivator(new SubClassAActivator())
  AddActivator(new SubClassBActivator())
  AddActivator(new SubClassCActivator())
 end sub

 'アクティベーターを登録
 public sub AddActivator(activator as ICalcuratorActivator)
  m_Activators.Add(activator.GetKey(), activator)
 end sub

 private m_Activators as HashTable '.NET2.0 ならジェネリックリスト

 '指定されたキーに対応する計算処理インスタンスを返す
 public function CreateCalculator(key as string) as clsSuperClass
  if ( m_Activators.ContainsKey(key) ) then
   return DirectCast(m_Activators(key) as ICalcuratorActivator).Activate()
  else
   'それ以外の場合、例外を投げるか nothing を返すか
  end if
 end function

end class

public interface ICalcuratorActivator
 '計算処理を一意に識別するキーを取得します。
 function GetKey() as string
 '計算処理インスタンスを生成します。
 function Activate() as clsSuperClass
end interface

で、アクティベーターを個々に作っていきます。

public class SubClassAActivator
 implements ICalcuratorActivator

 '計算処理を一意に識別するキーを取得します。
 public function GetKey() as string
  return "clsSubClassA"
 end function

 '計算処理インスタンスを生成します。
 function Activate() as clsSuperClass
  return new clsSubClassA()
 end function

end class

プログラムコードではこんな感じ

dim factory as CalcuratorFactory = new CalcuratorFactory()
dim calcrator as clsSuperClass = factory.CreateCalculator("clsCubClassA")


[ メッセージ編集済み 編集者: masa 編集日時 2007-05-09 21:05 ]

[ メッセージ編集済み 編集者: masa 編集日時 2007-05-09 21:06 ]
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2007-05-09 21:44
コード:

Public MustInherit Class clsSuperClass
Public Shared Function CreateInstance(ByVal className As String) As ClsSuperClass
Switch Case className
Case "clsSubClassA"
return New clsSubClassA()
(略)
Default
Throw New ApplicationException()
End Switch
End Function
(略)
End Class


とか?(VB.NET って、こんなだっけ?)

うげ。。。同じだった。Shared だっけ?C# でいう static つけるってことで。

[ メッセージ編集済み 編集者: Jitta 編集日時 2007-05-09 21:48 ]
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2007-05-09 23:15
赤ペン先生。

引用:

Jittaさんの書き込み (2007-05-09 21:44) より:

Switch Case className


Select Case

引用:

Default


Case Else

引用:

End Switch


End Select

本題の方ですが、現状の状態であれば変更が予定されていても、
リフレクションではなくコンパイル解決が図れる方法が望ましいと思います。
ポリモーフィズム (インターフェイスの型でロジックを書く) とか、Command パターンとか。

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2007-05-09 23:28
引用:

masaさんの書き込み (2007-05-09 20:49) より:
自由に計算クラスがどんどん増えていく状況ではなく、
システム側から必要時に提供していくのであれば
いわゆるファクトリークラスを用意したほうがよいような気もします。


同意です。

引用:

文字列ではなく列挙体としてもよいかもしれませんね。


入力元がVB.NETのコードなら同意ですが、今回はDBから取得した文字列ということですので、
列挙体にする必要はないと思います。

引用:

個人的には if の羅列が随時増えていくのはあまり好きではないので、


Jittaさんが書いてらっしゃるように、Select CaseはStringに対しても使えますよ。

引用:

Jittaさんの書き込み (2007-05-09 21:44) より:
コード:
Public MustInherit Class clsSuperClass
    Public Shared Function CreateInstance(ByVal className As String) As ClsSuperClass




ファクトリパターンに従うのなら、clsSuperClassの静的メソッドにするのではなく、
別途ファクトリクラスを作った方が良いのではないでしょうか?

いずれにしろ、DBに登録する文字列が必ずしもクラス名でなくて良くなるので、
DBテーブルのカラムサイズを小さくできるとか、
より分かりやすいキーを使えるなどのメリットもあると思います。
masa
大ベテラン
会議室デビュー日: 2004/10/28
投稿数: 161
投稿日時: 2007-05-10 01:14
引用:

ぼのぼのさんの書き込み (2007-05-09 23:28) より:

入力元がVB.NETのコードなら同意ですが、今回はDBから取得した文字列ということですので、
列挙体にする必要はないと思います。



もし、クラス名をDBに格納するという案を考えている段階であれば、
列挙体を定義してそれを数値化した値をDBに格納する手もあるかな、と。
文字列はあくまでも文字列ですが、
列挙体にすれば「計算処理を表す値」である保証ができます。

列挙値を追加した場合、列挙体を含むアセンブリをリビルドしなおす必要はありますが、
計算処理クラスも同じアセンブリに定義するとすればこれも問題にはならないですし。


引用:


Jittaさんが書いてらっしゃるように、Select CaseはStringに対しても使えますよ。




個人的には case がたくさん続くのもあまり好きではないですね。
計算処理インスタンスの生成が単純にコンストラクタを呼べば終わり、
とならないものも出てくるかもしれません。



ところで、さとるさん。
スーパークラスの代わりにインターフェースを使って汎用化してはいかがでしょうか。

ICalcrator インターフェース
 計算処理に必要な最低限のメソッド・プロパティを定義
 ↑
( implements )
 |
clsSuperClass クラス
 ↑
( inherits )
 |
clsSubClassA クラス
clsSubClassB クラス
clsSubClassC クラス

CreateCalcrator のようなメソッドからは ICalcrator を返すようにします。
各アプリケーションコードで汎用的に扱う点においては、
ICalcrator も clsSuperClass もそれほど変わりません。

dim calcrator as ICalcrator = factory.CreateCalcrator("キー")
calcrator.Calcrate(引数)


処理が複雑になってしまっているそうですが、
いろいろな計算処理のロジックを clsSuperClass に詰め込んでいたりするのが原因でしたら、
インターフェースを基底にすることですっきりするかもしれません。

clsSuperClass を継承していなくてもよくなりますから、
別のスーパークラスを用意することも可能になります。

例えば複数の計算処理を順番に実行する計算処理クラスのような、
全く実装のことなる計算処理も作りやすいです。

public class MultiCalcrator
 implements ICalcrator

 public sub new(calcrators as ICalcrator())
  if ( not calcrators is nothing ) then
   m_Calcrators = Directcast(m_Calcrators.Clone(), ICalcrator())
  end if
 end sub

 private m_Calcrators as ICalcrator()

 '計算処理
 public function calc(result as clsCalcResultClass) as boolean
  implements ICalcrator.calc

  if ( m_Calcrators == null ) then
   '例外をスロー、または何もせずに false を返すなど
  end if

  dim ret as boolean = true

  for i as integer = 0 to m_Calcrators.Length - 1
   if ( not m_Calcrators(i).calc(result) ) then
    if ( ret ) then ret = false
   end if
  next

  return ret

 end function

end class




[ メッセージ編集済み 編集者: masa 編集日時 2007-05-10 01:16 ]
うにくま
ベテラン
会議室デビュー日: 2005/11/05
投稿数: 82
投稿日時: 2007-05-10 01:55
引用:

ぼのぼのさんの書き込み (2007-05-09 23:28) より:

入力元がVB.NETのコードなら同意ですが、今回はDBから取得した文字列ということですので、
列挙体にする必要はないと思います。


引用:

masaさんの書き込み (2007-05-10 01:14) より:

もし、クラス名をDBに格納するという案を考えている段階であれば、
列挙体を定義してそれを数値化した値をDBに格納する手もあるかな、と。


の部分についてだけですが、Enum.Parseメソッドを使用する方法もあります。
この方法であれば、データベースには文字列で登録でき、プログラムでは列挙型を使用できます。

コード:
Public Enum CalculationMode
    Unknown = 0
    clsSubClassA = 1
    clsSubClassB = 2
    clsSubClassC = 3
End Enum

Dim mode As CalculationMode = DirectCast([Enum].Parse(GetType(CalculationMode), "clsSubClassB"), CalculationMode)

Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2007-05-14 22:03
引用:

ぼのぼのさんの書き込み (2007-05-09 23:28) より:
ファクトリパターンに従うのなら、clsSuperClassの静的メソッドにするのではなく、
別途ファクトリクラスを作った方が良いのではないでしょうか?


 すみません、デザイン パターンは便利だと思う一方で、何で?と思うこともあります。
ここで、別途ファクトリ クラスを作ることの利便性を教えてください。

 一応、検索<google.com>してみましたが、ファクトリ クラスをなぜ分けるのか、説明されているものに行き当たりませんでした。
唯一?ITpro の連載<nikkeibp.co.jp>で、次のように書いてありました。
引用:

その名もズバリ「Factory Method(工場メソッド)」というパターンは、クラスをnewして返すメソッドを用意するというものです。クラスのメンバの中に、他のクラスのオブジェクトを返すメソッドがあるのです。何でそんなメソッドが必要なのかと言うと、一緒に使ってほしいオブジェクトだからです。


なるほど、一緒に使う必要があるオブジェクトを作成する為というのは肯けます。
しかし、このスレッドのお題では一緒に使う必要があるオブジェクトは存在しません。よって、別のクラスにするメリットはないと思います。
 また、同じページの最後の言葉も気になります。
引用:

GoFデザインパターンに示された通りでなければいけない、という決まりはないのですから。


確かに、GoFパターンは、多くの事例を研究して取り出したパターンではあるでしょう。しかし、この言葉通り、示されている通りである必要は無いと思います。
現に検索結果には、このような言葉もあります。
引用:

サルでもわかる 逆引きデザインパターン<nulab.co.jp> より:
GoFのデザインパターンには、ファクトリメソッドパターン、アブストラクトファクトリパターンの2つが紹介されているだけで、ファクトリパターンは紹介されていません。 しかし実際に用途が多いのは、1つのファクトリが条件によりオブジェクトの生成を変更するシンプルなファクトリパターンですので、ここで解説しておきます。



 このようなわけで、別のクラスを作成する必要を感じません。ですので、「別途ファクトリクラスを作った方が良いのでは」とお考えになる理由を教えてください。

 私が考えている、GoF パターンにしたがわないことによるメリットを挙げます。
* 使用するクラスが少なくなり、クラス図の見通しが良くなる。
* clsSuperClass を継承したものであることがわかりやすい。
* GoF パターンではファクトリ クラスのインスタンスを生成し、さらに作りたいオブジェクトを生成している。これにより、オブジェクト生成の時間が1回余分にかかる。
* 関連して、検索結果のページで挙げられている例では、ファクトリ クラスのオブジェクトは、本当に必要なオブジェクトを生成するためにのみ必要でその前後では不要である。また、本当に必要なオブジェクトを生成するためにファクトリをインスタンス化する必要もなさそう。

クラス図描いたの忘れてた。


[ メッセージ編集済み 編集者: Jitta 編集日時 2007-05-14 22:12 ]

[ メッセージ編集済み 編集者: Jitta 編集日時 2007-05-14 22:13 ]

スキルアップ/キャリアアップ(JOB@IT)