teratail header banner
teratail header banner
質問するログイン新規登録

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

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

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

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

Q&A

1回答

4074閲覧

データが変更された場合、ほかのクラスにその変更を通知する必要がある際のクラス設計

esakyon

総合スコア14

C#

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

0グッド

0クリップ

投稿2020/10/15 08:39

0

0

やりたいこと

C#と、WPFというWindowsアプリケーションを作成するためのフレームワークを用いて開発しているところです。

その際、「データが変更された場合、ほかのクラスに変更を通知する必要がある」というケースが出てきました。
例としてコードを挙げると以下の通りです。

C#

1public class Main 2{ 3 public Main() 4 { 5 // dataを宣言、何らかの初期値が入る 6 Data data = new Data(name, id); 7 8 // dataを利用したクラスを4つ作成する。 9 // ただし、これらのクラスはdataを書き換えることができる。 10 // 変更が加わった際には、同じdataを利用している他のクラスに 11 // 「データが変更された」ということを通知する必要がある。 12 var a = new A(data); 13 var b = new B(data); 14 var c = new C(data); 15 var d = new D(data); 16 } 17} 18

A,B,C,Dそれぞれのクラスは入力されたdataをいじくったとき、それをほかのクラスに通知する必要があります。
ただし、このdataが複数個そんざいすることは無く、必ずこのアプリケーション上で1つしか存在しないです。
この際のクラス設計について非常に悩んでいます。

現在考えている案

今のところ考えている案が下の2つになります。

1. デザインパターンの一つであるシングルトンとオブザーバーパターンをDataクラスに使用する。

やりたいことで挙げたように、「dataが複数個そんざいすることは無く、必ずこのアプリケーション上で1つしか存在しない」ことを利用し、Dataクラス本体を唯一のクラスであるシングルトンで書く。
かつ、シングルトンであるDataに変更が加わった場合、オブザーバーパターンをDataクラスとそれを利用しているA,B,C,Dに適用し、変更が加わったらそれを通知する。

2. A,B,C,DのクラスすべてにIDataChangeSenderなどのinterfaceを継承させ、変更が加わったら相互で通知させる。

IDataChangeSender、IDataChangeReceiverの2つのインターフェースを作成し、A,B,C,Dのすべてに適応させ、それぞれが変更を加えたら、「変更したのを新しく送るね」といった感じで引数としてdataを渡す。

これらの案に対して自分が思ったこと

1.に関して

もともとは1.で実装していたのですが、アプリケーション全体をリファクタリングしている最中に、「デザインパターンは悪」といった評価をネットで受けました。
また、「シングルトンをグローバル関数として使ってはいけない」ともかかれており、自分はそのような使い方をしていたため
直したほうがいいのかと、2を考えました。

2.に関して

現在は1から2の方法を実装している途中なのですが、インターフェースがごちゃごちゃになり、1.の時よりすっきりとコード全体を把握できなくなりそうと感じました。

改めて聞きたい事

このような「データが変更された場合、ほかのクラスにその変更を通知する必要がある際のクラス設計」に関して皆さんはどのようなアプローチをしますか?
また、1.2.の案についてなど思うところが知りたいです。
よろしくお願いします

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

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

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

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

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

guest

回答1

0

こんばんは。

データが変更された場合、ほかのクラスにその変更を通知する

INotifyPropertyChangedという、もろそのもののインターフェイスが存在します。
WPFで開発しているとよくお世話になるものです。

インターフェイスを実装するか、あるいはPrismなどの実装を補助するフレームワークを利用することで変更通知が飛ぶようになります。

C#

1//using System.ComponentModel; 2//using Prism.Mvvm; 3 4// BindableBaseがINotifyPropertyChangedを実装してくれています。 5public class Data : BindableBase 6{ 7 private string _Value = string.Empty; 8 public string Value 9 { 10 get => _Value; 11 set => SetProperty(ref _Value, value); 12 } 13} 14 15public class Observer 16{ 17 Data data = new Data(); 18 19 public Observer() 20 { 21 data.PropertyChanged += (sender, e) => 22 { 23 switch (e.PropertyName) 24 { 25 case nameof(Data.Value): 26 Console.WriteLine($"Valueプロパティの値が \"{data.Value}\" に変更されました"); 27 break; 28 } 29 }; 30 31 data.Value = "hello"; // ここで変更通知が発火されます 32 } 33}

この他、Collectionの要素の変更を通知することができるObservableCollectionというものもあったりします。
詳しくは検索してみて下さい。

##(追記)参考:MVVMに沿った設計
WPFアプリはMVVMという設計思想に沿って設計するのが一般的となっています。
MVVMはModel-View-ViewModelの略で、

  • ウィンドウやユーザーコントロールなど、画面への表示に直接関与するクラスをView
  • ViewにBindingするプロパティを持つクラスをViewModel
  • ロジックなどを持つクラスをModel

というように、クラスのメンバの役割によってクラスを分離し、コードを読みやすくすることが出来ます。

以下、MVVMパターンを適用して

  • LabelにViewModelのValueを表示
  • Buttonクリック時にViewModelのButtonClickedを実行

以上の機能を実装したサンプルです。

私がPrismerなのでPrismを利用して書きますが、各クラスは以下のような感じになります。
実行環境が手元に無いので、もしかしたら動かないかもしれません。動かなかった場合はごめんなさい。

###MainWindow (XAML)

XAML

1<Window …略…> 2 <StackPanel> 3 <Label Content="{Binding Value}" /> 4 <Button Command="{Binding ButtonClicked}" /> 5 </StackPanel> 6</Window>

###MainWindow (C#)

C#

1public class MainWindow : Window 2{ 3 public MainWindow() 4 { 5 InitializeComponent(); 6 DataContext = new MainWindowViewModel(); // DataContextに指定したクラスの変更通知を受け取り、自動的に表示に反映するようになります。 7 } 8}

###ViewModel

C#

1//using Prism.Mvvm; 2//using Prism.Commands; 3 4// BindableBaseがINotifyPropertyChangedを実装してくれています。 5// MainWindowなどのDataContextにこのクラスのインスタンスを指定して下さい。 6public class MainWindowViewModel : BindableBase 7{ 8 private string _Value = string.Empty; 9 public string Value 10 { 11 get => _Value; 12 set => SetProperty(ref _Value, value); 13 } 14 15 // MVVMでは、ボタンクリック時のメソッドなどもVMに書きます。 16 // Xaml内でButtonに「Command="{Binding ButtonClicked}"」を設定すると使えるようになります。 17 public DelegateCommand ButtonClicked { get; set; } 18 19 public MainWindowViewModel() 20 { 21 ButtonClicked = new DelegateCommand(() => MessageBox.Show("ボタンが押されました")); 22 } 23}

MVVMパターンで開発する場合、変更通知はVM→V(とM→VM)で発生させることになります。
VがVMの変更通知を受け取る部分に関しては、XAMLコードをもとに自動的に処理してくれるので開発者はただVMにINotifyPropertyChangedを実装させてVのDataContextに指定するのみでOKです。
一方、VMがMの変更通知を受け取る部分に関しては、VMのコンストラクター内などでM.PropertyChanged += (sender, e) => { …略… };とすることになります。

投稿2020/10/15 08:50

編集2020/10/16 11:54
Automatic9045

総合スコア313

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

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

アカウントをお持ちの方は

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

ただいまの回答率
85.30%

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

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

質問する

関連した質問