C# App.Configでエラー
DbProviderFactoriesの仕組みを勉強してて、DbProviderFactories.GetFactoryClasses()でConfigurationErrorExceptionが発生。
App.Configをよ~く見てみるとセクションの後のスペースが交っていた。
これを消してやると解消。
1時間くらい悩みました・・・
インターフェースを考える(14)考えを整理(出来ていない)
C#実践開発手法のシングルメソッドインターフェースで色々書いてきたけども、
途中途中浮かんだ考えをだぁーっと箇条書きにしてみる。
C#実践開発手法 デザインパターンとSOLID原則によるアジャイルなコーディング
- 作者: ゲイリーマクリーンホール
- 出版社/メーカー: 日経BP社
- 発売日: 2015/06/25
- メディア: Kindle版
- この商品を含むブログを見る
- オブジェクト指向や〇〇パターンとかはツールであって目的ではないということ。
- 処理は小さく、抽象化されており、組み換え可能であることが望ましい。
- シンプルな処理であればあるほど、コードを修正する可能性は低くなる。
- 小さいクラスは設計・開発・テストのサイクルがシンプルで作業を理解しやすい。
- コードに修正の余地が無ければ、たった一つでも仕事をしていれば資産と言える。
- コードを修正して機能を増やすことよりクラスを増やして機能を増やしていく。
- クラス数が増えるのは問題ではない、増えたクラスがライブラリや名前空間で整理されていないのが問題。
- 複数の処理を持っているクラスはその分コードを修正する可能性が高くなる。
- 修正の可能性が低いコードほど堅牢で修正の可能性が高いコードほど脆弱なコードになる。
- 10の機能を持った1つのクラスを作るなら、1つの機能を持ったクラスを10個つくれば良い。
- 具体的な処理は不変であり、コラボレーション部分のみが修正の対象となれば良い。
- 分岐を目的としたパラメータを持つメソッドはパターン毎にメソッドを分割出来るはず。
- 条件分岐はパターン毎の処理を実行するという責務と、パターン毎に振り分けを行うという責務に分離する。
- シングルメソッドインターフェースは構造化プログラミングとOOPのポリモーフィズムのおいしいとこ取りをしている。
- 複数の処理を持ちたければ振る舞いクラスに委譲すれば良い。
- インターフェースのみに依存する場合、またはクラスに依存していても行き着く依存先がインターフェースのみであれば静的クラスの利用も悪くない。
- 何かを呼び出して戻り値を受け取る処理はまた別ななにかを呼び出して依存する。戻り値が必要無いように組み替えれば余計な依存関係は生まれない。
- プログラムの仕様変更が入った時のことを想像する。それがそのまま責務を分割する糸口になる場合がある。
- 思い切ってクラスの継承は無いものと考えた方が設計が捗る。
インターフェースを考える(13)コレクションとインターフェース
IActionやISpecificationなど、単一の要素に対するインターフェースでしたが、
実際にアプリケーションを組むとなると必ずコレクションに対する操作が必要になると思います。
今回は拡張メソッドを使って、都度コーディングをしなくてもこれらインターフェースの処理を使い回す方法を考えます。
まずIActionについて、以下のような拡張メソッドを用意しました。
public static void DoEach<T>(this IEnumerable<T> actors,IAction<T> action) { new List<T>(actors).ForEach(x => x.Do(action)); } public static IEnumerable<T> DoEachFluent<T>(this IEnumerable<T> actors, IAction<T> action) { actors.DoEach(action); return new List<T>(actors); }
やっていることは単純でListクラスのForEachメソッドにIActionを実行する匿名メソッドを指定しているだけです。
同じようにISpecificationを使った拡張メソッドをIEnumerableに追加します。
using System.Collections.Generic; namespace Nmdm.Specifications { public static class ISpecificationExtension { public static IEnumerable<T> FindAll<T>(this IEnumerable<T> collection,ISpecification<T> spec) { return new List<T>(collection).FindAll(x => spec.IsSatisfiedBy(x)); } } }
こちらも同じ要領でListのFindAllメソッドにboolを返す匿名メソッドを指定しているだけ。
どちらもやっていることはデリゲートを使うメソッドをインターフェースに適用させてるだけですね。
C#実践開発手法でもコードに適用力を持たせるならデリゲートよりクラスですよと書いてあるけど、
それなら標準のデリゲートと同じようにインターフェースも用意してあれば良かったのになぁ・・・
インターフェースを考える(12)ActionとActor
IActionインターフェースについて思いついたことがあるので書いてみます。
まずIActionインターフェース
namespace Nmdm.Actions { public interface IAction<TContext> { void Do(TContext context); } }
機能面だけで見ると一つの引数を受け取って戻り値の無いメソッドを実行するインターフェースですが。
これを少し見方を変えて「TContextが行う動作」とすると、以下の拡張メソッドでIActionをTContextのインスタンスメソッドのように呼び出せる。
namespace Nmdm.Actions { public static class IActionExtension { public static void Do<T>(this T actor, IAction<T> action) { action.Do(actor); } } }
これのクライアントコードの例は以下の通り
// IActionのパラメータになるクラス var actor = new object(); // IActionのNullオブジェクト var action = new NullAction<object>(); // IActionにパラメータを渡して実行する場合。 action.Do(actor); // actorのインスタンスメソッドに見せかけて実行する場合。 actor.Do(action);
メリット(?)を考える。
・あたかもactorが主体となって処理を実行するので視覚的にオブジェクト指向チックに見える。
・actorの振る舞いを別クラスで実装出来るのでactor自身の肥大化が防げる。
(ただし、actorの状態自体を書き換えるのであればactor自身にSetterのようなものが必要)
・IAction自体は組み換えしやすい。特に複雑な動作が求められる場合に有利?
うーん・・・処理とふるまいが一体化するっていうところからかなり逸脱してるけども・・・
とにかくクラスを細分化するっていうところだけ突き詰めていくとこういう書き方もありなのか・・・?
これをもう少し手を入れて「流暢なインターフェース」にするのであれば、
拡張メソッドにactor自身を返すメソッドDoFluentを追加する。
namespace Nmdm.Actions { public static class IActionExtension { public static void Do<T>(this T actor, IAction<T> action) { action.Do(actor); } public static T DoFluent<T>(this T actor, IAction<T> action) { actor.Do(action); return actor; } } }
このDoFluentの呼び出し例は以下の通り。
// 流暢な記述
actor
.DoFluent(action)
.DoFluent(action)
.DoFluent(action);
インターフェースを考える(11)IActionを手直し
ISpecification同様IActionを手直し+拡張メソッドを導入
まずCompositeActionクラス
namespace Nmdm.Actions { public sealed class CompositeAction<TContext> : IAction<TContext> { public CompositeAction(IAction<TContext> one, IAction<TContext> other) { this.One = one ?? new NullAction<TContext>(); this.Other = other ?? new NullAction<TContext>(); } private IAction<TContext> One { get; } private IAction<TContext> Other { get; } public void Do(TContext context) { this.One.Do(context); this.Other.Do(context); } } }
これの拡張メソッドが以下の通り
namespace Nmdm.Actions { public static class CompositeActionExtension { public static IAction<TContext> Add<TContext>(this IAction<TContext> one,IAction<TContext> other) { return new CompositeAction<TContext>(one, other); } } }
考え方はISpecificationで実装した拡張メソッドと同じか、
前にCompositeActionを書いた時は無理やり不変+流暢にするため、CompositeActionにAddメソッドを持たせたけど、
やはりあの書き方だとクラスが無駄に複雑になってましたね。
あくまで「書き方」に留まるレベルなら無理にクラスの実装をこねくり回すより、
拡張メソッドに委譲した方が楽ですね。