Câu hỏi Các kiểu hàm so với các giao diện phương thức đơn


Nếu một giao diện chỉ có một phương thức duy nhất, tôi có nên sử dụng một loại hàm không?

Dưới đây là một ví dụ về cả hai cách tiếp cận:

type Delegate interface {                 | type Delegate func(x int) int
  Do(x int) int                           | 
}                                         | 
                                          | 
type App struct {                         | type App struct {
  delegate Delegate                       |   delegate Delegate
}                                         | }
                                          | 
func (this *App) foo() {                  | func (this *App) foo() {
  ...                                     |   ...
  y := this.delegate.Do(x)                |   y := this.delegate(x)
  ...                                     |   ...
}                                         | }
                                          | 
func main() {                             | func main() {
  delegate := &DelegateImpl{}             |   delegate := &DelegateImpl{}
  app := &App{delegate}                   |   app := &App{delegate.Process}
  ...                                     |   ...
}                                         | }

Kiểm tra:

type FakeDelegate {                       | 
  t *testing.T                            | 
  expectedX int                           | 
  result int                              | 
}                                         | 
                                          | 
func (this *FakeDelegate) Do(x int) int { | 
  require.Equal(t, this.expectedX, x)     | 
  return this.result                      | 
}                                         | 
                                          | 
func TestAppFoo(t *testing.T) {           | func TestAppFoo(t *testing.T) {
  app := &App{}                           |   app := &App{}
  app.delegate = &FakeDelegate{t, 1, 2}   |   app.delegate = func(x int) int {
  app.foo()                               |     require.Equal(t, 1, x)
}                                         |     return 2
                                          |   }
                                          |   app.foo()
                                          | }

Có vẻ như mã bên phải (với kiểu hàm) có ít dòng hơn, đặc biệt là khi kiểm tra. Nhưng không có bất kỳ cạm bẫy nào?


11
2017-08-12 09:42


gốc




Các câu trả lời:


Trong trường hợp chung

Đây là một câu hỏi về thiết kế. Giao diện cung cấp cái gì đó có chức năng không: công văn động. Vì vậy, nếu sau này bạn muốn (có thể là của riêng bạn) mã khách hàng để áp dụng cho biết một đối tượng, và hình dung rằng đối tượng này có thể có thể là một trong nhiều loại khác nhau tại một điểm nhất định trong chương trình, đi cho một giao diện.

Chuyên nghiệp: bạn có thể linh hoạt.

Các khuyết điểm:

  • công văn động chi phí một chi phí nhỏ trong thời gian thực hiện. Bạn sẽ không muốn điều đó ở ngay giữa vòng lặp quan trọng chẳng hạn.
  • các giao diện, nếu có, sẽ được sử dụng khi hệ thống phát triển, có thể theo những cách hơi bất ngờ. Điều đó có nghĩa là trong khi hầu hết các lần bạn có thể xác định phạm vi trách nhiệm của một chức năng đủ nhẹ nhàng, thì trách nhiệm và chữ ký của giao diện cần được suy nghĩ cẩn thận, như một quyết định thiết kế.
  • có nhiều tri thức trí tuệ hơn để trải qua cho người đọc, những người cố gắng hiểu mã.

Go được thiết kế như một ngôn ngữ đơn giản và thực dụng. Tôi cho rằng, với tư cách là người dùng sẵn sàng, chúng ta nên tiến hành triết lý của nó. Do đó, tôi muốn nói: nếu bạn không cần một cái gì đó, không sử dụng nó. :)

Trong trường hợp cụ thể của bạn

Mặc dù một con trỏ hàm thực sự là một dạng công văn được quản lý bởi nhà phát triển, có vẻ như với tôi rằng các lý do trên vẫn có thể áp dụng được: hãy tìm giải pháp đơn giản nhất có thể đáp ứng các nhu cầu dự đoán được. Nếu không, Go sẽ trở thành Java. Nếu bạn chắc chắn rằng các đại biểu sẽ không bao giờ cần phải cung cấp bất kỳ điểm vào khác (như cung cấp thông tin meta ví dụ), tôi muốn nói dính vào con trỏ hàm.


6
2017-08-12 09:49





Tôi nghĩ việc sử dụng quy tắc đơn giản sau đây là hợp lý:

  • nếu triển khai có khả năng nhất hoạt động trên dữ liệu không được truyền trong các đối số thì hãy sử dụng một giao diện. Ví dụ từ thư viện chuẩn: io.Reader giao diện với một phương thức duy nhất Read.
  • nếu không thì hãy sử dụng một hàm. Ví dụ từ thư viện chuẩn: http.HandlerFunc, bufio.SplitFunc

Ngoài ra tôi thấy rằng tên tốt giúp với các quyết định thiết kế.


2
2017-08-12 10:51



Phương thức có thể được chuyển như một con trỏ hàm, do đó điểm "hoạt động trên dữ liệu" không thực sự hợp lệ. Trên thực tế nó không phải là "chức năng vs phương pháp", đó là "loại chức năng vs giao diện". - Abyx
Có thể io.Reader giao diện là một chức năng thay vì giao diện phương pháp duy nhất? Về mặt kỹ thuật nó có thể nhưng sẽ ít thuận tiện hơn để làm việc với nó. Có thể bufio.SplitFunc là một giao diện phương pháp duy nhất? Có, nhưng một lần nữa mã để làm việc với nó sẽ trở nên chi tiết hơn. Không có câu trả lời duy nhất cho câu hỏi của bạn. - kostya