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

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

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

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

MVVM

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

WPF

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

Q&A

解決済

1回答

1660閲覧

Prism環境下でのMVVMの質問

HiraKazu1124

総合スコア322

C#

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

MVVM

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

WPF

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

1グッド

2クリップ

投稿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#

1// AnimalType.cs 2public enum AnimalType { 3 Honyu, // 哺乳類 4 Tyou, // 鳥類 5 Hatyu, // 爬虫類 6 Ryousei, // 両生類 7 Gyo, // 魚類 8} 9 10// Animal.cs 11public class Animal { 12 public string Name {get; private set;} 13 public AnimalType Kind {get; private set;} 14 15 public Animal(string name, AnimalType kind) { 16 Name = name; 17 Kind = kind; 18 } 19} 20 21// SearchAnimalService.cs 22// 疑問点3 23public class SearchAnimalService { 24 private SearchAnimalService _instance; 25 public SearchAnimalServicce Default => _instance ?? new SearchAnimalService(); 26 27 public SearchAnimalService() { 28 // DBへの接続などの情報を定義(本来は別のクラスでDBとの接続定義を行う処理を書くと思いますが今回はここで...) 29 } 30 31 // 名前をDBから検索(失敗したらnull) 32 public async Task<Animal> Search(string name) { 33 Animal result = null; 34 35 // await (DBから名前が一致する動物を検索) 36 37 return result; 38 } 39}

「ViewModel部」

C#

1// MainWindowViewModel.cs 2public class MainWindowViewModel { // 本当はINotifyPropertyChangedの空実装を追加しています 3 // ViewにおいてあるTextBoxとバインド 4 public ReactiveProperty<string> SearchName {get;} = new(); 5 6 // 疑問点2 7 // ViewにおいてあるTextBlockとバインド 8 public ReactiveProperty<string> AnimalTypeString {get;} = new(); 9 10 // ViewにおいてあるButtonのCommandとバインド 11 public AsyncReactiveCommand SearchCommand {get;} = new(); 12 13 public MainWindowViewModel() { 14 SearchCommand.Subscribe(OnSearch); 15 } 16 17 private async Task OnSearch() { 18 Animal result = await SearchAnimalService.Default.Search(SearchName.Value); 19 if (result == null) return; 20 21 AnimalTypeString.Value = AnimalTypeToString(Animal.Kind); 22 } 23 24 // 疑問点1 25 private static string AnimalTypeToString(AnimalType kind) { 26 return kind switch { 27 AnimalType.Honyu => "哺乳類", 28 AnimalType.Tyou => "鳥類", 29 AnimalType.Hatyu => "爬虫類", 30 AnimalType.Ryousei => "両生類", 31 AnimalType.Gyo => "魚類", 32 _ => throw new NotImplementedException() // 理論上ここには到達しない 33 }; 34 } 35}

サンプルに対しての質問

「疑問点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で実装すれば単体テストなども楽に書けますよね?

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

Hiro_Pon👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

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のクラスを使っていますし、そのほかのことでもある程度雰囲気がわかりそうです! ご回答ありがとうございます!
guest

回答1

0

自己解決

prismを使っていても、ViewModelからModelクラスを普通に参照してよいとわかって安心しました。
DIを使うべきクラスなどは使いますが、使わないクラスは今まで通りの方針で開発していこうと思います^^

ご回答いただいた皆さんありがとうございました!

投稿2022/01/19 23:30

HiraKazu1124

総合スコア322

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問