none
参照渡しでフォームを受け取る汎用クラスの、引数の型 RRS feed

  • 質問

  • お世話になります。

    今日は自作したクラスを汎用的に使う方法について質問です。


    あるクラスを設計の異なる別のフォームから汎用的に使う場合、
    フォームからクラスへ参照渡しするときの型はどうすればいのでしょうか?


    あるプロジェクトに以下があります。
    Windows Application
    .NET 2.0
    Vs2010
    です。
    myForm1
    myForm2
    myClass

    ------------------
    myForm1

    myForm2
    はコントロールやプロパティの違う別物のフォームです。
    ただし両者とも
    「pMsg」プロパティをもち、
    「GetMsg」メソットと
    「MsgDsp」メソッドを持ちます。

    public Sub GetMsg

     Dim clsMyClass As New myClass
     clsMyClass.RunMsg(Me)

    End Sub

    Public Sub MsgDsp

     MessageBox.Show(pMsg)

    End Sub

    ------------------
    myClassは、RunMsgメソッドをもちます。
    引数として呼び出し元のフォームを参照渡しで受け取ります。
    Sub RunMsg(frm as XXX)

     frm.pMsg="こんにちは"
     frm.MsgDsp

    End Sub

    ------------------

    ポイントは RunMsgの引数(XXX)の部分です。

    ・「myForm1」にすると「myForm2」から使えなくなります。
    ・「Form」にすると、pMsg,GetMsgが無いのエラーになります。
    ・Objectにすれば動きますが、Form以外もOKになってしまいます。

    →「myClass」を使えるのがForm限定にしたいのですが、どうすればいいのでしょうか。

     

     

    2010年8月25日 7:59

回答

  • >本件の場合でも「独立性」は失われているというのでしょうか?

     

    今回、myClassはメッセージを管理及び設定するクラスと解釈しています。

    詳細なクラスのコードはわかりませんので推測を含みますが、

     

    myClassで引数のフォームによってメッセージを判定等している?

    frm.pMsg="こんにちは" の箇所

    この場合は、フォームが追加されるたびに何か考慮が必要になる可能性が

    あるので、そもそも各フォームにて行うべきものだと思われます。

    引数に設定されるフォームが増えると同時にmyClassにも対応が必要となる

    ならば、これは独立性が損なわれています。

    引数を意識することなく、一連の処理を行うようにすることで独立性が高まります。

     

    ②再利用性

    今回の場合であれば、引数型としてインターフェイス又は継承親フォームを渡すと

    思われますが、myClassは、例えば別のプロジェクトや別種類のフォームについて

    は利用できません。

    メッセージを管理するクラスであるのに、インターフェイスや継承親フォームが必要

    となってしまうため、このクラスを利用する時には、このインターフェイスや継承親

    フォームもセットで利用することになってしまいます。

    インターフェイスや継承親フォームから見ても、myClassがなければ使えない物と

    なってしまいます。

     

    行っているコードを見るに、単にフォーム1とフォーム2とで共通するコードを別の

    myClassにもっていったように思えますので、親フォームを作成し、そこにmyClass

    で行っているコードを書く方がよいかなと思いました。

    こうすることで、親フォームにはメッセージを管理するコードがあり、それを継承する

    子フォームはその内容を継承するため、同じような動きが期待できると思います。

    この場合、別のクラスなどに依存はしなくなるため、独立性があるといえると思います。

    2010年8月26日 1:08

すべての返信

  • Interface を使うのがいいと思います。

     ・「pMsg」プロパティと「GetMsg」「MsgDsp」メソッドを持つ Interface を定義する。(myInterface であるとします)
     ・myForm1 と myForm2 が myInterface を実装することを宣言する。(Implements キーワード)
     ・myForm1 と myForm2 に myInterface のプロパティとメソッドを実装する。
     ・myClass の RunMsg の XXX のところは myInterface にする。

    2010年8月25日 8:30
  • ぱっと思いつくいたのはインターフェースの利用です。

    ImyForm みたいな名前のインターフェースを作成し、
    myForm1 myForm2 に継承させます。

    ただし、ImyForm を渡すという仕様は
    オブジェクト指向プログラミングの作法上
    はよくなさそうです。
    例えば、将来 ImyForm のメソッドが増えると、
    余計なメソッドまで RunMsg に渡すことになってしまいます。

    ではどうすればいいかというと、
    デリゲートを利用して、ImyForm 丸ごとではなく
    メソッドを引数として渡すような手があります。
    興味があったらこちらも調べてみてください。

    追記:「ImyForm を渡すという仕様はオブジェクト指向プログラミングの作法上はよくなさそうです。」
    と書きましたが、呼ばれる側の自作クラスやフォームクラスの仕様(関係性)によっては
    問題が無い場合もあるかもしれません。

    例えば、フォームのメソッド(インターフェースのメソッド)が増えるに従い、
    呼ばれる側の自作クラスの処理も増やさなければならない場合などです。
    “注意しなきゃいけないポイントがある”くらいに捉えていただければ幸いです。

    • 編集済み anningo 2010年8月25日 8:45 追記
    2010年8月25日 8:34
  • anningo さんの返信についてですけど、
    私は特に作法的に問題はなさそうに思いました。

    > 例えば、将来 ImyForm のメソッドが増えると、
    > 余計なメソッドまで RunMsg に渡すことになってしまいます。

    と書かれましたが、
    その余計なメソッドまで同じインターフェイスに定義しなければよいと思います。

    ※具体的な手順を書いていたら、お二人に先を越されてしまったので、
     矛先を変えてしまいました。(^^;
     で、これを返信しようとしたら追記のフォローに先を・・・。でも念のため書かせてもらいます。。。

    追記:
    実は ImyForm という名前も例としても良くないと思いました。
    というのは、自作フォームが標準的に持つ機能群をすべて1つのインターフェイスで定義してしまうような場合ではそのような名前になってしまうかもしれませんが、本来は個々の機能ごとにその特徴を示すインターフェイス名を付けて分けるべきと思うからです。
    そのようにすることで、必然的に「余計なメソッド」が入り込む余地がなくなると考えます。
    些細なことに対して批判っぽい内容の返信ですみません。

    • 編集済み TH01 2010年8月25日 9:11 ほんの少し追記の文章を変更
    2010年8月25日 8:55
  • 他には、「pMsg」プロパティ、「GetMsg」メソッド、「MsgDsp」メソッドを持つ親フォームを作り

    その親を継承したフォームとしてmyForm1とmyForm2を作成し、それぞれのフォームで各

    メソッドの処理がことなる場合はオーバーライドし、RunMsgの引数型を親フォームにするとか。

     

    ただ、フォーム自身を渡してフォームの内容を操作するようなクラスを定義するのは、

    クラスの独立性が著しく損なわれるため、あまりおすすめはしない設計です。

    2010年8月25日 9:06
  • 私も少しに気になったところを・・・・ (^^;

    ImyForm みたいな名前のインターフェースを作成し、

     ネーミング的にはIMyFormが一般的でしょう。パスカル記法の頭に大文字のIを付けます。

    ただし、ImyForm を渡すという仕様は
    オブジェクト指向プログラミングの作法上
    はよくなさそうです。

    インターフェースというのはインスタンスをインターフェース型に変換して渡すものですから、 何を意味されているのかはっきりわかりませんでした。「ImyForm を渡す」というのも、正確には「ImyForm型としてインスタンス を渡す」という意味だと思いますので補足しておきます。

    例えば、将来 ImyForm のメソッドが増えると、
    余計なメソッドまで RunMsg に渡すことになってしまいます。

     ImyFormに必要なメソッドではなく、余計なメソッドが増える設計は再考すべきでしょう。この場合、インターフェースを継承したインターフェースを新しく作成するなどしても良いと思います。
     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2010年8月25日 14:19
    モデレータ
  • honefaiさん

    ご回答ありがとうございます。

    心から感謝申し上げます。

     

    ところで

    >ただ、フォーム自身を渡してフォームの内容を操作するようなクラスを定義するのは、

    >クラスの独立性が著しく損なわれるため、あまりおすすめはしない設計です。

    の部分で質問です。

    たしかに、myClassが、呼び出し元のフォームmyForm1/myForm2をRunMsgメソッドを通じて「操作」していますが、

    実際に操作しているのはmyForm1/myForm2自身がもつMsgDspメソッドです。

    myClassがmyForm1/myForm2のコントロールを「直接」動かしたら問題はありそうですが、

    本件の場合でも「独立性」は失われているというのでしょうか?

    2010年8月25日 14:28
  • 引き続き少しに気になったところを・・・・ (^^;

    実は ImyForm という名前も例としても良くないと思いました。
    というのは、自作フォームが標準的に持つ機能群をすべて1つのインターフェイスで定義してしまうような場合ではそのような名前になってしまうかもしれませんが、本来は個々の機能ごとにその特徴を示すインターフェイス名を付けて分けるべきと思うからです。

    myFormという機能らしくない名前が良くないと思われたようですが、私は特に良くないとは思いませんでした。私が考える本来のインターフェースの意味は、あらゆる型をインターフェースという型に変換して、そのインターフェースを経由することによってタイプセーフにあらゆる型にアクセスできることだと思うからです。ですから、自作のクラスはImyFormで表されるmyFormという私専用のフォームしか受け取らないんだぞっていうのは全然有りだと思うのです。
    でも、言われようとしていることはわかる気がします・・・、、ImyFormではなく、機能的な面を重視したIMessageDisplayというような名前の方が素敵かもしれません。ただ、ImyFormという名前が即ダメというわけではないと思うのです。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2010年8月25日 14:38
    モデレータ
  • TH01さんから引用
    > > 例えば、将来 ImyForm のメソッドが増えると、
    > > 余計なメソッドまで RunMsg に渡すことになってしまいます。
    > と書かれましたが、
    > その余計なメソッドまで同じインターフェイスに定義しなければよいと思います。

    メソッド毎(引数の組み合わせ毎)にインターフェースを用意してしまうような
    実装を恐れました。
    実際には、仕様がかなり複雑になったとしても、
    意味的にまとまりのあるインタフェース数個にまとめられるのかもしれません。

    # ここらへんは、スレ主さんがインターフェースの実装に
    # 慣れてから考えてもらったほうがいいかもです。

    TH01さんから引用
    > 追記:
    > 実は ImyForm という名前も例としても良くないと思いました。
    > というのは、自作フォームが標準的に持つ機能群をすべて1つのインターフェイスで定義してしまうような場合ではそのような名前になってしまうかもしれませんが、本来は個々の機能ごとにその特徴を示すインターフェイス名を付けて分けるべきと思うからです。
    > そのようにすることで、必然的に「余計なメソッド」が入り込む余地がなくなると考えます。
    > 些細なことに対して批判っぽい内容の返信ですみません。

    ImyForm ていうのは良くなかったです^^;
    “些細なこと”とは思いません。なんとかは細部に宿るともいいますし
    ネーミングは設計と同じか、それ以上に大事なことと感じてます。
    ご指摘ありがとうございました。

    honefaiさんから引用
    > ただ、フォーム自身を渡してフォームの内容を操作するようなクラスを定義するのは、
    > クラスの独立性が著しく損なわれるため、あまりおすすめはしない設計です。

    そもそも気になるのはここです。
    どうせなら、(これも honefaiさんが書かれていることですが)
    親フォームを作り、RunMsg メソッドは親フォームのメソッドにする
    というのもありかもしれません。

    # この返信は trapemiyaさんの返信を拝見する前に書いたものです。
    # 私にとってはだいぶ話が難しくなっているような気がしますが
    # また返信させていただきたいと思います^^;

    • 編集済み anningo 2010年8月25日 15:03 最後の# 3行追加
    2010年8月25日 14:47
  • >本件の場合でも「独立性」は失われているというのでしょうか?

     

    今回、myClassはメッセージを管理及び設定するクラスと解釈しています。

    詳細なクラスのコードはわかりませんので推測を含みますが、

     

    myClassで引数のフォームによってメッセージを判定等している?

    frm.pMsg="こんにちは" の箇所

    この場合は、フォームが追加されるたびに何か考慮が必要になる可能性が

    あるので、そもそも各フォームにて行うべきものだと思われます。

    引数に設定されるフォームが増えると同時にmyClassにも対応が必要となる

    ならば、これは独立性が損なわれています。

    引数を意識することなく、一連の処理を行うようにすることで独立性が高まります。

     

    ②再利用性

    今回の場合であれば、引数型としてインターフェイス又は継承親フォームを渡すと

    思われますが、myClassは、例えば別のプロジェクトや別種類のフォームについて

    は利用できません。

    メッセージを管理するクラスであるのに、インターフェイスや継承親フォームが必要

    となってしまうため、このクラスを利用する時には、このインターフェイスや継承親

    フォームもセットで利用することになってしまいます。

    インターフェイスや継承親フォームから見ても、myClassがなければ使えない物と

    なってしまいます。

     

    行っているコードを見るに、単にフォーム1とフォーム2とで共通するコードを別の

    myClassにもっていったように思えますので、親フォームを作成し、そこにmyClass

    で行っているコードを書く方がよいかなと思いました。

    こうすることで、親フォームにはメッセージを管理するコードがあり、それを継承する

    子フォームはその内容を継承するため、同じような動きが期待できると思います。

    この場合、別のクラスなどに依存はしなくなるため、独立性があるといえると思います。

    2010年8月26日 1:08
  • 引用は全て trapemiyaさんからです。

    私も少しに気になったところを・・・・ (^^;

    ImyForm みたいな名前のインターフェースを作成し、

     ネーミング的にはIMyFormが一般的でしょう。パスカル記法の頭に大文字のIを付けます。

     ですね(アルファペットの大&小文字って便利……)

    ただし、ImyForm を渡すという仕様は
    オブジェクト指向プログラミングの作法上
    はよくなさそうです。

    インターフェースというのはインスタンスをインターフェース型に変換して渡すものですから、 何を意味されているのかはっきりわかりませんでした。「ImyForm を渡す」というのも、正確には「ImyForm型としてインスタンス を渡す」という意味だと思いますので補足しておきます。

    補足ありがとうございます。
    実行時ではなく設計時(コーディング時)の表現として
    「ImyForm を渡す」と書きました。

    例えば、将来 ImyForm のメソッドが増えると、
    余計なメソッドまで RunMsg に渡すことになってしまいます。

     ImyFormに必要なメソッドではなく、余計なメソッドが増える設計は再考すべきでしょう。この場合、インターフェースを継承したインターフェースを新しく作成するなどしても良いと思います。

    IMyForm は抽象基底クラスと考えていました。
    これが間違いの元。
    少なくとも今回のテーマでは、抽象基底クラスではない
    インターフェース(trapemiyaさんが書いた IMessageDisplay といったもの)で例示するべきだった気がしています。

    totojoさんの返信でも「myInterface」となっていました。

    2010年8月26日 2:52
  • 少なくとも今回のテーマでは、抽象基底クラスではない
    インターフェース(trapemiyaさんが書いた IMessageDisplay といったもの)で例示するべきだった気がしています。
    そうですね。単純なインターフェースの適用で十分だと思います。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2010年8月26日 4:17
    モデレータ
  • 私の返信内容については、trapemiya さんに書いていただいた通りだと思いました。

    インターフェイスの目的が、
    「自作フォームの共通機能」を公開するものなのか、
    「メッセージを表示する機能」を公開するものなのかで変わりますね。
    私の返信では前者を否定っぽく書いてましたが、ありだと思ってます。

    ただ、次のページに該当する場合は、再検討が必要ですけど。

    空のインターフェイスを作成しないでください
    http://msdn.microsoft.com/ja-jp/library/ms182128(VS.90).aspx

    anningo さん
    > ネーミングは設計と同じか、それ以上に大事なことと感じてます。

    私もです。
    実は最初から、アースワームさんのクラス名や public なプロパティ名が小文字で始まっている点も、気になって仕方がありません。(^^;
    (MyClass 自体予約後なので実際の名前はもちろん違うんでしょうけど)

    trapemiya さん
    > そうですね。単純なインターフェースの適用で十分だと思います。

    タイトルのご質問への直接的なご提案としては、インターフェイスが最適だと私は考えます。
    しかし改めて考えると、目的としては anningo さんが書かれた「委譲」が近いかもしれないとも思いました。
    ただ、インターフェイスまたは委譲の場合、メッセージの内容がフォームに依存するかどうかも判断材料として重要で、依存する場合にはフォーム自身に機能を持たせるために継承で実現するのもありなのかもしれないと思いました(私はこれを今思ったのですが、honefai さんは最初から依存する可能性も考慮されてますね)。

    なんか玉虫色な話ですが、現在の情報では具体的な利用場面がイメージできず、どれもありえると思ったのでこうなりました。

    ちなみに、良いかどうかは別にして、委譲だと以下のようになります。
    メッセージ表示のトリガーが自身のフォームでなければ、イベントをインターフェイスで公開することになるかなと思います。

    Public Class MyForm1
        Public Sub GetMsg()
            MyAClass.RunMsg(AddressOf MsgDsp)
        End Sub

        Private _msg As String
        Public Property pMsg() As String
            Get
                Return _msg
            End Get
            Set(ByVal value As String)
                _msg = value
            End Set
        End Property

        Public Sub MsgDsp()
            MessageBox.Show(Me.pMsg)
        End Sub

        Public Sub MsgDsp(ByVal msg As String)
            Me.pMsg = msg
            MsgDsp()
        End Sub
    End Class

    Public Delegate Sub DisplayMessageDelegate(ByVal msg As String)

    Public Class MyAClass
        Public Shared Sub RunMsg(ByVal method As DisplayMessageDelegate)
            method.Invoke("こんにちは")
        End Sub
    End Class

    ※honefai さんが書かれた内容にはツッコミできる隙がなかったので、前から思っている別なこと書かせていただきます。
    フォントサイズを固定指定されているので、私の環境ではほんの少しだけ読みづらいです。。(^^;

    2010年8月26日 5:22
  • みなさんありがとうございます。

    インターフェースについて、まだわかっていないので勉強します。

    正直みなさんのお話されている内容は(レベル的に)ほとんどわかりません。。。

    すみません。

    一生懸命勉強します。

     

    2010年8月30日 1:21