浅谈golang中的代理模式

浅谈golang中的代理模式来自一个大佬的博客,建议食用设计模式不分语言,是一种思维层面的体现,但是不能在不同语言中使用同一套实现(每种语言有不同的特性),比如go,本身是没有继承一说,但是通过结构体的组合来实现语义上的继承。而多态也是通过接口的方式来实现的。下方的图来自于大佬博客,贴在这里方便查看!!!设计原则设计模式结构型模式代理模式首先,我们知道代理模式中分为静态代理和动态代理。静态代理需要在编译前就要写好,而动态代理需要在运行时通过反射来实现方法增强。上述的话,太过粗糙,下面列举一下双方的区别:静态代理:

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

来自一个大佬的博客,建议食用

设计模式不分语言,是一种思维层面的体现,但是不能在不同语言中使用同一套实现(每种语言有不同的特性),比如go,本身是没有继承一说,但是通过结构体的组合来实现语义上的继承。而多态也是通过接口的方式来实现的。

下方的图来自于大佬博客,贴在这里方便查看!!!

设计原则

在这里插入图片描述

设计模式

在这里插入图片描述

结构型模式

代理模式

首先,我们知道代理模式中分为静态代理和动态代理。静态代理需要在编译前就要写好,而动态代理需要在运行时通过反射来实现方法增强。
上述的话,太过粗糙,下面列举一下双方的区别:

静态代理:

  • 代理类实现和目标类相同的接口,每个类都单独编辑一个代理类。
  • 我们需要在代理类中,将目标类中的所有方法都要重新实现,并且为每个方法都附加相似的代码逻辑。
  • 如果要添加方法增强的类不止一个,我们需要对每个类都创建一个代理类。

动态代理:

  • 不需要为每个目标类编辑代理类。
  • 在程序运行时,系统会动态地创建代理类,然后用代理类替换掉原始类。
  • 一般采用反射实现。

代理模式的优点:

  • 代理模式能将代理对象与真实被调用目标对象分离。
  • 在一定程度上降低了系统的耦合性,拓展性好。
  • 可以起到保护目标对象的作用。
  • 可以增强目标对象的功能。

代理的应用场景:

  • 监控
  • 统计
  • 鉴权
  • 限流
  • 事务

静态代理

这里很好看懂,不需过多赘述。

import (
	"fmt"
	"time"
)

//静态代理
//就是对目标类提前实现一个代理类,在调用时使用代理类即可
//代理时需要实现目标类的全部方法

type IUser interface { 
   
	Login(username, password string) error
}

type User struct { 
   
}

func (u *User) Login(username, password string) error { 
   
	//这里就是目标类的函数处理逻辑
	return nil
}

//接下来是代理上述User结构体
type UserProxy struct { 
   
	//有点挟天子以令诸侯,哈哈哈,开个玩笑
	user *User
}

func NewUserProxy(user *User) *UserProxy { 
   
	//返回静态代理类
	return &UserProxy{ 
   
		user: user,
	}
}

func (p *UserProxy) Login(username, password string) error { 
   
	//这里就代理一个程序消耗计时功能
	start := time.Now()
	err := p.user.Login(username, password)
	if err != nil { 
   
		return err
	}
	
	//其实这里用defer栈也可以
	fmt.Printf("调用该程序用时:%v",time.Since(start))
	return nil
}

动态代理

思路:仿照java动态代理的思路,通过InvocationHandler来提供Invoke接口,然后用Proxy结合接口去生成代理类,这个接口需要用户自己实现自有逻辑。

//提供动态调用方法接口
type InvocationHandler interface { 
   
	Invoke(proxy *Proxy, method *Method, args []interface{ 
   }) ([]interface{ 
   }, error)
}

//代理,用来总管代理类的生成
type Proxy struct { 
   
	target  interface{ 
   }        //目标类,后面的类型和java的Object一样
	methods map[string]*Method //map用来装载待增强的不同的方法
	handle  InvocationHandler  //用来暴露统一invoke接口,类似多态
}

//创建新的代理
func NewProxy(target interface{ 
   }, h InvocationHandler) *Proxy { 
   
	typ := reflect.TypeOf(target)          //用来显示目标类动态的真实类型
	value := reflect.ValueOf(target)       //获取目标类的值
	methods := make(map[string]*Method, 0) //初始化目标类的方法map
	//将目标类的方法逐个装载
	for i := 0; i < value.NumMethod(); i++ { 
   
		method := value.Method(i)
		methods[typ.Method(i).Name] = &Method{ 
   value: method}
	}
	return &Proxy{ 
   target: target, methods: methods, handle: h}
}

//代理调用代理方法
func (p *Proxy) InvokeMethod(name string, args ...interface{ 
   }) ([]interface{ 
   }, error) { 
   
	return p.handle.Invoke(p, p.methods[name], args)
}

//用来承载目标类的方法定位和调用
type Method struct { 
   
	value reflect.Value //用来装载方法实例
}

//这里相当于调用原方法,在该方法外可以做方法增强,需要调用者自己实现!!!
func (m *Method) Invoke(args ...interface{ 
   }) (res []interface{ 
   }, err error) { 
   
	defer func() { 
   
		//用来捕捉异常
		if p := recover(); p != nil { 
   
			err = errors.New(fmt.Sprintf("%s", p))
		}
	}()

	//处理参数
	params := make([]reflect.Value, 0)
	if args != nil { 
   
		for i := 0; i < len(args); i++ { 
   
			params = append(params, reflect.ValueOf(args[i]))
		}
	}

	//调用方法
	call := m.value.Call(params)

	//接收返回值
	res = make([]interface{ 
   }, 0)
	if call != nil && len(call) > 0 { 
   
		for i := 0; i < len(call); i++ { 
   
			res = append(res, call[i].Interface())
		}
	}
	return
}

测试代码:

func TestMethod_Invoke(t *testing.T) { 
   
	//这里对活动时长做统计
	people := &People{ 
   }   //创建目标类
	h := new(PeopleProxy) //创建接口实现类
	proxy := NewProxy(people, h)
	//调用方法
	ret, err := proxy.InvokeMethod("Work", "打游戏", "学习")
	if err != nil { 
   
		fmt.Println(err)
	}

	fmt.Println(ret)
}

//目标类
type People struct { 
   
}

func (p *People) Work(content string, next string) string { 
   
	fmt.Println("活动内容是:" + content + ",接下来需要做:" + next)
	return "all right"
}

//用户需要自己实现的增强内容,需要实现InvocationHandler接口
type PeopleProxy struct { 
   
}

//在这里做方法增强
func (p *PeopleProxy) Invoke(proxy *Proxy, method *Method, args []interface{ 
   }) ([]interface{ 
   }, error) { 
   
	start := time.Now()
	defer fmt.Printf("耗时:%v\n", time.Since(start))
	fmt.Println("before method")
	invoke, err := method.Invoke(args...)
	fmt.Println("after method")
	return invoke, err
}

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/159492.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • ftp服务器映射到外网_内网穿透安全吗

    ftp服务器映射到外网_内网穿透安全吗转载:https://service.oray.com/question/752.html花生壳服务目前的用户量已达1600万以上,广泛应用于网站建设、视频监控、遥感测绘、FTP、VPN、企业OA、ERP等应用领域。花生壳服务可以将您的动态公网IP和域名进行实时绑定。简单来说,就像您的手机一样,无论在那里,只要通过一个号码就可以找到您了,而这个号码就相于在贝锐使用花生壳服务的域名。现在我们以实例讲解如何使用花生壳来协助搭建FTP服务,从而实现FTP服务的外网访问。需要使用的软件工具:花生壳8客户端和SE

  • intelj idea 2021 激活码[免费获取]

    (intelj idea 2021 激活码)最近有小伙伴私信我,问我这边有没有免费的intellijIdea的激活码,然后我将全栈君台教程分享给他了。激活成功之后他一直表示感谢,哈哈~https://javaforall.cn/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~MLZP…

  • 动态规划 之背包问题(九讲)[通俗易懂]

    动态规划 之背包问题(九讲)[通俗易懂]背包九讲参考:"AcWing题库"参考书目:"背包九讲"1、01背包问题题目描述:有N件物品和一个容量是V的背包。每件物品只能使用一次。第i

  • 武侠世界2-健壮性

    前几周就获得的武侠世界2的源代码,一直没有时间表去看。从网上搞来的武侠世界2的源代码,能编译通过,大的问题没有,小问题还是挺多。其它的细节,大家其实可以在网上搜索一下。下面的游戏运行的截图:我还把角色升到2级呢,废话少说,直奔主题。1、在windows下代码的健壮性打开World.sln,工程的main函数在World.cpp里面。开始部分我们能看到#ifdefined(…

  • RSA加密算法原理

    RSA加密算法原理

  • Topk算法_topn算法

    Topk算法_topn算法topK算法思路1:可以采用快速选择算法,借助快排,设mid为每次划分中间结果,每次划分完之后如果mid==k,则说明序列刚刚好,第k位置和他前面的位置都是前K大的数,如果mid < k,则说明第K大的元素在后半部分,则前半部分肯定是前K大的数,只需从后半部分找k – mid大的数即可,否则如果mid > k,则说明第K大的数在前半部分,只需从前半部分找前K大的数字即可。时间复杂度:假设每次划分的mid都在中间,每层都只是对一半做划分,所以每次划分的数据量为n,n/2,n/4,n/8…一

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号