计划写一系列基于golang语言面向对象和设计模式的文章,此系列将结合golang代码实现介绍一些常用的设计模式。设计模式主要包括Gang of Four的经典书籍里面的三大类型包括创建型(Creational)行为型(Behavioral)和结构型(Structural),本篇为开篇第一篇。首先介绍一下面向对象和go语言中面向对象的方法。
面向对象(OOP)的编程方法是当前高级语言编程比如C++, Java,python等常用的编程思想。面向对象网上资料很多这里仅作简单的介绍。面向对象编程的核心思想就是将代码分成许多小的对象(object),每个对象都有自己的属性和行为。属性描述了对象当前的状态,行为描述了对象可以做什么事情。行为通常被抽象成一个函数供调用,在OOP中这个函数也被称为方法(method)。
类(class)是具有相同属性和方法的一组对象的集合,它可以用来创建并初始化对象。在设计类的时候一个关键的指导原则就是封装。封装简单来说就是将代码及其处理的数据绑定在一起,形成一个独立单位,对外实现完整功能,并尽可能隐藏对象的内部细节。在代码实现过程中我们并不能将所有的实现都封装在一个大类里面,这样写出的代码是几乎无法维护的。而应该是将其拆成许多可维护的小类。
在有些时候我们会发现一些有相同的属性和方法的类或对象,例如我们在开发一个web后端的时候经常会用到缓存,缓存的种类很多例如 Redis,Memcahe等。而对缓存的操作基本就是固定的GET SET RPUSH等,因此可以创建一个Cache的父类其他的子类继承这个父类即可,这就是面向对象的继承(Inheritance)。
然而继承在使用的时候往往会遇到一些问题,继承使得类之间的层级关系变得复杂,这样超级父类(Superclass)会变得很脆弱,微小的改动可能会带来子类上的一些问题。为了解决这个问题就引入了组合(Composition )的概念,现在大部分人在编程的时候一般都秉承Composition Over Inheritance的基本原则。组合使用的方式
1.父类定义接口,子类实现这些接口
2.Method的复用采用调用的方式而不是继承
上面罗嗦了这么多接下来是golang中面向对象的代码实现
golang中是没有类的概念的,而是使用结构体。如下是定义了一个Cluster的结构体(注意代码摘自一些项目仅为解释概念使用并不能直接运行)。
type cluster struct {
remote pb.ClusterClient
callOpts []grpc.CallOption
}
通过结构体可以实例化一个对象
api := cluster{remote: RetryClusterClient(c)}
这样我们就可以访问对象的变量了
fmt.Println(api.remote)
Go 语言也提供了指针的访问方法,同样也是用点来访问变量,这点跟C语言的指针是不一样的如下函数
func NewCluster(c *Client) Cluster {
api := &cluster{remote: RetryClusterClient(c)}
if c != nil {
api.callOpts = c.callOpts
}
return api
}
结构体Method通过func来定义,结构体的方法与普通函数不一样在func与函数名之间需要有一个receiver。如下面的例子
func (c *cluster) memberAdd(ctx context.Context, peerAddrs []string, isLearner bool) (*MemberAddResponse, error) {
// fail-fast before panic in rafthttp
if _, err := types.NewURLs(peerAddrs); err != nil {
return nil, err
}
上面的例子中*cluster 是一个receiver
当然也可以使用非指针类型的receiver,指针类型的receiver提供了Pass-By-Reference 机制,而非指针则是Pass-By-Value,总体来说到底是使用指针类型还是非指针类型遵循以下原则
go语言中没有public 和private,但是go语言提供了定义公有变量和私有变量的方法:变量首字母法。如果一个变量的首字母是大写则为public 反之则为private。同样首字母法也适用与method
接口是go语言里面非常重要的一个用法,也是实现面向对象编程的关键。go语言中采用的是隐式接口实现,如果一个对象实现了一个接口的所有的method那么这个对象就实现了这个接口,通过接口go语言实现了多态。
如下我们定义一个接口Cluster 里面包含了六个method
type Cluster interface {
MemberList(ctx context.Context) (*MemberListResponse, error)
MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
MemberRemove(ctx context.Context, id uint64) (*MemberRemoveResponse, error)
MemberUpdate(ctx context.Context, id uint64, peerAddrs []string) (*MemberUpdateResponse, error)
MemberPromote(ctx context.Context, id uint64) (*MemberPromoteResponse, error)
}
我们的实际应用中会有HttpCluster和GrpcCluster两种实现,因此我们需要定义两个struct然后分别实现接口中的六个Method即可
GrpcCluster:
// 定义结构体
type GrpcCluster struct {
remote pb.ClusterClient
callOpts []grpc.CallOption
}
// 实现接口
func (c *GrpcCluster) MemberAdd(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
{
res,err := memberAdd()
return res,err
}
func (c *GrpcCluster) MemberList(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
{
res,err := memberlist()
return res,err
}
func (c *GrpcCluster) MemberAddAsLearner(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
{
res,err := addaslearner()
return res,err
}
func (c *GrpcCluster) MemberRemove(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
{
res,err := remove()
return res,err
}
func (c *GrpcCluster) MemberUpdate(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
{
res,err := update()
return res,err
}
func (c *GrpcCluster) MemberPromote(ctx context.Context, peerAddrs []string) (*MemberAddResponse, error)
{
res,err := promote()
return res,err
}
HttpCluster 同样定义结构体并实现方法即可,实现方法类似此处省略了伪代码。
定义好之后就可以在代码中使用了如下片段为如何使用
var clt Cluster
service_type := getserviceType()
switch service_type {
case "http":
clt = HttpCluster{}
case "grpc"
clt = GrpcCluster{}
}
exeute(clt)
Goglang中没有extends关键字,继承是通过结构体嵌套(
Struct embeding)实现的
如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现继承
如果一个struct嵌套了另一个【有名】的结构体,那么这个模式叫做组合如下两个结构体
type SetSlackServiceOptions struct {
WebHook *string `url:"webhook,omitempty" json:"webhook,omitempty" `
Username *string `url:"username,omitempty" json:"username,omitempty" `
Channel *string `url:"channel,omitempty" json:"channel,omitempty"`
}
type ExtendedStruct struct {
SetSlackServiceOptions
MoreValues []string
}
结构体ExtendedStruct中隐式声明了SetSlackServiceOptions因此ExtendedStruct就自动拥有了SetSlackServiceOptions所有的方法因此达到了继承的功能。
另外接口也可以组合使用
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type ReadWriter interface {
Reader
Writer
}
其实很简单, 就是把Reader
, Writer
嵌入到ReadWriter
中, 这样ReadWriter
就拥有了Reader
和Writer
的方法。
这篇作为开篇主要介绍了面向对象的编程方式以及go语言中面向对象的一些特殊方式,下一篇将开始介绍设计模式,敬请期待