golang设计模式——中介模式

中介模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-627Y6Nue-1661349537075)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220824095652355.png)]

中介者模式是设计模式系列中最后一个模式。中介者模式能帮大家更好的维护系统。

中介者模式:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变他们之间的交互。

UML

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CCXOusCF-1661349537076)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220824100719886.png)]

分析

对于定义,大家比较容易理解。由中介者控制所有的交互,Colleague对象无需相互感知。

使用中介者模式有个前提,Colleague之间相互影响,即一个Colleague的变化会影响其它Colleague。

我们再来看UML。通过UML可以看出

  • Mediator包含所有Colleague,这是中介者能控制所有交互的基础
  • Colleague包含Mediator,Colleague变化可直接通知Mediator,这是Colleague无需知道其它Colleague的基础,只需知道Mediator即可

根据前面提到的前提和UML类图,我们思考的再深一些,ConcreteColleague是否有相同父类并不重要,只要Mediator包含所有ConcreteColleague,ConcreteColleague有Mediator,中介者模式依然有效。

应用场景

使用中介者模式有哪些好处?

我们设想一种场景,假设有多个Colleague,分别为Colleague1、Colleague2、Colleague3、Colleague4,如果一个Colleague的动作会影响到其它Colleague,便形成多对多的网状结构,随着Colleague增加,会更加难以管理。如果使用中介者模式,会变成一对多的星状结构,所有Colleague只和Mediator相关,管理复杂度降低。

在实际工作中,没碰到过网状结构,所以没用过中介者模式。比较合适的例子,如GoLand中的注册,点击Active Goland/Evaluate for free,会显示不同的UI组件。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8JquMClb-1661349537077)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220824214549199.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vTEo9hfh-1661349537077)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220824214602978.png)]

代码实现

我们简单实现GoLand激活的例子。

package main

import "fmt"

/**
 * @Description: 中介者接口
 */
type Mediator interface {
	Process(colleague UIColleague)
}

/**
 * @Description: 真正的中介者
 */
type UIMediator struct {
	activate *ActivateColleague
	evaluate *EvaluateColleague
	text     *TextColleague
}

/**
 * @Description: 中介者大总管,处理所有事情
 * @receiver u
 */
func (u *UIMediator) Process(colleague UIColleague) {
	if colleague == u.activate { //如果是激活
		u.evaluate.Show("试用内容隐藏")
		u.text.Show("请输入激活码")
	} else if colleague == u.evaluate { //如果是试用
		u.activate.Show("激活内容隐藏")
		u.text.Show("请出入激活时间")
	}
}

/**
 * @Description: Colleague接口
 */
type UIColleague interface {
	Action()
}

/**
 * @Description: 激活UI
 */
type ActivateColleague struct {
	mediator Mediator
}

/**
 * @Description: 激活触发的动作
 * @receiver a
 */
func (a *ActivateColleague) Action() {
	a.mediator.Process(a)
}

/**
 * @Description: 激活UI显示内容
 * @receiver e
 * @param text
 */
func (e *ActivateColleague) Show(text string) {
	fmt.Println(text)
}

/**
 * @Description: 试用UI
 */
type EvaluateColleague struct {
	mediator Mediator
}

/**
 * @Description: 试用触发的动作
 * @receiver e
 */
func (e *EvaluateColleague) Action() {
	e.mediator.Process(e)
}

/**
 * @Description: 试用UI显示内容
 * @receiver e
 * @param text
 */
func (e *EvaluateColleague) Show(text string) {
	fmt.Println(text)
}

/**
 * @Description: 文案UI
 */
type TextColleague struct {
	mediator Mediator
}

/**
 * @Description: 文案触发动作
 * @receiver t
 */
func (t *TextColleague) Action() {
	t.mediator.Process(t)
}

/**
 * @Description: 文案显示内容
 * @receiver t
 * @param text
 */
func (t *TextColleague) Show(text string) {
	fmt.Println(text)
}

func main() {
	//初始化
	m := &UIMediator{}

	activate := &ActivateColleague{
		mediator: m,
	}
	evaluate := &EvaluateColleague{
		mediator: m,
	}
	text := &TextColleague{
		mediator: m,
	}

	m.activate = activate
	m.evaluate = evaluate
	m.text = text
	//点击激活
	fmt.Println("-----------------点击激活")
	activate.Action()
	//点击试用
	fmt.Println("-----------------点击试用")
	evaluate.Action()
}

输出

—————–点击激活

试用内容隐藏

请输入激活码

—————–点击试用

激活内容隐藏

请出入激活时间

代码中可以看出,Mediator有所有的UI,UI只调用Mediator,无需知道其它UI,Mediator通过Process处理所有操作。当今后增加新UI,只需Mediator知道即可,不必改动其它UI。当然,要防止Mediator变成超级大类,这需要根据实际情况做取舍。

实例

代码

// Package mediator 中介模式
// 采用原课程的示例,并且做了一些裁剪
// 假设我们现在有一个较为复杂的对话框,里面包括登录组件,注册组件,以及选择框
// 当选择框选择“登录”时,展示登录相关组件
// 当选择框选择“注册”时,展示注册相关组件
package mediator

import (
	"fmt"
	"reflect"
)

// Input 假设这表示一个输入框
type Input string

// String String
func (i Input) String() string {
	return string(i)
}

// Selection 假设这表示一个选择框
type Selection string

// Selected 当前选中的对象
func (s Selection) Selected() string {
	return string(s)
}

// Button 假设这表示一个按钮
type Button struct {
	onClick func()
}

// SetonClick 添加点击事件回调
func (b *Button) SetonClick(f func()) {
	b.onClick = f
}

// IMediator 中介模式接口
type IMediator interface {
	HandleEvent(component interface{})
}

// Dialog 对话框组件
type Dialog struct {
	LoginButton         *Button
	RegButton           *Button
	Selection           *Selection
	UsernameInput       *Input
	PasswordInput       *Input
	RepeatPasswordInput *Input
}

// HandleEvent HandleEvent
func (d *Dialog) HandleEvent(component interface{}) {
	switch {
	case reflect.DeepEqual(component, d.Selection):
		if d.Selection.Selected() == "登录" {
			fmt.Println("select login")
			fmt.Printf("show: %s\n", d.UsernameInput)
			fmt.Printf("show: %s\n", d.PasswordInput)
		} else if d.Selection.Selected() == "注册" {
			fmt.Println("select register")
			fmt.Printf("show: %s\n", d.UsernameInput)
			fmt.Printf("show: %s\n", d.PasswordInput)
			fmt.Printf("show: %s\n", d.RepeatPasswordInput)
		}
		// others, 如果点击了登录按钮,注册按钮
	}
}

测试

package mediator

import "testing"

func TestDemo(t *testing.T) {
	usernameInput := Input("username input")
	passwordInput := Input("password input")
	repeatPwdInput := Input("repeat password input")

	selection := Selection("登录")
	d := &Dialog{
		Selection:           &selection,
		UsernameInput:       &usernameInput,
		PasswordInput:       &passwordInput,
		RepeatPasswordInput: &repeatPwdInput,
	}
	d.HandleEvent(&selection)

	regSelection := Selection("注册")
	d.Selection = &regSelection
	d.HandleEvent(&regSelection)
}

总结

中介者模式算是聚而歼之的模式,因为从分而治之上讲,对应的操作应该让每一个Colleague自行管理,但这样做,Colleague之间需要相互了解,沟通成本太高。

如果收拢到Mediator,整体效率会高很多,但Mediator存在成为上帝类的可能,反而导致不好维护。

这是不是特别像实际生活。所以说世上的处理方案没有定法,能够更好的解决问题就是最好的。共勉。

相关文章

显卡天梯图2024最新版,显卡是电脑进行图形处理的重要设备,...
初始化电脑时出现问题怎么办,可以使用win系统的安装介质,连...
todesk远程开机怎么设置,两台电脑要在同一局域网内,然后需...
油猴谷歌插件怎么安装,可以通过谷歌应用商店进行安装,需要...
虚拟内存这个名词想必很多人都听说过,我们在使用电脑的时候...