IOC与面向接口编程在Go的简单实现

概述

在上一篇笔记中我们记录了依赖注入的概念,简单来说就是不通过new()的方式在类内部创建依赖对象,而是将依赖对象在外部创建好之后,通过构造函数、函数参数等方法传递给类使用。在项目中的对象会随着项目规模越来越庞大,对象之间越来越复杂,出现对象之间的多重依赖关系,对象之间耦合度过高的问题。说到对象耦合度过高,面向接口编程的设计原则主要用于实现松耦合的代码结构,我们这片笔记来记录一下控制反转和面向接口编程,用这两种思路修改之间的项目代码。
 IOC就是Inversion of Control 的缩写,就是控制反转。在项目没有使用IOC之前,对象D依赖对象C,对象C依赖对象B,对象B依赖对象A,这种称为依赖发现IOC的思想是借助”第三方”实现具有依赖关系对象之间的解耦。在引入IOC容器之前,对象B依赖于对象A,那么需要主动创建对象A提供,创建和使用对象A的方式都由程序来控制执行。在引入IOC容器后,对象A、B、C、D所需要的对象都由IOC容器提供,此时对象ABCD之间失去了直接联系,当对象B需要对象A时IOC容器会创建一个对象A注入到对象B需要的地方。因此IOC容器就好像”粘合剂”一样,同时我们可以看出之前对象B需要对象A从主动创建对象A,到IOC容器主动创建对象A注入到对象B所需要的地方,控制权反转来过来所以称为控制反转
面向接口编程是一种设计原则,旨在实现松耦合的代码结构。通过定义接口我们可以将不同模块之间的依赖关系降到最低,从而使代码具有灵活性和可维护性。例如说之前数据本来在Mysql中存得好好的,老板不知道从哪里听来说数据放在redis贼快,虽然改起来麻烦但为了保住饭碗你还是照做了,到了第二天老板跟你说还是存Mysql稳妥一点改回来吧。遇到这种情况面向接口编程思路是你早就知道需求总是要变动的。

修改项目

我们从用户注册业务的流程修改代码。首先我们将第三方依赖通过ioc来初始化,例如说Mysql redis的初始化;然后还有Gin以及其中间件的初始化(注册路由)。

IOC与面向接口编程在Go的简单实现

IOC与面向接口编程在Go的简单实现

type UserHandler struct {
    svc     service.UserService
}

xxx

// 注册路由
func (h *UserHandler) RegisterRoutes(server *gin.Engine) {
    ug := server.Group("/v2/users/")
    ug.POST("/signin", h.SignIn)
    ug.POST("/loginin", h.Login)
    ug.POST("/loginsms/code/send", h.SendSMS)
    ug.POST("/loginsms", h.VerifySMS)
}
然后就是正常的业务逻辑,从UserHandler的结构体中写上UserService,在UserService中定义好各种接口,如这里写的是Signup方法的实现,继续执行到repository层,再到dao层。
// Serivce层
type UserService interface {
    Signup(ctx context.Context, u domain.User) error
    Login(ctx context.Context, email string, password string) (domain.User, error)
    FindOrCreate(ctx context.Context, phone string, id string) (domain.User, error)
}

xxx

func (svc *userService) Signup(ctx context.Context, u domain.User) (err error) {
    id := snowflake.GenId()
    hashStr, err := bcrypt.GetPwd(u.Password)
    if err != nil {
        return err
    }
    u.Password = hashStr
    u.ID = strconv.Itoa(id)
    return svc.repo.Create(ctx, u)
}
//  repository层
type UserRepository interface {
    Create(ctx context.Context, u domain.User) error
    FindByEmail(ctx context.Context, email string) (domain.User, error)
    FindByPhone(ctx context.Context, phone string) (domain.User, error)
}

func (repo *CacheUserRepository) Create(ctx context.Context, u domain.User) (err error) {
    return repo.dao.Insert(ctx, u)
}
到这里大家可以注意一下了,dao层就开始操作数据库了。如下面这里是用Mysql实现的。那如果要改成其他数据库实现呢?
// dao层
type UserDAO interface {
    Insert(ctx context.Context, u domain.User) error
    FindByEmail(ctx context.Context, email string) (domain.User, error)
    FindByPhone(ctx context.Context, phone string) (domain.User, error)
}

func NewUserDAO(db *gorm.DB) UserDAO {
    return &GORMUserDAO{
        db: db,
    }
}

func (dao *GORMUserDAO) Insert(ctx context.Context, u domain.User) (err error) {
    if err = dao.db.WithContext(ctx).Create(&u).Error; err != nil {
        log.Println(err)
        return err
    }
    return nil
}
此时如果要将数据存到redis当中,只需要改一处的代码,修改dao层接口的实现就可以了,之前service层、repository层的代码都不需要改了。
type RedisUserDAO struct {
    rdb *redis.Cmdable
}

func NewRedisUserDAO(rdb *redis.Cmdable) *RedisUserDAO {
    return &RedisUserDAO{rdb: rdb}
}

func (rdb *RedisUserDAO) Insert(ctx context.Context, u domain.User) (err error) {
    xxx
}

func (rdb *RedisUserDAO) FindByEmail(ctx context.Context, u domain.User) (domain.User, error) {
    xxx
}

func (rdb *RedisUserDAO) FindByPhone(ctx context.Context, u domain.User) (domain.User, error) {
    xxx
}

写在最后

本人是新手小白,如果这篇笔记中有任何错误或不准确之处,真诚地希望各位读者能够给予批评和指正。谢谢!练习的代码放在这里–↓

https://github.com/FengZeHe/LearnGolang/tree/main/project/BasicProjectV2

原文始发于微信公众号(ProgrammerHe):IOC与面向接口编程在Go的简单实现

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

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/260521.html

(0)
李, 若俞的头像李, 若俞

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!