ホームページ  >  記事  >  バックエンド開発  >  強力なデザインパターンツールである「責任連鎖モデル」について詳しく話しましょう(Go実装プロセス付き)

強力なデザインパターンツールである「責任連鎖モデル」について詳しく話しましょう(Go実装プロセス付き)

藏色散人
藏色散人転載
2023-01-17 11:43:371721ブラウズ

この記事では、Golang のデザイン パターンに関する関連知識をお届けします。責任チェーン パターンとは何か、その価値、責任チェーン Go コードの具体的な実装方法を主に紹介します。一緒に見ていきましょう。それは困っている人たちを助けます。

今日もデザイン パターンに関する記事を更新していきます。テンプレート パターンと戦略パターンに関する前の 2 つの記事で、「テンプレート、戦略、そして責任連鎖の 3 つの設計パターンは、複雑で変化しやすいビジネス システム プロセスの問題点を解決する強力なツールです。」この記事では、3 番目のデザイン パターン ツールである責任連鎖パターンについて説明します。

責任連鎖モデル

責任の連鎖——英語名 Chain of relationship は、鎖と訳されることもあります。の担当モデル。インターネット上には責任連鎖と呼ばれる名前が他にもあるようですが、ここでは誰もがそれらが同じものであることを知っています。

これは動作設計パターンです。このパターンを使用すると、リクエストに対して複数のプロセッサで構成されるリンクを作成できます。各プロセッサは独自の責任を負い、互いに結合されていません。独自のタスクを完了した後、リクエスト オブジェクトはリンク内の次のプロセッサに渡されます。 . 処理用のプロセッサ。

責任の連鎖は、多くの一般的なフレームワークで使用されています。ミドルウェアやインターセプターなどのフレームワーク コンポーネントはすべて、この設計パターンを適用しています。これら 2 つのコンポーネントをより頻繁に使用する必要があります。 Webインターフェースを開発する場合、アクセスログの記録、トークンの解析、インターフェース応答の統一構造の整形など、同様のプロジェクトに共通する操作はすべてミドルウェアやインターセプターで完結するため、これらの基本操作をインターフェースに統合することができます。切り離された。

ミドルウェアとインターセプターのコンポーネントは、フレームワークによって設計されており、それらに直接挿入できます。今日の記事で説明するのは、責任の連鎖を中核的なビジネス プロセスに適用する方法です。単に基本的な公開操作を行うだけではなく、

#責任連鎖の価値 上記では、公共コンポーネントにおける責任連鎖のいくつかの応用について説明しました。プロジェクトの基本的な共通関数をコア ロジックの前処理と後処理に追加できます。しかし実際には、一部の中核ビジネスでは、

責任連鎖モデルを適用することで、ビジネス プロセスのステップを苦労なく拡張できます

たとえば、タオバオが設立された当初、買い物注文の処理プロセスは最初は次のようなものだったと思われます。

強力なデザインパターンツールである「責任連鎖モデル」について詳しく話しましょう(Go実装プロセス付き)責任連鎖モデル - 買い物注文 - 純粋バージョン

プロセス全体は比較的クリーンです

「ユーザー パラメーターの検証 - ショッピング カート データの検証 -」 「製品在庫の確認 – 運賃計算 – 在庫控除 – 注文生成」

、これをショッピング注文プロセスの純粋バージョンと呼びましょう。通常、これは製品が 0 から 1 になるときです。このプロセスは比較的純粋でオンラインです。ショッピングでは、商品を選択し、注文し、オンラインで支払うだけです。 しかし、私たちは皆、インターネット サーフィンのベテランであり、インターネット サーフィンのことに精通しています。このプロセスがあまりにも純粋なままであれば、会社の PM と運用部門が去ってしまう可能性があります。ショッピング ウェブサイトが稼働し、消費者が増えた後は、通常、売上を増やすために、特定のカテゴリの商品の全額割引などのプロモーション方法が追加されます。

運営も怠けてはいけません。顧客についてもっと話し、買い物祭りを作り、より多くのユーザーを引き付けるためにクーポンを手配します。このように、注文の際には、ショッピングカート内の商品が割引条件を満たしているか、クーポンを持っているか、クーポンを持っていれば値引きや減額が可能かを判断する必要があります。これは、社内の発注プロセスの途中に 2 つのサブプロセスを追加することに相当します。

強力なデザインパターンツールである「責任連鎖モデル」について詳しく話しましょう(Go実装プロセス付き)責任連鎖モデル - 買い物注文 - 体験版

新しく追加されたロジックを実装するには、少なくとも注文プロセスを作成する必要があります。 2 つの if else 分岐を追加すると、これら 2 つのロジックを追加できます。しかし、最も恐ろしいことは、

プロセス全体が結合されているため、変更後にプロセス全体をテストする必要があるということです

。上記の経験から、このプロセスは将来確実に拡張されることも知っておく必要があります。たとえば、コミュニティカットや注文などの機能を追加します。将来、注文生成プロセスにステップを追加するたびに、すでに書かれた手順を変更する必要がありますが、コードが怖いですか? 友人の中には、インターネット電子商取引のショッピング プロセスは確かに非常に多様であり、各企業のプロセスも異なると言う人もいるかもしれません。患者が医師の診察を受けるために病院に行く別の例を考えてみましょう。一般的に、患者が医師の診察を受けるための基本的な手順は次のとおりです:

登録—>クリニックで医師の診察—>料金所で支払う—>薬局で薬を受け取る

しかし、検査やフィルムなどが必要な患者もいるかもしれません。病院での治療のプロセスは次のとおりです:

登録—>初回診察—>ラジオ画像撮影診療科→再診室→担当窓口で支払い→薬局で薬を受け取る

そのため、患者様の状況に応じて診療の流れも多くなります。状態。

これで確信できます: プロセスのステップが修正されていない場合、元の開発およびテストされたプロセスを変更せずにプロセスにステップを追加するには、プロセス全体を分離する必要があります。プロセスのスケーラビリティを高めるためのさまざまなステップ。この場合、責任連鎖モデルを使用できます。このモデルでは、最初にプロセス リンクにステップを設定してから、 を実行できます。

責任連鎖モデルを使用してプロセスを実装する

責任連鎖を設計するように求められた場合、どのようにするかそれをデザインすべきでしょうか?どのようなメソッドを提供および実装する必要がありますか?プロセス内のステップを接続するためにそれをどのように使用しますか?ここでは、責任連鎖モデルを使用して、医師の診察を受けてデモンストレーションを行うというシナリオのプロセス ステップを実装します。ショッピングと注文のプロセスは似ています。自分で実装してみることができます。まず、構造を学びます。責任連鎖モデルを理解して、いくつかの模擬例を作成し、それをマスターしたら、ビジネス上の問題を解決するためにそれを使用してみることができます。

まず第一に、上記のプロセス拡張の問題点について考えることができます。プロセスの各ステップは、論理的な抽象化を完了するために処理オブジェクトによって完了する必要があります。すべての処理オブジェクトは、統一されたメソッドを提供する必要があります。独自のロジックを処理します。次に、次の処理オブジェクトへの参照も維持する必要があります。現在のステップ自身のロジックが処理された後、次のオブジェクトの処理メソッドが呼び出され、リクエストは処理のために後続のオブジェクトに渡されます。 、プロセスが終了するまで順番に処理が進みます。

要約すると、責任連鎖パターンを実装するオブジェクトには、少なくとも次の特性が含まれている必要があります:

  • メンバー属性
    • nextHandler: 次の待機 呼び出されたオブジェクト インスタンス
  • メンバー メソッド
    • SetNext: 次のオブジェクト インスタンスを現在のオブジェクトの nextHandlerAttributes;
    • Do: 現在のオブジェクトのビジネス ロジック エントリ (各処理オブジェクトが独自のロジックを実装する場所);
    • Execute : 責任者責任チェーン上のリクエストの処理と配信のために、現在のオブジェクトの Do を呼び出します。nextHandler が空でない場合は、nextHandler.Do## を呼び出します。 #;
UML クラス図に抽象化すると、次のようになります。

強力なデザインパターンツールである「責任連鎖モデル」について詳しく話しましょう(Go実装プロセス付き)

責任モード処理オブジェクトのチェーンのインターフェイス

Handler を定義します。これは、特定の処理のタイプである ConcreteHandler によって実装されます。物体。

上の図と上記のオブジェクト特性の分析を観察すると、

SetNextExecute の 2 つの動作がそれぞれ ConcreteHandler# であることがわかります。 ## これらはすべて同じであるため、これら 2 つは抽象処理型で実装でき、各特定の処理オブジェクトは抽象型を継承して繰り返し操作を減らします。 したがって、責任連鎖パターンの抽象化と洗練は、次の図に発展する可能性があります。

強力なデザインパターンツールである「責任連鎖モデル」について詳しく話しましょう(Go実装プロセス付き)責任連鎖パターンがどのように実装されるべきかを理解した後、インターフェイスと型の設計の観点から 最後に、コードの実装フェーズに入ります。責任連鎖モデルは、純粋なオブジェクト指向言語で実装するのに非常に便利です。上記の UML クラス図をインターフェイスと抽象クラスに直接変換するだけです。いくつかの実装クラスを作成すれば完了です。

上記の UML クラス図を Go コードに変換するのはまだ少し難しいです。ここでは、Go を使用して責任連鎖モデルを実装し、病院の治療プロセスを完了するコード例を示します。

責任連鎖 Go コードの実装Go は継承をサポートしていませんが、型の匿名の組み合わせを使用できます。これを実現するために、患者が病院に治療に行くプロセスを例として、具体的な例を以下に示します。

医療の具体的な流れは次のとおりです。

登録—>クリニックでの診察—>診療所での支払い—>病院で薬を受け取る薬局

当社の目標は、責任連鎖モデルを使用して、このプロセスの各ステップを相互に結合することなく実装し、プロセスへのステップの追加をサポートすることです。

まず、責任連鎖パターンの共通部分、つまり、パターンのインターフェイスと抽象クラスを実装しましょう。

type PatientHandler interface {
 Execute(*patient) error SetNext(PatientHandler) PatientHandler Do(*patient) error}// 充当抽象类型,实现公共方法,抽象方法不实现留给实现类自己实现type Next struct {
 nextHandler PatientHandler}func (n *Next) SetNext(handler PatientHandler) PatientHandler {
 n.nextHandler = handler return handler}func (n *Next) Execute(patient *patient) (err error) {
 // 调用不到外部类型的 Do 方法,所以 Next 不能实现 Do 方法
 if n.nextHandler != nil {
  if err = n.nextHandler.Do(patient); err != nil {
   return
  }

  return n.nextHandler.Execute(patient)
 }

 return}

上記の

Next 型コードは pattern 内の抽象クラスの役割として機能します。ここでは、この Next 型に焦点を当てましょう。

在我们的职责链的UML图里有说明Do方法是一个抽象方法,留给具体处理请求的类来实现,所以这里Next类型充当抽象类型,只实现公共方法,抽象方法留给实现类自己实现。并且由于 Go 并不支持继承,即使Next实现了Do方法,也不能达到在父类方法中调用子类方法的效果—即在我们的例子里面用Next 类型的Execute方法调用不到外部实现类型的Do方法。

所以我们这里选择Next类型直接不实现Do方法,这也是在暗示这个类型是专门用作让实现类进行内嵌组合使用的。

接下来我们定义职责链要处理的请求,再回看一下我们的UML图,实现处理逻辑和请求传递的DoExecute方法的参数都是流程中要处理的请求。这里是医院接诊的流程,所以我们定义一个患者类作为流程的请求。

//流程中的请求类--患者type patient struct {
 Name              string
 RegistrationDone  bool
 DoctorCheckUpDone bool
 MedicineDone      bool
 PaymentDone       bool}

然后我们按照挂号—>诊室看病—>收费处缴费—>药房拿药这个流程定义四个步骤的处理类,来分别实现每个环节的逻辑。

// Reception 挂号处处理器type Reception struct {
 Next}func (r *Reception) Do(p *patient) (err error) {
 if p.RegistrationDone {
  fmt.Println("Patient registration already done")
  return
 }
 fmt.Println("Reception registering patient")
 p.RegistrationDone = true
 return}// Clinic 诊室处理器--用于医生给病人看病type Clinic struct {
 Next}func (d *Clinic) Do(p *patient) (err error) {
 if p.DoctorCheckUpDone {
  fmt.Println("Doctor checkup already done")
  return
 }
 fmt.Println("Doctor checking patient")
 p.DoctorCheckUpDone = true
 return}// Cashier 收费处处理器type Cashier struct {
 Next}func (c *Cashier) Do(p *patient) (err error) {
 if p.PaymentDone {
  fmt.Println("Payment Done")
  return
 }
 fmt.Println("Cashier getting money from patient patient")
 p.PaymentDone = true
 return}// Pharmacy 药房处理器type Pharmacy struct {
 Next}func (m *Pharmacy) Do (p *patient) (err error) {
 if p.MedicineDone {
  fmt.Println("Medicine already given to patient")
  return
 }
 fmt.Println("Pharmacy giving medicine to patient")
 p.MedicineDone = true
 return}

处理器定义好了,怎么给用他们串成患者就诊这个流程呢?

func main() {
 receptionHandler := &Reception{}
 patient := &patient{Name: "abc"}
 // 设置病人看病的链路
 receptionHandler.SetNext(&Clinic{}).SetNext(&Cashier{}).SetNext(&Pharmacy{})
  receptionHandler.Execute(patient)}

上面的链式调用看起来是不是很清爽,嘿嘿别高兴太早,这里边有个BUG— 即Reception接诊挂号这个步骤提供的逻辑没有调用到,所以我们这里再定义个StartHandler 类型,它不提供处理实现只是作为第一个Handler向下转发请求

// StartHandler 不做操作,作为第一个Handler向下转发请求type StartHandler struct {
 Next}// Do 空Handler的Dofunc (h *StartHandler) Do(c *patient) (err error) {
 // 空Handler 这里什么也不做 只是载体 do nothing...
 return}

这也是Go 语法限制,公共方法Exeute并不能像面向对象那样先调用this.Do 再调用this.nextHandler.Do 具体原因咱们上边已经解释过了,如果觉得不清楚的可以拿Java实现一遍看看区别,再琢磨一下为啥Go里边不行。

所以整个流程每个环节都能被正确执行到,应该这样把处理类串起来。

func main() {
 patientHealthHandler := StartHandler{}
 //
 patient := &patient{Name: "abc"}
 // 设置病人看病的链路
 patientHealthHandler.SetNext(&Reception{}).// 挂号
  SetNext(&Clinic{}). // 诊室看病
  SetNext(&Cashier{}). // 收费处交钱
  SetNext(&Pharmacy{}) // 药房拿药
 //还可以扩展,比如中间加入化验科化验,图像科拍片等等

 // 执行上面设置好的业务流程
 if err := patientHealthHandler.Execute(patient); err != nil {
  // 异常
  fmt.Println("Fail | Error:" + err.Error())
  return
 }
 // 成功
 fmt.Println("Success")}

总结

职责链模式所拥有的特点让流程中的每个处理节点都只需关注满足自己处理条件的请求进行处理即可,对于不感兴趣的请求,会直接转发给下一个节点对象进行处理。

另外职责链也可以设置中止条件,针对我们文中的例子就是在Execute方法里加判断,一旦满足中止后就不再继续往链路的下级节点传递请求。Gin 的中间件的abort方法就是按照这个原理实现的,同时这也是职责链跟装饰器模式的一个区别,装饰器模式无法在增强实体的过程中停止,只能执行完整个装饰链路。

后面大家可以看看针对那些可能未来经常会变的核心业务流程,可以在设计初期就考虑使用职责链来实现,减轻未来流程不停迭代时不好扩展的痛点。当然职责链也不是万能的,对于那些固定的流程显然是不适合的。咱们千万不要手里拿着锤子就看什么都是钉子,所有的设计模式一定要用在合适的地方。

既然这里提到了装饰器,那么下一期就写写装饰器吧,不对,装饰器算是代理模式的一个特殊应用,那就还是先介绍代理未来再介绍装饰器吧,这样阅读体验会更好一些。

喜欢这系列文章的朋友们还请多多关注,转发起来吧。

推荐学习:《go视频教程

以上が強力なデザインパターンツールである「責任連鎖モデル」について詳しく話しましょう(Go実装プロセス付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlearnku.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。