sort
gói:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Ý nghĩa của giao diện ẩn danh Interface
trong struct là reverse
gì?
sort
gói:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Ý nghĩa của giao diện ẩn danh Interface
trong struct là reverse
gì?
Câu trả lời:
Bằng cách này, đảo ngược thực hiện sort.Interface
và chúng ta có thể ghi đè một phương thức cụ thể mà không cần phải xác định tất cả các phương thức khác
type reverse struct {
// This embedded Interface permits Reverse to use the methods of
// another Interface implementation.
Interface
}
Lưu ý cách ở đây nó hoán đổi (j,i)
thay vì (i,j)
và đây cũng là phương thức duy nhất được khai báo cho cấu trúc reverse
ngay cả khi reverse
triển khaisort.Interface
// Less returns the opposite of the embedded implementation's Less method.
func (r reverse) Less(i, j int) bool {
return r.Interface.Less(j, i)
}
Bất kỳ cấu trúc nào được chuyển vào bên trong phương thức này, chúng tôi chuyển đổi nó thành một reverse
cấu trúc mới .
// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
return &reverse{data}
}
Giá trị thực sẽ đến nếu bạn nghĩ rằng bạn sẽ phải làm gì nếu cách tiếp cận này không thể thực hiện được.
Reverse
phương thức khác vào sort.Interface
?Bất kỳ thay đổi nào trong số này sẽ yêu cầu nhiều dòng mã hơn trên hàng nghìn gói muốn sử dụng chức năng đảo ngược tiêu chuẩn.
reverse
có một thành viên của loại Interface
. Thành viên này sau đó có thể gọi các phương thức của nó trên cấu trúc bên ngoài hoặc có thể ghi đè.
extend
để mở rộng các lớp con không trừu tượng? Đối với tôi, đây có thể là một cách tiện dụng để ghi đè chỉ một số phương pháp nhất định trong khi sử dụng các phương thức hiện có được thực hiện bởi nội bộ Interface
.
return r.Interface.Less(j, i)
đang gọi thực hiện cha mẹ?
Ok, câu trả lời được chấp nhận đã giúp tôi hiểu, nhưng tôi quyết định đăng một lời giải thích mà tôi nghĩ phù hợp hơn với cách suy nghĩ của tôi.
Các "hiệu quả Go" có ví dụ về các giao diện có nhúng giao diện khác:
// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface {
Reader
Writer
}
và một cấu trúc có nhúng các cấu trúc khác:
// ReadWriter stores pointers to a Reader and a Writer.
// It implements io.ReadWriter.
type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
Nhưng không có đề cập đến một cấu trúc có nhúng một giao diện. Tôi đã bối rối khi thấy điều này trong sort
gói:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
...
type reverse struct {
Interface
}
Nhưng ý tưởng rất đơn giản. Nó gần giống như:
type reverse struct {
IntSlice // IntSlice struct attaches the methods of Interface to []int, sorting in increasing order
}
phương pháp IntSlice
được thăng chức reverse
.
Và điều này:
type reverse struct {
Interface
}
có nghĩa là sort.reverse
có thể nhúng bất kỳ cấu trúc nào triển khai giao diện sort.Interface
và bất kỳ phương thức nào mà giao diện đó có, chúng sẽ được thăng cấp reverse
.
sort.Interface
có phương thức Less(i, j int) bool
mà bây giờ có thể được ghi đè:
// Less returns the opposite of the embedded implementation's Less method.
func (r reverse) Less(i, j int) bool {
return r.Interface.Less(j, i)
}
Sự bối rối của tôi trong sự hiểu biết
type reverse struct {
Interface
}
là tôi đã nghĩ rằng một cấu trúc luôn có cấu trúc cố định, tức là một số trường cố định thuộc loại cố định.
Nhưng những điều sau đây chứng minh tôi đã sai:
package main
import "fmt"
// some interface
type Stringer interface {
String() string
}
// a struct that implements Stringer interface
type Struct1 struct {
field1 string
}
func (s Struct1) String() string {
return s.field1
}
// another struct that implements Stringer interface, but has a different set of fields
type Struct2 struct {
field1 []string
dummy bool
}
func (s Struct2) String() string {
return fmt.Sprintf("%v, %v", s.field1, s.dummy)
}
// container that can embedd any struct which implements Stringer interface
type StringerContainer struct {
Stringer
}
func main() {
// the following prints: This is Struct1
fmt.Println(StringerContainer{Struct1{"This is Struct1"}})
// the following prints: [This is Struct1], true
fmt.Println(StringerContainer{Struct2{[]string{"This", "is", "Struct1"}, true}})
// the following does not compile:
// cannot use "This is a type that does not implement Stringer" (type string)
// as type Stringer in field value:
// string does not implement Stringer (missing String method)
fmt.Println(StringerContainer{"This is a type that does not implement Stringer"})
}
Tuyên bố
type reverse struct {
Interface
}
cho phép bạn khởi tạo reverse
với mọi thứ triển khai giao diện Interface
. Thí dụ:
&reverse{sort.Intslice([]int{1,2,3})}
Bằng cách này, tất cả các phương pháp được thực hiện bởi Interface
giá trị nhúng sẽ được phổ biến ra bên ngoài trong khi bạn vẫn có thể ghi đè một số phương pháp trong đó reverse
, chẳng hạn như Less
để đảo ngược việc sắp xếp.
Đây là những gì thực sự xảy ra khi bạn sử dụng sort.Reverse
. Bạn có thể đọc về cách nhúng trong phần cấu trúc của thông số kỹ thuật .
Tôi cũng sẽ đưa ra lời giải thích của tôi. Các sort
gói định nghĩa một kiểu unexported reverse
, mà là một cấu trúc, mà nhúng Interface
.
type reverse struct {
// This embedded Interface permits Reverse to use the methods of
// another Interface implementation.
Interface
}
Điều này cho phép Reverse sử dụng các phương pháp triển khai Giao diện khác. Đây là cái gọi là composition
, là một tính năng mạnh mẽ của cờ vây.
Các Less
phương pháp cho reverse
các cuộc gọi các Less
phương pháp nhúng Interface
giá trị, nhưng với các chỉ số lộn, đảo ngược thứ tự của các kết quả phân loại.
// Less returns the opposite of the embedded implementation's Less method.
func (r reverse) Less(i, j int) bool {
return r.Interface.Less(j, i)
}
Len
và Swap
hai phương thức khác của reverse
, được cung cấp ngầm bởi Interface
giá trị ban đầu vì nó là một trường nhúng. Hàm đã xuất Reverse
trả về một thể hiện của reverse
kiểu có chứa Interface
giá trị ban đầu .
// Reverse returns the reverse order for data.
func Reverse(data Interface) Interface {
return &reverse{data}
}
Less
Phương thức cho reverse
gọi Less
phương thức của Interface
giá trị nhúng , nhưng với các chỉ số bị lật, đảo ngược thứ tự của kết quả sắp xếp." - điều này trông giống như việc gọi triển khai cha mẹ.
Tôi thấy tính năng này rất hữu ích khi viết mocks trong các bài kiểm tra .
Đây là một ví dụ:
package main_test
import (
"fmt"
"testing"
)
// Item represents the entity retrieved from the store
// It's not relevant in this example
type Item struct {
First, Last string
}
// Store abstracts the DB store
type Store interface {
Create(string, string) (*Item, error)
GetByID(string) (*Item, error)
Update(*Item) error
HealthCheck() error
Close() error
}
// this is a mock implementing Store interface
type storeMock struct {
Store
// healthy is false by default
healthy bool
}
// HealthCheck is mocked function
func (s *storeMock) HealthCheck() error {
if !s.healthy {
return fmt.Errorf("mock error")
}
return nil
}
// IsHealthy is the tested function
func IsHealthy(s Store) bool {
return s.HealthCheck() == nil
}
func TestIsHealthy(t *testing.T) {
mock := &storeMock{}
if IsHealthy(mock) {
t.Errorf("IsHealthy should return false")
}
mock = &storeMock{healthy: true}
if !IsHealthy(mock) {
t.Errorf("IsHealthy should return true")
}
}
Bằng cách sử dụng:
type storeMock struct {
Store
...
}
Người ta không cần phải chế nhạo tất cả các Store
phương pháp. Chỉ HealthCheck
có thể bị chế nhạo, vì chỉ có phương pháp này được sử dụng trong TestIsHealthy
thử nghiệm.
Dưới kết quả của test
lệnh:
$ go test -run '^TestIsHealthy$' ./main_test.go
ok command-line-arguments 0.003s
Một ví dụ thực tế về trường hợp sử dụng này mà người ta có thể tìm thấy khi thử nghiệm AWS SDK .
Để làm cho nó rõ ràng hơn, đây là phương án thay thế xấu xí - điều tối thiểu cần phải triển khai để đáp ứng Store
giao diện:
type storeMock struct {
healthy bool
}
func (s *storeMock) Create(a, b string) (i *Item, err error) {
return
}
func (s *storeMock) GetByID(a string) (i *Item, err error) {
return
}
func (s *storeMock) Update(i *Item) (err error) {
return
}
// HealthCheck is mocked function
func (s *storeMock) HealthCheck() error {
if !s.healthy {
return fmt.Errorf("mock error")
}
return nil
}
func (s *storeMock) Close() (err error) {
return
}