長時間実行されるタスク用に設計されたビューがあります。ヘッダーと進行状況バーがあります。したがって、モデルには、ヘッダーのテキストと、進行状況バーのカウンター、および TotalAmountOfWork フィールドがあります。モデルにもあります
public delegate void TaskCompleted(string resultDescription);
public event TaskCompletedCopyingCompletedEvent;
public event Action UpdateViewState;
カウンターが変更されると、モデルは UpdateViewState を呼び出します。ViewModel は、イベントと更新のためにサブスクライブされ、それがビューになります。
Ok。ハードディスクからフラッシュドライブにファイルをコピーすることを目的とした2つのクラスと、診断情報の取得を目的とした1つのクラスがあり、この情報も最終的にフラッシュドライブにコピーする必要があります。
同じ ViewModel で使用したいのですが、コードの繰り返しを避ける方法がわかりません。適切なオブジェクト指向設計に依存して作成する方法がわかりません。
これら 3 つのクラスは、次のようなインターフェイスを実装できます。
interface ILongRunningTask {
void DoWork();
}
そして、ILongRunningTask を引数として ViewModel を実装できます。
しかし、インターフェイスの名前を見てください。あまりにも一般化されているように見えます。このような抽象化には何か問題があるようです。
Ok。長時間実行されるタスクを呼び出すには、ViewModel がデリゲートを取得する必要があるように思えます。しかし、この場合、ViewModel はどのようにモデルと対話してそのプロパティを更新するのでしょうか?
//update これで、モデルは次のようになります。
public class FilesCopyingModel : IFilesCopier {
protected int filesCountToCopy;
public int FilesCountToCopy {
get { return filesCountToCopy; }
set {
filesCountToCopy = value;
InvokeUpdateViewState();
}
}
protected int currentProgressValue;
public int CurrentProgressValue {
get { return currentProgressValue; }
set {
currentProgressValue = value;
InvokeUpdateViewState();
}
}
public delegate void CopyingCompleted(string resultDescription);
public event CopyingCompleted CopyingCompletedEvent;
public event Action UpdateViewState;
private readonly IFilesCopier filesCopier;
protected FilesCopyingModel() {
}
public FilesCopyingModel(IFilesCopier filesCopier) {
if (filesCopier == null)
throw new ArgumentNullException("filesCopier");
this.filesCopier = filesCopier;
}
protected static string GetCurrentDateTime() {
return DateTime.Now.ToString("dd.MM.yyyy hh.mm.ss");
}
protected void InvokeCopyCompletedEvent(string resultDescription) {
if (CopyingCompletedEvent != null)
CopyingCompletedEvent(resultDescription);
}
protected void InvokeUpdateViewState() {
if (UpdateViewState != null)
UpdateViewState();
}
protected DriveInfo GetFirstReadyRemovableDrive() {
return
DriveInfo.GetDrives()
.FirstOrDefault(driveInfo => driveInfo.DriveType == DriveType.Removable && driveInfo.IsReady);
}
public void Copy() {
filesCopier.Copy();
}
}
public interface IFilesCopier {
void Copy();
}
public class KFilesCopier : FilesCopyingModel, IFilesCopier {
private string destinationKFilesDirPath;
public new void Copy() {
//some code
}
private static string ComposeDestinationKFilesDirPath(DriveInfo drive) {
//some code
}
}
public class LogsDirCopier : FilesCopyingModel, IFilesCopier {
public readonly string LogsDirPath;
public LogsDirCopier() {
//some code
}
public new void Copy() {
//some code
}
private void InternalCopyLogsDir(string destinationPath) {
//some code
}
private static void CloseStorer(ZipStorer zipStorer) {
//some code
}
private static string ComposeDestinationArchiveFilePath(string destinationPath) {
//some code
}
private void DetermineLogFilesCount() {
//some code
}
ViewModel は、次のように上記のインフラストラクチャとやり取りします。
public class FilesCopyingViewModel: Screen {
private readonly FilesCopyingModel model;
private readonly IWindowManager windowManager;
public int CurrentProgress {
get { return model.CurrentProgressValue; }
}
public int FilesCountToCopy {
get { return model.FilesCountToCopy; }
}
[ImportingConstructor]
public LongRunningViewModel(IFilesCopier copier) {
model = copier as FilesCopyingModel;
model.CopyingCompletedEvent += CopyingCompletedHandler;
model.UpdateViewState += UpdateViewStateHandler;
windowManager = new WindowManager();
}
private void UpdateViewStateHandler() {
NotifyOfPropertyChange(() => CurrentProgress);
NotifyOfPropertyChange(() => FilesCountToCopy);
}
private void CopyingCompletedHandler(string resultDescription) {
//some code
}
private void RemoveDriveSafely() {
//some code
}
private void PromptEjection(string result) {
//some code
}
private void PromptSuccessEjection() {
//some code
}
private void PromptEjectFlashError() {
//some code
}
protected override void OnActivate() {
try {
var copier = (IFilesCopier) model;
Task.Factory.StartNew(copier.Copy);
}
catch (Exception ex) {
//error handling
}
}
}
これら 2 つのクラスは、メソッドの名前として「Copy」を使用します。ここで、非常によく似た動作のクラスをもう 1 つ追加したいと思いますが、そのメソッドの名前は「CollectDiagnosticInfo」のようにする必要があるようです。または、DiagnosticInfoCopier:IFilesCopier クラスを追加して、同じことを行うこともできます。よくわかりませんが、第六感はある種の匂いがあることを示唆しています。