No more Death March

あるSEのチラシの裏 C# WPF

WPF 実行環境によっては画面がぼやける・・・

WPFではまったのでメモします。

開発用マシンとは異なるOS、ハードで実行すると画面がぼやけてしまった。
アンチエイリアス処理の結果かなぁとは思ったけどぼやけかたが余りに酷い。

なにかないかと試してみたのがこちら↓
FrameworkElement.UseLayoutRounding プロパティ (System.Windows)

UIのルート要素でtrue(既定はfalse)を指定すると実用に耐えるくらい改善されました。

二つの環境を見比べるとアスペクト比が若干崩れていますがとりあえず我慢・・・
WPFってはまりどころがたくさんあって心が折れそう・・・

インターフェースを考える(6)IEvent

以前書いたドメインイベントの実装について、
インターフェースの側から考える。

nomoredeathmarch.hatenablog.com
nomoredeathmarch.hatenablog.com

まず作成したインターフェースについて復習

まず、イベントを表すクラスのインターフェースIEvent

namespace Nmdm.Events
{
    public interface IEvent
    {
    }
}

あくまで印をつけるためのマーカー

続いてそのインターフェースを受け取るIHandler

namespace Nmdm.Events
{
    public interface IHandler
    {
        void Handle(IEvent value);
        bool CanHandle(IEvent value);
    }
}

こちらはパブリッシャーからの受信機構となるメソッド。

でここからが今回の本題、
Publisherクラスをstaticで実装したけども、これもインターフェースで抽象化してみる。

以下がインターフェースを抜き出したIPublisher

namespace Nmdm.Events
{
    public interface IPublisher
    {
        void Publish(IEvent e);
        void Subscribe(IHandler handler);
        void Unsubscribe(IHandler handler);
    }
}

staticじゃなくなったことで単体テストがしやすくなったけど、
半面、どこからでもイベント通知、購読することが出来なくなったわけだ。

ただ、staticクラスのように振舞わせたければシングルトンなりこのクラスをキャッシュするためだけのスタティッククラスを作れば良いわけだし、
それであれば通常のクラスとして設計すれば良いとも思える。

これを以前作ったPublisherクラスに実装させる。

using System;
using System.Collections.Generic;
using System.Threading;

namespace Nmdm.Events
{
    public class Publisher:IPublisher
    {
        private static Mutex Mutex { get; } = new Mutex();
        private static List<WeakReference<IHandler>> Subscribers { get; } 
            = new List<WeakReference<IHandler>>();
        public void Publish(IEvent e)
        {
            try
            {
                Mutex.WaitOne();
                if (e == null) throw new ArgumentNullException("e");
                var list = new List<IHandler>();
                list.AddRange(Subscribers.ConvertAll<IHandler>(x => ToStrong(x)));
                list.RemoveAll(x => x == null || !x.CanHandle(e));
                list.ForEach(x => x.Handle(e));
                Subscribers.RemoveAll(x => ToStrong(x) == null);
            }
            finally
            {
                Mutex.ReleaseMutex();
            }
        }

        private static IHandler ToStrong(WeakReference<IHandler> obj)
        {
            IHandler ret;
            obj.TryGetTarget(out ret);
            return ret;
        }

        public void Subscribe(IHandler subscriber)
        {
            try
            {
                Mutex.WaitOne();
                if (subscriber == null) throw new ArgumentNullException("subscriber");
                if (Subscribers.Contains(new WeakReference<IHandler>(subscriber))) return;
                Subscribers.Add(new WeakReference<IHandler>(subscriber));
            }
            finally
            {
                Mutex.ReleaseMutex();
            }

        }

        public void Unsubscribe(IHandler subscriber)
        {
            try
            {
                Mutex.WaitOne();
                if (subscriber == null) throw new ArgumentNullException("subscriber");
                Subscribers.RemoveAll(x => subscriber.Equals(ToStrong(x)));
            }
            finally
            {
                Mutex.ReleaseMutex();
            }

        }
    }
}

インターフェースを考える(5)IAction その2

前回に続きIActionについて考える。
IPredicateやITaskと同じように組み合わせ用にCompositeActionクラスを作る。

using System;
using System.Collections.Generic;

namespace Nmdm.Actions
{
    public sealed class CompositeAction<TContext> : IAction<TContext>
    {
        public CompositeAction(IEnumerable<IAction<TContext>> actions)
        {
            if (actions == null) throw new ArgumentNullException("actions");
            this.Actions = actions;
        }

        private IEnumerable<IAction<TContext>> Actions { get; }

        public void Do(TContext context)
        {
            foreach(var obj in this.Actions)
            {
                obj.Do(context);
            }
        }
    }
}

IPredicateの時も気になったけど、このクラスを使う時にList型の変数を使ってIActionをコレクションに入れてからコンストラクタを呼び出すのかって考えるといまいちな感じがする。
かといって、クラスは基本不変にしたい・・・・

ということで↓へ改造

using System;
using System.Collections.Generic;

namespace Nmdm.Actions
{
    public sealed class CompositeAction<TContext> : IAction<TContext>
    {
        public CompositeAction()
        {
            this.Actions = new IAction<TContext>[] { };
        }

        private CompositeAction(IEnumerable<IAction<TContext>> actions)
        {
            if (actions == null) throw new ArgumentNullException("actions");
            this.Actions = actions;
        }

        private IEnumerable<IAction<TContext>> Actions { get; }

        public CompositeAction<TContext> Add(IAction<TContext> action)
        {
            if (action == null) throw new ArgumentNullException("actions");
            var actions = new List<IAction<TContext>>(this.Actions);
            actions.Add(action);
            return new CompositeAction<TContext>(actions);
        }

        public void Do(TContext context)
        {
            foreach(var obj in this.Actions)
            {
                obj.Do(context);
            }
        }
    }
}

コンストラクタをプライベートにしてAddメソッドを追加、メソッドの戻り値でprivateなコンストラクタで生成した結果を返して不変かつ流暢なインターフェースに。
このクラス自体がIActionを実装しているからAddRangeメソッドはいらないと思う。
ついでにIActionの受け口をAddメソッドだけにしたから個々の要素に対してもnullチェックが出来ている。
元のソースと比べると複雑さが増しているけどIAction以外への依存はなく変更の可能性が低いから良いかと思います。


==2017.03.14追記===
やっぱり改造やめ、読みにくいから、というならまだしも、
書きにくいから、という理由でメソッドを増やすのに少し抵抗を感じる。

インターフェースを考える(4)IAction

C#実践開発手法よりIActionインターフェースです。

namespace Nmdm.Actions
{
    public interface IAction<TContext>
    {
        void Do(TContext context);
    }
}

単一の引数を受け取る戻り値の無いインターフェースで、
引数は型パラメータで指定出来る・・・と

使いどころはどこなんだろう。
こないだ書いたITaskに引数が一つ付いただけなわけですが・・・
nomoredeathmarch.hatenablog.com

手段と目的がごっちゃになってるけど何かスマートな使い方は出来ないものか・・・

たとえば、ドメインイベントのハンドラにイベントの具体的な処理を着脱できるようにするとか?
まずIHandlerインターフェス(前の記事ではISubscriberだったけどC#実践開発手法の表記に合わせた。)

namespace Nmdm.Events
{
    public interface IHandler
    {
        void Handle(IEvent value);
        bool CanHandle(IEvent value);
    }
}

こいつの実装クラスとして、着脱可能なアクションをコンストラクタで受け取るHandlerクラス

using System;
using Nmdm.Actions;

namespace Nmdm.Events
{
    public sealed class Handler<T> : IHandler 
        where T:IEvent
    {
        public Handler(IAction<T> action)
        {
            if (action == null) throw new ArgumentNullException("action");
            this.Action = action;
        }

        private IAction<T> Action { get; }

        public bool CanHandle(IEvent value)
        {
            if (value == null) return false;
            return value is T;
        }

        public void Handle(IEvent value)
        {
            if (!this.CanHandle(value)) return;
            this.Action.Do((T)value);
        }
    }
}

コンストラクタでIAction(制約でTはIEventとする。)を受け取ってprivate変数へ。
あとはHandlerクラスがドメインイベントの通知を受け取る度にIActionが実行されると。

ドメインイベントとは違うけど、MVVMのメッセージ通知とかに使えるかな?
イベントでメッセージの内容を受け取って、アクション側でメッセージ表示の具体的な方法を実装する。
最初は標準のメッセージボックスを表示してたけど、そのうちカスタマイズしたメッセージボックスにすげ変えると・・・

パブリッシャに予約してなければ単体テストもそのままで良さそう。
イベント通知される場面なら↓みたいなIActionのモックと入れ替えても良いのか。

namespace Nmdm.Actions
{
    public sealed class ActionMock<T> : IAction<T>
    {
        public void Do(T context)
        {
        }
    }
}

「イベントの通知を受け取る」っていう仕事と「イベントの情報を読み込んで処理を実行する。」っていう仕事を分離して実装出来ますね。

WPF IValueConverter小メモ

DateTime向けのコンバーターでDateTime以外の入力があったら例外投げていたけど、
どうもnullで飛んでくるタイミングがあって例外発生してたもよう。

変換に失敗したら例外投げるより既定値で渡した方が良い?

そのうち検証しよう。

インターフェースを考える(3)IPredicate

今回もC#実践開発手法から、IPredicateを題材にします。

まずはインターフェース

namespace Nmdm.Predicates
{
    public interface IPredicate
    {
        bool Test();
    }
}

ITaskインターフェース同様引数はありませんが、戻り値がbool型です。
戻り値をジェネリックにしてIFuncとしても良さそうですが、
条件判定という目的に特化している。と考えたら別のインターフェースにした方がシンプルかな。

ITaskの時と同じようにモックを作るのですが、bool型を返すのでtrueを返すモックとfalseを返すモックの2パターンを作ります。

まずは常にtrueを返すTruePredicateクラス

namespace Nmdm.Predicates
{
    public sealed class TruePredicate : IPredicate
    {
        public bool Test()
        {
            return true;
        }
    }
}

反対に常にfalseを返すFalsePredicateクラス

namespace Nmdm.Predicates
{
    public sealed class FalsePredicate : IPredicate
    {
        public bool Test()
        {
            return false;
        }
    }
}

IPredicateを組み立てるためのクラスですが、ここでは一旦置いておき、
ITaskとの連携を考えてみます。
まずはTrueかFalseか判定して処理を分岐させるクラスとしてTestedTaskを作ってみました。

C#実践開発手法の中で紹介されている分岐デコレータと同じですね。

using Nmdm.Tasks;

namespace Nmdm.Predicates
{
    public sealed class TestedTask : ITask
    {
        public TestedTask(IPredicate predicate,ITask trueTask,ITask falseTask)
        {
            this.Predicate = predicate;
            this.TrueTask = trueTask;
            this.FalseTask = falseTask;
        }

        private IPredicate Predicate { get; }
        private ITask TrueTask { get; }
        private ITask FalseTask { get; }
      
        public void Do()
        {
            if(this.Predicate.Test())
            {
                this.TrueTask.Do();
                return;
            }
            this.FalseTask.Do();
        }
    }
}

条件判定と処理を組み合わせて処理の分岐を表現しているわけですね。なるほど・・・

記事が長くなりそうなのでこのへんで一端切ります。