英文:
When and why to return an interface in Golang?
问题
示例代码:
type IClient interface {
UploadFile(sourcePath, host string) error
CopyFile(sourcePath, destPath string) error
DeleteFile(sourcePath, option string) error
GetChecksum(sourcePath string) (*string, error)
GetSize(sourcePath string) (*float32, error)
}
type FileWorker struct {
Extension string
Host string
}
func NewFileWorker(fileExtension, host string) IClient {
var fileWorker = &FileWorker{
Extension: fileExtension,
Host: host,
}
return fileWorker
}
//注意:FileWorker类型是实现IClient接口的所有方法的接收者
我看到过其他类似上述代码中返回接口的“New”(或“Get”)函数。但实际上它并不返回接口,而是返回实现该接口的结构体。而这个结构体类型是实现该接口的各种方法的接收者。
问题:我们何时以及为什么返回接口?目前我看到这是一种绕弯的方式,导致在尝试理解某些代码时需要追溯多个文件。
英文:
Example code:
type IClient interface {
UploadFile(sourcePath, host string) error
CopyFile(sourcePath, destPath string) error
DeleteFile(sourcePath, option string) error
GetChecksum(sourcePath string) (*string, error)
GetSize(sourcePath string) (*float32, error)
}
type FileWorker struct {
Extension string
Host string
}
func NewFileWorker(fileExtension, host string) IClient {
var fileWorker = &FileWorker {
Extension: extension,
Host: host,
}
return fileWorker
}
//NOTE: type FileWorker is the receiver of all methods implementing the IClient interface
I have seen other "New" (or "Get") function returning interface like above. But it does not actually return the interface, but instead a struct that implements said interface. In turn, that struct type is the receiver of various methods implementing said interface.
Question: When and why do we return interfaces? Currently I see this is a roundabout way of doing things, resulting in one having to trace back through multiple files while trying to understand some codes.
答案1
得分: 1
接口是实现代码重用的一种方式。通常情况下,重要的不是对象是什么,而是它能做什么。考虑以下方法:
func Grade(questions []MultipleChoiceQuestion) {
total := len(questions)
correct := 0
for _, q := range questions {
if q.Test() {
correct++
}
}
return correct / total
}
函数Grade
接收一个问题集合并对用户的答案进行评分。
如果我们想要添加判断题怎么办?目前Grade
只接受MultipleChoiceQuestion
,这意味着我们需要创建一个全新的函数,比如GradeTrueOrFalse(questions []TrueOrFalseQuestions)
,才能进行包含判断题的测试。此外,我们将无法同时包含多项选择题和判断题。
然而,我们应该能够在单个测试中组合不同类型的问题。原始函数并不关心问题的类型,只关心问题能否进行Test()
操作。这就是接口的用武之地。
通过将Test
方法抽象为一个接口,我们可以在Grade
中使用任何类型的问题。
type Tester interface {
Test() bool
}
type MultipleChoiceQuestion struct
func (q MultipleChoiceQuestion) Test() bool {
// 实现...
}
type TrueOrFalseQuestion struct
func (q TrueOrFalseQuestion) Test() bool {
// 实现...
}
func Grade(questions []Tester) {
total := len(questions)
correct := 0
for _, q := range questions {
if q.Test() {
correct++
}
}
return correct / total
}
现在,Grade
可以接收任何类型的问题。此外,questions []Tester
可以包含多项选择题和判断题的混合。
当正确使用时,你很少需要执行类型断言或者"追踪多个文件"来理解发生了什么。
英文:
Interfaces are a way to achieve code reuse. Often times, what matters is not what an object is, but what it does. Consider the following method:
func Grade(questions []MultipleChoiceQuestion) {
total := len(questions)
correct := 0
for _, q := range questions {
if q.Test() {
correct++
}
}
return correct / total
}
The function Grade
takes in a collection of questions and grades the user's answers.
What if we wanted to add true or false questions? Right now Grade
only accepts MultipleChoiceQuestions, which means that we would have to make a whole new function, say GradeTrueOrFalse(questions []TrueOrFalseQuestions)
in order to have a test with true or false questions. Furthermore, we would be unable to have a test composed of both multiple choice and true or false questions.
However, we should be able to copmbine questions in a single test. The original function doesn't care about what kind of question it is, only that the question can Test() the user's answer. This is where interfaces come in.
By abstracting away the Test
method into an interface we can use Grade
for any kind of question.
type Tester interface {
Test() bool
}
type MultipleChoiceQuestion struct
func (q MultipleChoiceQuestion) Test() bool {
// implementation...
}
type TrueOrFalseQuestion struct
func (q TrueOrFalseQuestion) Test() bool {
// implementation...
}
func Grade(questions []Tester) {
total := len(questions)
correct := 0
for _, q := range questions {
if q.Test() {
correct++
}
}
return correct / total
}
Now Grade can take in any kind of question. Furthermore, questions []Tester
can be a mix of multiple choice and true or false questions.
When used properly, you should rarely have to perform a type assertion or "trace back through multiple files" to understand what is going on.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论