一个简单的业务系统归结为生产者和消费者之间的交互,生产者提供服务,消费者使用服务;
如下代码结构:业务逻辑层和数据库层
- ├── db
- │ └── db.go
- └── user
- └── user.go
- //user.go
- package user
-
- // 第一对外所依赖的接口
- type UserStore interface {
- Insert(item interface{}) error
- Get(id int) error
- }
- type UserService struct {
- store UserStore
- }
- // Accepting interface here!
- func NewUserService(s UserStore) *UserService {
- return &UserService{
- store: s,
- }
- }
- func (u *UserService) CreateUser() { ... }
- func (u *UserService) RetrieveUser(id int) User { ... }
-
-
- //db.go
- package db
- // 生产者实现消费者所需的接口依赖,注意该生产者可以供给多个消费者使用,实现所有消费者所需要的方法;
- type Store struct {
- db *sql.DB
- }
- func NewDB() *Store { ... } //func to initialise DB
- func (s *Store) Insert(item interface{}) error { ... } //insert item
- func (s *Store) Get(id int) error { ... } //get item by id
消费者 user.go 需要存储依赖才能执行与用户相关的业务逻辑。它不关心存储是什么,只是它有 2 个方法,Insert() 和 Get(),因此它能够创建和检索用户。因此,它定义了自己的接口 UserStore 并将其作为依赖项接受。 db.go 中的 Store 结构实现了这个接口,因此可以用作依赖项。 就这么简单!接受接口就是让消费者定义他们想要的接口。消费者不应该担心依赖是什么,只要它可以执行消费者需要的任务。由于 Go 继承的隐含性质,这也是可能的。这样做会带来一些好处:
生产者应该向消费者提供具体的类型,而不是接口。如果返回了接口还需要cast the interface to struct.
- //db.go
- package db
- type Store struct {
- db *sql.DB
- }
- // 返回一个实体类型
- func NewDB() *Store { ... }
- func (s *Store) Insert(item interface{}) error { ... }
- func (s *Store) Get(id int) error { ... }
- // 后面新增方法,消费者不需要改动 这也是返回实体的便利之处
- //postgres.go 生产者
- package db
-
- type Store interface {
- Insert(item interface{}) error
- Get(id int) error
- }
-
- type MyStore struct {
- db *sql.DB
- }
-
- func InitDB() Store { ... } //func to initialise DB
- func (s *MyStore ) Insert(item interface{}) error { ... } //insert item
- func (s *MyStore ) Get(id int) error { ... } //get item by id
-
-
-
- //user.go 消费者
- package user
-
- type UserService struct {
- store db.Store
- }
-
- func NewUserService(s db.Store) *UserService {
- return &UserService{
- store: s,
- }
- }
- func (u *UserService) CreateUser() { ... }
- func (u *UserService) RetrieveUser(id int) User { ... }
接口现在由生产者定义,使用者使用该接口作为入口点。这就是所谓的先发制人接口。生产者在实际使用接口之前先行定义接口。先发制人的接口也使得测试更加困难。