在线商城,需要在Go后台提供存储与查询产品的服务,既实现一个负责保存和检索产品的存储库。
productrepo/
└── api.go
0 directories, 1 file
创建一个productrepo
包和一个api.go
文件,该API应该暴露出存储库里所有的产品方法。
// api.go
package productrepo
type ProductRepository interface {
StoreProduct(name string, id int)
FindProductByID(id int)
}
在productrepo
包下,定义了ProductRepository
接口,它代表的就是存储库。该接口中定义两个方法:
StoreProduct()
方法用于存储产品信息FindProductByID()
方法通过产品ID查找产品信息存储库接口定义完成后,就需要有实体对象去实现该接口。
productrepo/
├── api.go
└── mock.go
0 directories, 2 files
在productrepo
包下,新建mock.go
文件,定义mockProductRepo
对象。
// mock.go
package productrepo
import "fmt"
// 实现接口的空实体对象
type mockProductRepo struct {}
func (m mockProductRepo) StoreProduct(name string, id int) {
fmt.Println("mocking the StoreProduct func")
}
func (m mockProductRepo) FindProductByID(id int) {
fmt.Println("mocking the FindProductByID func")
}
在示例代码中mock出ProductRepository
接口所需的方法。
然后在api.go
中增加New()
方法,它返回的一个实现了ProductRepository
接口的对象。
// api.go
func New() ProductRepository {
return mockProductRepo{}
}
对于已经定义的ProductRepository
接口,可以有多种对象去实现它。
不同的数据库存储都需要实现ProductRepository
接口定义的StoreProduct()
方法和FindProductByID()
方法。
以本地MySQL存储库为例,它要管理产品对象,需要实现ProductRepository
接口。
productrepo/
├── api.go
├── mock.go
└── mysql.go
0 directories, 3 files
在productrepo
包下,新建mysql.go
文件,定义了mysqlProductRepo
对象并实现接口方法。
// mysql.go
package productrepo
import "fmt"
type mysqlProductRepo struct {
}
func (m mysqlProductRepo) StoreProduct(name string, id int) {
fmt.Println("mysqlProductRepo: mocking the StoreProduct func")
// In a real world project you would query a MySQL database here.
}
func (m mysqlProductRepo) FindProductByID(id int) {
fmt.Println("mysqlProductRepo: mocking the FindProductByID func")
// In a real world project you would query a MySQL database here.
}
相似地,当项目中同时需要把产品信息存储到云端时,以阿里云为例。
productrepo/
├── aliyun.go
├── api.go
├── mock.go
└── mysql.go
0 directories, 4 files
在productrepo
包下,新建aliyun.go
文件,定义了aliCloudProductRepo
对象并实现接口方法。
// aliyun.go
package productrepo
import "fmt"
type aliCloudProductRepo struct {
}
func (m aliCloudProductRepo) StoreProduct(name string, id int) {
fmt.Println("aliCloudProductRepo: mocking the StoreProduct func")
// In a real world project you would query an ali Cloud database here.
}
func (m aliCloudProductRepo) FindProductByID(id int) {
fmt.Println("aliCloudProductRepo: mocking the FindProductByID func")
// In a real world project you would query an ali Cloud database here.
}
此时,更新api.go
中定义的New()
方法。
// api.go
func New(environment string) ProductRepository {
switch environment {
case "aliCloud":
return aliCloudProductRepo{}
case "local-mysql":
return mysqlProductRepo{}
}
return mockProductRepo{}
}
通过将环境变量environment
传递给New()
函数,它将基于该环境值返回ProductRepository
接口的正确实现对象。
.
├── go.mod
├── main.go
└── productrepo
├── aliyun.go
├── api.go
├── mock.go
└── mysql.go
1 directory, 6 files
定义程序入口main.go
文件以及main函数。
// main.go
package main
import "productrepo"
func main() {
env := "aliCloud"
repo := productrepo.New(env)
repo.StoreProduct("HuaWei mate 40", 105)
}
通过使用productrepo.New()
方法基于环境值来获取ProductRepository
接口对象。
如果需要切换产品存储库,则只需要使用对应的env
值调用productrepo.New()
方法即可。
// mysql.go
func NewMysqlProductRepo() *mysqlProductRepo {
return &mysqlProductRepo{}
}
//aliyun.go
func NewAliCloudProductRepo() *aliCloudProductRepo{
return &aliCloudProductRepo{}
}
// mock.go
func NewMockProductRepo() *mockProductRepo {
return &mockProductRepo{}
}
// main.go
package main
import "productrepo"
func main() {
env := "aliCloud"
switch env {
case "aliCloud":
repo := productrepo.NewAliCloudProductRepo()
repo.StoreProduct("HuaWei mate 40", 105)
// the more function to do, the more code is repeated.
case "local-mysql":
repo := productrepo.NewMysqlProductRepo()
repo.StoreProduct("HuaWei mate 40", 105)
// the more function to do, the more code is repeated.
default:
repo := productrepo.NewMockProductRepo()
repo.StoreProduct("HuaWei mate 40", 105)
// the more function to do, the more code is repeated.
}
}
在项目演进过程中,会迭代很多存储库对象,而通过ProductRepository
接口,可以轻松地实现扩展,而不必反复编写相同逻辑的代码。
开发中,常常提到要功能模块化,上面示例:通过接口为载体,一类服务就是一个接口,实现接口即服务。