Dieser Artikel vermittelt Ihnen relevantes Wissen über das Verantwortungskettenmuster und seinen Wert sowie die spezifische Implementierungsmethode des Verantwortungsketten-Go-Codes wird Ihnen hilfreich sein.
Heute werde ich weiterhin Artikel zu Designmustern aktualisieren. In den beiden vorherigen Artikeln zu Vorlagenmustern und Strategiemustern habe ich Ihnen eine „gewalttätige Theorie“ erzählt, die ich zusammengefasst habe: „Drei Designmuster: Vorlage, Strategie und Kette von.“ Verantwortung. Es ist ein leistungsstarkes Werkzeug, um die Probleme komplexer und veränderlicher Geschäftssystemprozesse zu lösen. Lassen Sie uns in diesem Artikel über das dritte Designmuster-Tool sprechen – das Chain-of-Responsibility-Muster.
. Als Taobao beispielsweise zum ersten Mal gegründet wurde, war der Prozess der Auftragsabwicklung möglicherweise zunächst so.
Chain-of-Responsibility-Modell – Einkaufsbestellung – reine Version
Der gesamte Prozess ist relativ sauber
„Benutzerparameterüberprüfung – Warenkorbdatenüberprüfung – Produktbestandsüberprüfung – Frachtberechnung – Bestandsabzug – Auftragsgenerierung“, Lass uns Man kann es die reine Version des Einkaufsbestellvorgangs nennen. Dies geschieht normalerweise, wenn das Produkt von 0 auf 1 wechselt. Beim Online-Einkauf können Sie einfach Produkte auswählen, Bestellungen aufgeben und online bezahlen. Aber wir sind alle Veteranen des Internet-Surfens und wir sind alle damit vertraut. Wenn dieser Prozess so rein bleibt, können der PM und der Betrieb des Unternehmens verschwinden. Sobald die Shopping-Website in Betrieb ist und es Verbraucher gibt, werden zur Steigerung des Umsatzes in der Regel einige Werbemethoden wie volle Rabatte auf bestimmte Warenkategorien hinzugefügt.
Der Betrieb darf nicht untätig bleiben, mehr über Kunden sprechen, ein Einkaufsfestival veranstalten und dann Gutscheine arrangieren, um mehr Benutzer anzulocken. Auf diese Weise muss während des Bestellvorgangs festgestellt werden, ob die Produkte im Warenkorb die Rabattbedingungen erfüllen, ob der Benutzer über einen Gutschein verfügt und wenn ja, kann der Betrag reduziert oder reduziert werden. Dies entspricht dem Hinzufügen von zwei Unterprozessen mitten in unserem internen Bestellprozess.
Chain of Responsibility-Modell – Einkaufsbestellung – erfahrene Version
Um die neu hinzugefügte Logik zu implementieren, müssen wir dem schriftlichen Bestellprozess mindestens zwei If-Else-Zweige hinzufügen, um diese beiden Logiken hinzuzufügen. Aber das Schlimmste ist: Da der gesamte Prozess miteinander gekoppelt ist, müssen wir nach der Änderung den gesamten Prozess testen. Und mit den oben genannten Erfahrungen sollten wir auch wissen, dass dieser Prozess in Zukunft definitiv erweitert wird. Beispielsweise werden wir in Zukunft jedes Mal, wenn Sie Schritte zum Bestellgenerierungsprozess hinzufügen, hinzufügen. Sie müssen die bereits geschriebenen Schritte ändern. Haben Sie Angst vor dem Code?
Einige Freunde mögen sagen, dass der Prozess des Internet-E-Commerce-Einkaufs tatsächlich sehr unterschiedlich sein kann und der Prozess jedes Unternehmens anders ist. Nehmen wir ein weiteres Beispiel eines Patienten, der ins Krankenhaus geht, um einen Arzt aufzusuchen. Die grundlegenden Schritte für einen Patienten, einen Arzt aufzusuchen, sind:
Anmeldung –>Aufsuchen eines Arztes in der Klinik –>Bezahlen an der Kasse – >Medikamente in der Apotheke besorgenEs kann jedoch Patienten geben, die Labortests, Filme usw. benötigen. Ihr medizinischer Behandlungsprozess im Krankenhaus kann wie folgt ablaufen:
Anmeldung –>Erstkonsultation –>Röntgenaufnahme in der Bildgebungsabteilung –>Wiederholungsraum – >Zahlung im Büro berechnen -> Medikamente in der Apotheke besorgen
Daher wird der Prozess der medizinischen Behandlung je nach Zustand des Patienten auch die Anzahl der Schritte erhöhen.
Jetzt können wir bestätigen: Wenn die Schritte eines Prozesses nicht festgelegt sind, müssen wir die verschiedenen Schritte im gesamten Prozess entkoppeln, um Schritte zum Prozess hinzuzufügen, ohne den ursprünglich entwickelten und getesteten Prozess zu ändern Skalierbarkeit des Prozesses: Sie können das Chain-of-Responsibility-Modell verwenden. Dieses Modell ermöglicht es uns, zunächst die Schritte in der Prozessverknüpfung festzulegen und sie dann auszuführen.
nextHandler
: Die nächste Objektinstanz, die darauf wartet, aufgerufen zu werdennextHandler
: 下一个等待被调用的对象实例SetNext
: 把下一个对象的实例绑定到当前对象的nextHandler
属性上;Do
: 当前对象业务逻辑入口,他是每个处理对象实现自己逻辑的地方;Execute
: 负责职责链上请求的处理和传递;它会调用当前对象的Do
,nextHandler
不为空则调用nextHandler.Do
;如果抽象成 UML 类图表示的话,差不多就是下面这个样子。
定义了一个职责链模式处理对象的接口Handler
,由ConcreteHandler
–具体处理对象的类型来实现。
观察上图以及上面对象特性的分析,其实是能看出 SetNext
和 Execute
这两个行为是每个 ConcreteHandler
都一样的,所以这两个可以交给抽象处理类型来实现,每个具体处理对象再继承抽象类型,即可减少重复操作。
所以责任链模式的抽象和提炼可以进化成下图这样:
了解完职责链模式从接口和类型设计上应该怎么实现后,我们进入代码实现环节,职责链模式如果用纯面向对象的语言实现起来还是很方便的,把上面的UML类图直接翻译成接口、抽象类,再搞几个实现类就完事。
想把上面这个UML类图翻译成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
类型充当了模式中抽象类的角色,关于这个Next
SetNext
: Binden Sie die Instanz des nächsten Objekts an die nextHandler
-Eigenschaft des aktuellen Objekts 🎜 Do
: Der Geschäftslogikeintrag des aktuellen Objekts, in dem jedes Verarbeitungsobjekt seine eigene Logik implementiert. 🎜Execute
: Verantwortlich für die Verarbeitung und Zustellung von Anforderungen Die Verantwortungskette wird aufgerufen. Wenn Do
des aktuellen Objekts nextHandler
nicht leer ist, rufen Sie nextHandler.Do ul>🎜Wenn es in ein UML-Klassendiagramm abstrahiert würde, würde es wie folgt aussehen. 🎜🎜<img src="https://img.php.cn/upload/article/000/000/020/00d6dfe5b5918c83d994b1f96d5eb29b-2.png" alt="Sind Sie auch ein Geschäftsentwickler? Verwenden Sie dieses Designmuster im Voraus, um Produktnachfrage verhindern. Bar">🎜🎜 definiert eine Schnittstelle <code>Handler
für Verarbeitungsobjekte im Verantwortungskettenmodus, die durch ConcreteHandler
implementiert wird - den Typ des spezifischen Verarbeitungsobjekts. 🎜🎜Wenn wir das obige Bild und die Analyse der Objekteigenschaften oben betrachten, können wir tatsächlich erkennen, dass die beiden Verhaltensweisen von SetNext
und Execute
für jeden ConcreteHandler Dasselbe, sodass diese beiden durch abstrakte Verarbeitungstypen implementiert werden können und jedes spezifische Verarbeitungsobjekt den abstrakten Typ erbt, um wiederholte Vorgänge zu reduzieren. 🎜🎜So kann sich die Abstraktion und Verfeinerung des Verantwortungskettenmodells zu der folgenden Abbildung entwickeln: 🎜🎜<img src="https://img.php.cn/upload/article/000/000/020/2825a64757aefc1a1e4a326bf16d28e4-3.%20png" alt="Sind Sie auch ein Geschäftsentwickler? Verwenden Sie dieses Designmuster im Voraus, um zusätzliche Produktanforderungen zu vermeiden">🎜🎜Nachdem wir verstanden haben, wie das Chain-of-Responsibility-Modell in Bezug auf Schnittstellen- und Schriftdesign implementiert werden sollte, geben wir das ein Wenn das Chain-of-Responsibility-Modell verwendet wird, ist es immer noch sehr praktisch, das obige UML-Klassendiagramm direkt in Schnittstellen und abstrakte Klassen zu übersetzen und dann einige Implementierungsklassen zu erstellen Erledigt. 🎜🎜Es ist immer noch etwas schwierig, das obige UML-Klassendiagramm in Go-Code zu übersetzen. Hier stellen wir ein Codebeispiel bereit, das Go verwendet, um das Verantwortungskettenmodell zu implementieren und den Krankenhausbehandlungsprozess abzuschließen. 🎜🎜<a name="<strong>%E8%81%8C%E8%B4%A3%E9%93%BE%20Go%20%E4%BB%A3%E7%A0%81%E5%AE%%209E%E7%8E%B0</strong>">🎜🎜🎜🎜Verantwortungskette Go-Code-Implementierung🎜🎜🎜Obwohl Go die Vererbung nicht unterstützt, können wir dennoch anonyme Typkombinationen verwenden, um sie zu implementieren. Das Folgende ist ein geduldiger Prozess Ins Krankenhaus gehen, um einen Arzt aufzusuchen. Ein konkretes Beispiel wird am Beispiel des Verarbeitungsablaufs gegeben. 🎜🎜Der konkrete Ablauf eines Arztbesuchs ist wie folgt: 🎜🎜🎜Registrierung –>Aufsuchen eines Arztes in der Klinik –>Bezahlen an der Gebührenstelle –>Medikamente in der Apotheke bekommen🎜🎜🎜Unser Ziel ist die Verwendung Das Chain-of-Responsibility-Modell dient dazu, jeden Schritt in diesem Prozess zu realisieren. Sie sind nicht miteinander gekoppelt und unterstützen das Hinzufügen von Schritten zum Prozess. 🎜🎜Zuerst implementieren wir die öffentlichen Teile des Chain-of-Responsibility-Musters – also die Schnittstelle und die abstrakte Klasse des Musters🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">//流程中的请求类--患者type patient struct {
Name string
RegistrationDone bool
DoctorCheckUpDone bool
MedicineDone bool
PaymentDone bool}</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div>🎜Der Typ <code>Next
im obigen Code fungiert als abstrakte Klasse im Muster . Über diesen Weiter Der Typ
wird hier hervorgehoben. 🎜在我们的职责链的UML图里有说明Do
方法是一个抽象方法,留给具体处理请求的类来实现,所以这里Next
类型充当抽象类型,只实现公共方法,抽象方法留给实现类自己实现。并且由于 Go 并不支持继承,即使Next
实现了Do
方法,也不能达到在父类方法中调用子类方法的效果—即在我们的例子里面用Next
类型的Execute
方法调用不到外部实现类型的Do
方法。
所以我们这里选择Next
类型直接不实现Do
方法,这也是在暗示这个类型是专门用作让实现类进行内嵌组合使用的。
接下来我们定义职责链要处理的请求,再回看一下我们的UML图,实现处理逻辑和请求传递的Do
、Execute
方法的参数都是流程中要处理的请求。这里是医院接诊的流程,所以我们定义一个患者类作为流程的请求。
//流程中的请求类--患者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视频教程》
Das obige ist der detaillierte Inhalt vonLassen Sie uns ausführlich über das „Chain of Responsibility Model' sprechen, ein leistungsstarkes Design-Pattern-Tool (mit Go-Implementierungsprozess).. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!