質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
87.20%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

解決済

Prism環境下でのMVVMの質問

HiraKazu1124
HiraKazu1124

総合スコア282

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

1回答

0評価

1クリップ

373閲覧

投稿2022/01/19 04:48

導入

今までPrismを使わずにWPFXamarin.Formsで開発を行ってきましたが、今回初めてPrismを導入することにして現在学習中です。
ちなみに、ReactivePropertyライブラリも使っています。

現在.NET5.0 + WPFでWindowsアプリを開発しているのですが、疑問点が出てきて質問しました(DIの感覚がつかめていないのかな?)。

ひとまず現状私が理解している内容を書きます。
Prism.WPF(Full App)テンプレートでソリューション(名前はSampleApp)を作成すると...

  1. SampleApp.Modules.ModuleName
  2. SampleApp.Servises.Interfaces
  3. SampleApp.Services
  4. SampleApp.Modules.ModuleName.Tests
  5. SampleApp
  6. SampleApp.Core

という感じでプロジェクトが作成されますよね?(ModuleNameの名前はいつも変更してます^^;)
それぞれのプロジェクトの役割をある程度学習したところなのですが...

  1. 主にユーザコントロールの作成(リージョンの作成)
  2. Model部のインターフェイスを定義
  3. Model部の実装の作成
  4. 1の単体テスト
  5. View(主にWindow)とViewModelの定義、DIの定義などアプリ本体の処理を作成
  6. 5で使われる基底クラス(ViewModelBaseなど)やRegion名をすべて定数で定義するなど(5全体で使われるクラスの定義?)

上記のような感じで理解してます(1番目の箇条書きと番号が対応しています)。
※もしこの時点で間違っている理解があればご指摘いただければ嬉しいです^^

※以下SampleApp.は省略します。
現在一番わからないのが、Model部の処理をServicesが担当し、Model部のプロパティやメソッドの公開をServices.Interfacesが担当することです。
もちろんDIでViewModelのコンストラクタに注入できるのはわかるのですが...

「ViewModelがModelクラスをフィールドやプロパティで持つのは良くない」
「あくまで、Services.Interfacesのインターフェイスを定義して、それを使うべき」
...という記述をどこかで読んでそこら辺からこんがらがっています(-_-;)
(この記述自体が間違っているのならある程度すっきりします^^)

ViewModelでModelのクラスを持つことができないのはかなり不便ではないでしょうか?
もちろん疎結合にしたいのはわかりますが、一番疑問に感じるのは列挙体の定義とかですかね...

質問のサンプルコード

prismを使っていなかった頃の簡単なサンプルを書きますので、Prism環境下ならどうなるのかご教授いただけませんでしょうか。
ViewとViewModelのバインディングは大丈夫なのでViewの実装は割愛させていただきます。
ほかにも複雑な処理は割愛しています。
(質問ページに直接入力なのでタイポあったらすいません💦)

「アプリの内容」
入力欄に動物の名前を入力すると、名前を検索して「~類」と種類を表示するアプリ

「モデル部」

C#

// AnimalType.cs public enum AnimalType { Honyu, // 哺乳類 Tyou, // 鳥類 Hatyu, // 爬虫類 Ryousei, // 両生類 Gyo, // 魚類 } // Animal.cs public class Animal { public string Name {get; private set;} public AnimalType Kind {get; private set;} public Animal(string name, AnimalType kind) { Name = name; Kind = kind; } } // SearchAnimalService.cs // 疑問点3 public class SearchAnimalService { private SearchAnimalService _instance; public SearchAnimalServicce Default => _instance ?? new SearchAnimalService(); public SearchAnimalService() { // DBへの接続などの情報を定義(本来は別のクラスでDBとの接続定義を行う処理を書くと思いますが今回はここで...) } // 名前をDBから検索(失敗したらnull) public async Task<Animal> Search(string name) { Animal result = null; // await (DBから名前が一致する動物を検索) return result; } }

「ViewModel部」

C#

// MainWindowViewModel.cs public class MainWindowViewModel { // 本当はINotifyPropertyChangedの空実装を追加しています // ViewにおいてあるTextBoxとバインド public ReactiveProperty<string> SearchName {get;} = new(); // 疑問点2 // ViewにおいてあるTextBlockとバインド public ReactiveProperty<string> AnimalTypeString {get;} = new(); // ViewにおいてあるButtonのCommandとバインド public AsyncReactiveCommand SearchCommand {get;} = new(); public MainWindowViewModel() { SearchCommand.Subscribe(OnSearch); } private async Task OnSearch() { Animal result = await SearchAnimalService.Default.Search(SearchName.Value); if (result == null) return; AnimalTypeString.Value = AnimalTypeToString(Animal.Kind); } // 疑問点1 private static string AnimalTypeToString(AnimalType kind) { return kind switch { AnimalType.Honyu => "哺乳類", AnimalType.Tyou => "鳥類", AnimalType.Hatyu => "爬虫類", AnimalType.Ryousei => "両生類", AnimalType.Gyo => "魚類", _ => throw new NotImplementedException() // 理論上ここには到達しない }; } }

サンプルに対しての質問

「疑問点1」
一番の疑問点です。
「ViewModelはModelで定義されたものを使ってはいけない、インターフェイスを介すべき」
ことに従うなら、AnimalTypeを使っていること自体間違いになりますよね?
今まではそんなこと気にせずに使っていましたが、実際のところどうなんでしょうか?

AnimalTypeをViewModelに実装はできませんし、Modelで「哺乳類」など表示するための文字列を出すのもおかしいと思いますし...
(この例は列挙体から文字列に変換ですが、列挙体から文字色に変換することなども考えるとModel部で文字色を返すメソッドを作るのはかなりおかしいですよね)
それともModelとViewModelの間に別の層を用意するのでしょうか?(訳が分からない世界に迷い込みそう...)

「疑問点2」
今回はシンプルな処理なのでAnimalTypeStringとしてstring型のプロパティを定義しています。
しかし、Animalクラスにどんどんプロパティが追加され、写真も表示したい、wikiへのリンクを表示したい...
などとなったときに、ReactiveProperty<Animal>というように定義したくなりませんでしょうか?
別にそれでもViewとのバインディングには問題ないですし...
(今回の例では必要ないですが、Animalクラスに変更通知機能を実装することもあると思います。)

それとも、そういう場合はAnimalViewModelを作ってAnimalクラスをラップするのがよいのでしょうか?
(でも、そんなことしてもViewModelがModelクラスを使っていることは事実ですよね?)

または、AnimalInterfaceなるものを作って、DIでMainWindowViewModelのコンストラクタに注入するのでしょうか?
(これを使っても疑問点1のような列挙体の問題は解決しませんが...)
(それとも列挙体を使わずに、定数使いまくるのか?複雑になると名前付けが大変...orz)

「疑問点3」
これは疑問というより確認ですが、このようなDBにアクセスするような機能をDIで実装するのが自然なイメージで認識してます。
DBとのやり取りを行うインターフェイスを作成し、それをDIで実装すれば単体テストなども楽に書けますよね?

まぁ...作っている環境は個人開発なので、そこまでこだわらなくてもよいのかもしれませんが💦
なんだか気持ち悪かったので質問しました。
長文になりましたが、よろしくお願いします。

良い質問の評価を上げる

以下のような質問は評価を上げましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

  • プログラミングに関係のない質問
  • やってほしいことだけを記載した丸投げの質問
  • 問題・課題が含まれていない質問
  • 意図的に内容が抹消された質問
  • 過去に投稿した質問と同じ内容の質問
  • 広告と受け取られるような投稿

評価を下げると、トップページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

Zuishin

2022/01/19 04:51

> 「ViewModelはModelで定義されたものを使ってはいけない、インターフェイスを介すべき」 デマです。
HiraKazu1124

2022/01/19 07:04

やはりそうなんですね...(-_-;) 自分でもおかしいなぁ...とは思いつつ、DIを使った開発自体が初めてだったもので、「そんなものなのか?」と思って心配になっていました💦 じゃあおそらく`Prism WPF(Full App)`テンプレートで自動的に作られるプロジェクト... * SapmleApp.Services.Interface * SampleApp.Services というのは、DIで作った方が良いクラス(すぐに思いつくのはシングルトンにしたくなるものとか)などを置いておくために自動で作られたプロジェクトというだけで、Modelに該当するプロジェクトは自分で新規プロジェクトで書けばよさそうですね! SampleApp.Modules.ModuleName なんていう名前でプロジェクト作るぐらいなら、 SampleApp.Models.ModelName みたいなプロジェクトも作ってくれれば誤解がなかったのですが💦 Modelに該当するプロジェクトが見当たらなくて混乱してたのかもしれません。 まぁ...名前はすぐに変更するでしょうけどww ご回答ありがとうございます!
Zuishin

2022/01/19 07:08

View は ViewModel と Model に依存し、ViewModel は Model に依存します。 ViewModel から Model を使用するのは問題ありませんが、その逆に Model から ViewModel を使用してはいけません。 しかし時には Model からダイアログを表示したいなど、ViewModel や View を使いたいときがあります。Services と Interfaces は主にこのような時に使います。
HiraKazu1124

2022/01/19 07:12

https://qiita.com/okazuki/items/cfbf5c9eaea6c5aed4e1 上記サイトも検索(prism full app)でほぼトップに出てきますが、なんだかインターフェイスを介してしかModelにアクセスしないような例しか載せていないので、さらに誤解が深まったんですよね💦 たぶんDIの例を紹介したかっただけなんでしょうね... デマの出元はどこだったかはっきり覚えてません(-_-);
HiraKazu1124

2022/01/19 07:17

>Zuishinさん あ~なるほど! いままでダイアログの表示などはMessageパターンでシングルトンクラスを介して「ダイアログ表示のメッセージ」みたいなものを作って開発していましたが、それがServicesとInterfacesで実装できるんですね! 確かにModelからViewとViewModelの操作をしたいときは自作クラスでやろうとすると結構大変ですよね💦 貴重な情報ありがとうございます。 早速試してみます^^ (確かprismにすでに用意されているものもあったような?) ご回答ありがとうございます!
TN8001

2022/01/19 08:56

わたしはPrism全然わからないので何とも言えませんが、↓なんかはだいぶ参考になるんじゃないでしょうか? [brianlagunas/PrismOutlook: The Prism for WpF app that we are building live on Twitch/YouTube](https://github.com/brianlagunas/PrismOutlook [Prism for WPF - Building Outlook - YouTube](https://www.youtube.com/playlist?list=PLG8rj6Rr0BU8MaincrMrHuhJrI2Id8RSW 有料のコントロールを使いまくっているのでトライアルで試すか、コードの雰囲気を見るだけになってしまいますがw 少なくともPrismの中の人はどういうつもりかはわかるんじゃないでしょうか?(わたしはコードも動画も見てません^^; 公式サンプルにも一個くらい規模の大き目のものがあっていいと思うんですけどねぇ。
HiraKazu1124

2022/01/19 23:26

>TN8001さん いいですね^^ このサンプルコードでは普通にViewModelからModelのクラスを使っていますし、そのほかのことでもある程度雰囲気がわかりそうです! ご回答ありがとうございます!

まだ回答がついていません

会員登録して回答してみよう

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
87.20%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです