業務アプリのオブジェクト指向について への返答

投稿で使用できる特殊コードの説明。(別タブで開きます。)
本名は入力しないようにしましょう。
投稿した後で削除するときに使うパスワードです。返答があった後は削除できません。
返答する人が目安にします。相手が小学生か社会人かで返答の仕方も変わります。
最初の投稿が質問の場合、質問者が解決時にチェックしてください。(以降も追加書き込み・返信は可能です。)
※「過去ログ」について書くときはその過去ログのURLも書いてください。

以下の返答は逆順(新しい順)に並んでいます。

投稿者 たかくん  (社会人) 投稿日時 2012/5/14 13:02:35
YuOさん、大変丁寧に解説してくださりありがとうございます。
今の僕ではまだ理解が難しいですが何となくは解ります。
しっかり解読します。
思ったのは僕自身.Netのライブラリを知らなさすぎると思いました。
入門書に出てくるような基本クラスしか知りません。
なので後は発想に頼ったコーディングになってしまうんです。
それと業務とゲームはプログラムでも分けて考えたほうがよさそうですね。
「MVC」使えるようになればかなり幅が広がりますね。
それとあの例題は僕が作ってるゲームの中に実際ある処理を簡単に表現したものなんです。
コンピューターの思考をリアルタイムにGUIに伝えてベリファイ表示してる部分なんです。
ありがとうございます。
またよろしくお願いします。
投稿者 YuO  (社会人) 投稿日時 2012/5/14 12:25:26
> 申し訳ないですがM,とかVとかPなるものの意味から解らないのです。
> どれが処理にあたり、どれがGUIになるのでしょうか?

PMで大雑把に言うなら,
・見た目がV
・「見た目に付随する処理」や「Vのためのデータ」がP
・上記以外全てがM
です。
以前るきおさんが書かれていたPLはPに,BLはMに記述します。


> MODEL
> 主にデータの操作(取得,更新,削除など)を行うためリソース(DB,外部APIなど)にアクセスし処理を行う部分。
> VIEW
> クライアントにUIを返却する部分。画面デザインなどを担当する。

上記のVの表記はWebアプリケーション用のMVCあたりの表記だと思うのですが……。
UIを「返却する」といった考えはないですよ。UIそのものです。
あと,ステートレスなHTTPの世界と違って,クライアントアプリケーションはステートフルな世界です。
なので,Mの書き方も当然Webアプリケーションとは異なってきます。


> ではYuOさんなら僕の書いた例題はどう書かれるのですか?

このサイズではコードビハインドの方が楽なんですよね。
処理が小さすぎて,分離するコストが高くつきすぎます。
あえて分離するなら,単純にV-M構成にしてしまうでしょう。
# 特定の場合は文字を赤くする,といったビューロジックが不要であるため。

まずはModelから。
非常に長くなったため,ideoneの方に書いておきました → http://ideone.com/KBtyO
一応,WinFormsに依存しないプログラムにしているため,このModelはWPFでも使えるはずです。
注)文法チェックすらしていませんので,そのままではコンパイル不可の可能性があります。

次に,Form。
ProgressBar1.ValueをCurrentに,ProgressBar1.MaximumをMaxValueにデータバインドするように設定しておいて (DataSourceとしてModelDataBindings1ができているとして),
Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles MyBase.Load
        ModelDataBindings1.DataSource = New Model()
    End Sub
End Class
とだけ書きます。
デザイン画面での設定が面倒ですが,今回はビューロジックが無いため,コードビハインドは上記のみになります。
実際には,New Model()を直接BindingSource.DataSourceに代入するようなことはせずにフィールドにPやVMを持たせて,
ボタンクリック等でメソッド呼び出しを行うコードビハインドが必要になると思いますが。


> るきおさんはクラスとクラスをイベントで連携するのは正しい判断と言っておられますが・・・

依存関係を片方向にするためにイベントを使うのは正しいやり方です。
システムに組み込まれたObserverパターンがイベントです。

VはPが変化したことをイベントを通じて知ります。
このために特化したInterfaceがSystem.ComponentModel.INotifyPropertyChangedです。
また,.NET 1.0以来の方法で,「プロパティ名 + Changed」というイベントを用意して知らせることもできます。
どれも,「変化したこと」を知らせるイベントで,変化した値はPにアクセスして知ることになります。
# Pのプロパティをコントロールのプロパティに割り当てて,同一視するための方法がデータバインディング。


> >言い換えれば,MのインターフェースをV (またはC/P/VM) が知っていないといけないのです。
> これはMの処理をInterfaceクラスでVに提供するって事なのですか?

ここでいう「インターフェース」は言語としてのInterfaceではなく,一般名詞としてのインターフェースです。
Mをどういう風に扱う必要があるか,Vが知っていないといけない,と書いています。
投稿者 たかくん  (社会人) 投稿日時 2012/5/14 04:16:59
すいません、調べて解りました。

MODEL
主にデータの操作(取得,更新,削除など)を行うためリソース(DB,外部APIなど)にアクセスし処理を行う部分。

VIEW
クライアントにUIを返却する部分。画面デザインなどを担当する。

と書いてありました、Mは処理、VはGUI、ですね
ではYuOさんなら僕の書いた例題はどう書かれるのですか?
るきおさんはクラスとクラスをイベントで連携するのは正しい判断と言っておられますが・・・

>言い換えれば,MのインターフェースをV (またはC/P/VM) が知っていないといけないのです。

これはMの処理をInterfaceクラスでVに提供するって事なのですか?
あー、また混乱してきたです。
投稿者 たかくん  (社会人) 投稿日時 2012/5/14 03:22:07
おはよございます。
YuOさんありがとうございます。
申し訳ないですがM,とかVとかPなるものの意味から解らないのです。
どれが処理にあたり、どれがGUIになるのでしょうか?
もう少し噛み砕いていただけると助かります。
投稿者 YuO  (社会人) 投稿日時 2012/5/14 02:00:27
業務アプリとゲームでは,適切な方法が異なってきますが……。

業務アプリを前提に書きますが,そもそもViewはModelに依存します。
なぜなら,「そのプログラムがやりたいこと」はMに記述されているからです。
これは,MVC系のパターン (MVC/MVP/PM/MVVM etc.) 全てで共通です。
言い換えれば,MのインターフェースをV (またはC/P/VM) が知っていないといけないのです。

今回の場合,PMパターンを例にするとMとV+Pを分離して,MとV+Pを依存しないように,としたいようですが,それは無理な話です。
V+PはMに依存します。再度になりますが,「やりたいこと」がMにあるのですから。
MVC系のパターンにおいても,MをいかにしてVから独立させるか,が主題であって,MとVを相互に独立させることは主題になっていません。
# 「できないこと」がわかっているから。

純粋なVの開発であれば,ある程度Mから分離可能です。
非ドメインロジックの処理も,Mからある程度分離してP側に記述可能だと思います。
WPFなどはそういう思想になっていますね。


とりあえず,MVCやMVVMなどのパターンのとっかかりになりそうなサイトをいくつか挙げておきます。
・MVCモデルの問題点を解決するPMモデルとMVPモデル - GeekなNooblog
http://d.hatena.ne.jp/sona-zip/20110823/p1
Javaのサイトですが,図と文字だけでもよいかと。
あと,リンクの起点にもなります (GeekなNooblogさんの一連の記事は読んでみた方がよいかと)。
・MVVMパターンとイベント駆動開発、そしてMVC/MVP/PMパターンとの関係 – 何故MVVMなのか - the sea of fertility
http://ugaya40.net/wpf/mvvm-mvc-mvp-pm-eventdriven.html
Livetの開発で知られる尾上さんのサイトなので,MVVMパターン中心ですが,スライドの,特に各パターンの説明は分かり易いと思います。
投稿者 たかくん  (社会人) 投稿日時 2012/5/13 19:15:13
今晩は、今日は今ある疑問を例題にしてみました。
以下を見て下さい。

Public Class EventTest

    Private ProcessLoop As Timer = New Timer

    Public Event VelifyEvent(ByVal nowint As Integer, ByVal maxval As Integer)

    Public Sub New()
        Me.ProcessLoop.Interval = 1000
        Me.ProcessLoop.Start()
        AddHandler Me.ProcessLoop.Tick, AddressOf Me.Process
    End Sub

    Public Sub Process(ByVal sender As System.Object, ByVal e As System.EventArgs)
        For cnt As Integer = 0 To 1000
            RaiseEvent VelifyEvent(cnt, 1000)
        Next
    End Sub
End Class

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim Test As EventTest = New EventTest()
        AddHandler Test.VelifyEvent, AddressOf Me.VelifyEvent
    End Sub

    Private Sub VelifyEvent(ByVal nowint As Integer, ByVal maxval As Integer)
        If (nowint = 0 And maxval = 0) Then
            Me.ProgressBar1.Value = 0
            Me.ProgressBar1.Maximum = 0
        Else
            Me.ProgressBar1.Value = nowint
            Me.ProgressBar1.Maximum = maxval
        End If
        Me.ProgressBar1.Invalidate()
    End Sub

End Class

これは、処理クラスの動的な処理をFormクラスのメソッドと処理クラスのイベントで連携させたものですがFormをGUIとした時、処理クラスと切り離したいのですが、例えばGUIクラスと処理クラスを別々の人が担当していた場合、このような処理は仕様として処理クラスの人はGUIクラスの人にドキュメントとかで伝えてるのでしょうか?
処理クラスのイベントがどんなイベントか解らないとGUIクラスの処理も書きようがないと思うのですが
実際のグループ開発ではどのようにされてるんでしょうか?
人とゲームを共同で作るのですがこの辺の事が気になるのです。
よろしくお願いします。
投稿者 たかくん  (社会人) 投稿日時 2012/5/8 04:12:24
YuOさん、今晩は、返信できてなくてすみませんでした。
バインディングですか、まだ僕は使った事がないのでその時がきたらじっくり調べてみます。
それとプロではないのでMVVMというのが解らなかったですね。
バインディング機構なんだろうなと想像しますが・・・
僕はいつもはゲーム作りを通してオブジェクト指向を学んでます。
クラス間で動的なやりとりが多いので大変です。
またよろしくお願いしますね。
投稿者 YuO  (社会人) 投稿日時 2012/5/8 02:21:54
WinFormsでもMとVの分離はとりあえずしておきたい私は特殊……?
# 過去にコードビハインドがとても大きくなった上にViewクラスが引き回されて手を付けられなくなった経験有り。

たかくんさんは,WPF/SilverlightのM-V-VMなどの議論を追ってみるとよいかもしれませんね。
ViewとModelの責務の分離の話が出てきますし。
投稿者 たかくん  (社会人) 投稿日時 2012/5/7 23:42:25
今晩は、るきおさん、とくまさんありがとうございます。
そもそも分離にこだわるのはオブジェクトとは格クラスがお互いのリソースに深く干渉しないのが前提
で、そうする事によって保守がしやすく解りやすいプログラムになると思ってたので勉強しているのですが、こうして質問したりするとさらに頭が混乱するのが僕のようです。
るきおさんのコメントで少し楽になりました。
僕は追及してしまうたちなので自分の中でどこに線を引くかが下手なんですね。
一応目標は.NETみたいな独立したオブジェクトを作れるようになりたいんです。
やはりたくさん経験して自分の中に「これ」というスタイルを見出す事が大切だと痛感しました。
まだまだ悩みそうです。
お二人さん、ありがとうございました。
投稿者 とくま  (社会人) 投稿日時 2012/5/7 16:09:17
> オブジェクト指向ではGUIクラス(Form)と処理クラスは基本分離しますよね?
私はオブジェクト指向では分離しないのが基本だと思います。
TextBox の入力と、入力チェックを分けるかといえば、継承して内包してしまう
ほうがオブジェクト指向らしいと思います。
「入力チェック付き TextBox」という1つの新しいオブジェクトを作ります。

話題に上がっている分離については、別の概念だと思います。
> 処理クラスでデータを読み込んでいる最中ににリアルタイムにGUIクラスで
> ベリファイメーターを見せる
既存のクラスで言えば、FileStream クラスと ProgressBar クラスみたいな
話でしょうか。これは分離も何も、最初から別のオブジェクトです。
ここで「メーター付き処理クラス」を作るかと言えば、2つのオブジェクトが
連携する程度に納めると思います。

> ようは処理クラスからGUIクラスに働きかけたいのです。
> 僕は今までGUIクラスから処理クラスに問い合わせをして
> GUIのコントロールを操作するような事をしていました
それで事足りているなら必要ないのでは?GUI を止めたくないのであれば、
処理クラスを BackgroundWorker クラスに投げるのが王道では?
> こんな感じなのですがこれだとGUIの中で処理クラスと混同してしまいそうですし
何を言わんとしているのか分かりませんでした。

WEB ならその特性からも3層構造に分離する事を前提にすることも多い
と思いますし、そういった概念も有効な手段を生むと思いますが、
オブジェクト指向の話とは違ってきますからね。
投稿者 るきお  (社会人) 投稿日時 2012/5/7 13:01:55
>こんにちは、オブジェクト指向ではGUIクラス(Form)と処理クラスは基本分離しますよね?
オブジェクト指向だから分離させているわけではないと思います。
GUI部分をPL、処理クラスをBLと呼ぶとすると、
たかくんさんの指摘のようにBLとPLは密接にかかわることが多く、完全分離は困難です。
イベントで連携すると言う判断は正しいと思います。私はこれを疎結合と呼んでいます。

If 処理クラス.データプロパティ=新規 Then
    TextBox1.Enabled=false
EndIf

この処理もイベントプロシージャの中で行い、かつ処理クラスの状態をイベントの引数から取得するようすれば気にするほど混乱しないと思います。


実際には割り切ってPLとBLを分離しない実装もあります。
PLとBLを一応わけますが、PLの方にはBLに関する情報の取得や分岐もまぜます。
この場合でもさすがにBLの方にPLの制御はまぜないです。

要は何の目的でPLとBLを分けるかです。
初期投資を低コストに抑える目的であれば上記の混在型はありです。
保守費用まで見ても混在型が採用されるかもしれません。ケースバイケースです。
拡張性や多様なプラットフォームへの対応を目的としているのであればイベントレベルの連携にとどめる(疎結合)方策を採用することが多くなるでしょう。
投稿者 YuO  (社会人) 投稿日時 2012/5/6 04:16:52
個人的にはMVVMもどきの構成を作ってバインドさせることが多いです。
# 時間が無いとfat-VMになりますが……。

制限はあるものの,WinFormsでもINotifyPropertyChangedやIDataErrorInfoは利用可能です。
# IDataErrorInfoは.NET Fx 1.0から存在し,INotifyPropertyChangedは2.0から存在します。

なので,バインディング機構を使ってコントロールのプロパティを変更させることで,
GUI側での明示的なプロパティ変更コードはあまり必要がなくなります。


WPF/Silverlightと違い,
・ListViewやTreeViewでバインドできない
・コマンドバインディングができない
・バインディングの設定が調べにくい (プロパティタブのDataBindingsにまとまってしまう)
・IValueConverterのような機構がない
・PropertyChangedを拾ったタイミングでコンテキストが変わらない
などの違いがあるのは注意が必要です。
投稿者 たかくん  (社会人) 投稿日時 2012/5/5 11:20:11
こんにちは、オブジェクト指向ではGUIクラス(Form)と処理クラスは基本分離しますよね?
例えば処理クラスでデータを読み込んでいる最中ににリアルタイムにGUIクラスでベリファイメーター
を見せるようにするにはどんなプログラム技術がいるのでしょうか?
僕が思いつくものとしてはイベントで処理クラスとGUIクラスを繋ぐ位しか思いつかないのですが
ようは処理クラスからGUIクラスに働きかけたいのです。
僕は今までGUIクラスから処理クラスに問い合わせをしてGUIのコントロールを操作するような事をしていました、例えば

    If 処理クラス.データプロパティ=新規 Then
        TextBox1.Enabled=false
    EndIf

こんな感じなのですがこれだとGUIの中で処理クラスと混同してしまいそうですし、リアルタイムに処理クラスとGUIクラスをやりとりするのは不可能かと思います。
プロで業務アプリを作ってる人はどのようにされてるのでしょうか?
よろしくお願いします。