パイプラインパターン


導入


パイプラインパターンの実装さまざまな記事は、このパターンのさまざまな側面をカバーするために既に作成されました.しかし、Azure関数やWebjobの使用によって非常に複雑な流れに組み込もうとしたことがありますし、まだ両方を持っています.クリーンでしっかりした、テスト可能なコードと、いくつかの外部のリクエストを実行するために中断されることができます.
このトピックについてあなたの意見を共有しましょう.記事のこのシリーズでは、説明と例とすべての実装の詳細を見つける.始めましょう!

実装


最初に、パイプラインパターン自体を実装し、その後、さらに開発します.
コードを書く前に、最終的な解決策を満たすべきすべての仮定を定義したいと思います.
  • パイプラインパターンの実装は一般的です.
  • 実装は、しっかりした原則に従います.
  • エラー処理が適切です.
  • まず始めましょう.
    あなたがこのパターンに新しいならば、読書を続ける前に、しばらくここで止まって、あなた自身に質問をしてください.
  • どのように、私のパイプラインパターン実装は働きますか?
  • 私のパイプラインの入出力は?
  • パイプラインパターンの一般的な実装を定義するには?
  • 私はそれを一般的にする必要がありますか?
  • 一般的な実装によってどんな方法を提供すべきでしょうか?
  • どのようにパイプのステップを実行するつもりですか?
  • あなたのアイデアを準備ができていますか?以下は私です.
    パイプラインにはステップが必要です.一歩は、単独の責任で別々のクラス以外です.各々のステップは、そのロジックが実行されるとき、対話するモデルを必要とします.だからここに出発点があります.
    public interface IPipeStep<TPipeModel>
    {
        Task ExecuteAsync(TPipeModel pipeModel);
    }
    
    その上、パイプステップを管理するパイプサービスが必要です.
    インタフェース:IPipeService メソッド定義でパイプラインとIPipeServiceExecution パイプ実行のメソッドを定義するには、次の手順に従います.
    public interface IPipeService<TPipeModel> : IPipeServiceExecution<TPipeModel>
    {
        IPipeService<TPipeModel> Add(Func<IPipeStep<TPipeModel>> pipeStep);
    }
    
    public interface IPipeServiceExecution<TPipeModel>
    {
        Task ExecuteAsync(TPipeModel pipeModel);
    } 
    
    と実装
    public class PipeService<TPipeModel> : IPipeService<TPipeModel>
    {
        private readonly IList<Func<IPipeStep<TPipeModel>>> _pipeSteps;
    
        public PipeService()
        {
            _pipeSteps = new List<Func<IPipeStep<TPipeModel>>>();          
        }
    
        public IPipeService<TPipeModel> Add(Func<IPipeStep<TPipeModel>> pipeStep)
        {
            _pipeSteps.Add(pipeStep);
            return this;
        }
    
        public async Task ExecuteAsync(TPipeModel pipeModel)
        {
            foreach (var pipeStep in _pipeSteps)
            {
                await pipeStep.Invoke().ExecuteAsync(pipeModel);
            }
        }
    } 
    
    それだ!簡単ですね.一般的なパイプの実装を使用する準備が整いました.最初のパイプラインを作りましょう.
    …を待つ.パイプラインの作成?何が最良の場所ですか?おそらく、それは多分、複数のステップを持っているでしょう?各々のステップは、別々のクラスによって代表されそうです.ステップは、一緒に、そして、最後にIPipeServiceExecution が返される.それは工場のための素晴らしい場所ではないか.そうですとも!
    Concrete Pipe Factoryの実装に利用されるパイプライン工場のさらに別の汎用インタフェースを作りましょう.工場は、単一の責任を持つことになっています.工場は常にすべての定義されたステップで完全なパイプラインを提供します.また、何が本当に重要です.CreatePipe メソッドリターンIPipeServiceExecution インターフェースなので、パイプがパイプから返されたときのみ実行可能です.
    public interface IPipeFactory<TPipeModel>
    {
        IPipeServiceExecution<TPipeModel> CreatePipe();
    }
    
    完全な、一般的なパイプラインの実装を見つけることができますhere .
    使用this link , 上記の実装を使用する“テキスト変換”パイプラインの例に移動できます.入力として、それはフォーマットされていないテキストをとって、定義されたステップに基づいて、それを所望の形式に変えます.
    各ステップをテストすることができますどのように簡単に注意してください.また、各ステップは常に単一の責任を持っています.また、オープンクローズド原則がある.それはすべて私たちの仮定リストから2点目はすでに満たされていることを意味-我々はしっかりした原則に従ってください.
    ちょうど追加側の注意:覚えて、手順は本当にパイプラインの問題を注文!
    エラー処理はどうですか?
    もちろん、各ステップごとに行うことができますし、パイプライン実行の外でキャッチできます.しかし、問題を処理するためにパイプラインモデルを使用する必要がある複数のエラー処理手順を定義する必要がある場合はどうなりますか?
    再び、単一の責任原則とユニットテストを念頭に置いて拡張しましょうIPipeService パイプが壊れた場合に実行されるエラー処理手順を追加する可能性があります.
    public interface IPipeService<TPipeModel> : IPipeServiceExecution<TPipeModel>
    {
        IPipeService<TPipeModel> Add(Func<IPipeStep<TPipeModel>> pipeStep);
        IPipeService<TPipeModel> AddErrorStep(Func<IPipeStep<TPipeModel>> errorStep);
    }
    
    エラー処理によるパイプラインパターンの最終実装を見つけることができますhere . 以下のコード例がありますthis link .