Có đủ để các phương thức được phân biệt chỉ bằng tên đối số (không phải kiểu) không?


36

Là đủ để các phương thức được phân biệt chỉ bằng tên đối số (không phải loại) hay tốt hơn là đặt tên rõ ràng hơn?

Ví dụ T Find<T>(int id)so với T FindById<T>(int id).

Có bất kỳ lý do chính đáng để đặt tên rõ ràng hơn (tức là thêm ById) so với chỉ giữ tên đối số?

Một lý do tôi có thể nghĩ đến là khi chữ ký của các phương thức giống nhau nhưng chúng có ý nghĩa khác nhau.

FindByFirstName(string name)FindByLastName(string name)


4
Vì vậy, khi bạn quá tải Tìm để bao gồm T Find<T>(string name)hoặc (int size)làm thế nào để bạn có kế hoạch để giải quyết các vấn đề không thể tránh khỏi?
UKMonkey

3
@UKMonkey có vấn đề gì không thể tránh khỏi?
Konrad

3
trong trường hợp đầu tiên: nếu nhiều mục có cùng tên thì bạn sẽ phải thay đổi chữ ký hàm; điều đó có nghĩa là mọi người có thể sẽ bị nhầm lẫn với những gì nó có nghĩa là trở lại; Trong trường hợp sau, đối số là như nhau - và do đó, quá tải bất hợp pháp. Bạn có thể bắt đầu đặt tên hàm bằng "byX" hoặc tạo một đối tượng cho đối số để bạn có thể có quá tải tương đương với cùng một đối số. Hoặc là hoạt động tốt cho các tình huống khác nhau.
UKMonkey

2
@UKMonkey bạn có thể đăng câu trả lời với một số ví dụ mã nếu bạn muốn
Konrad

3
Id có lẽ nên là một IDđối tượng mờ đục và không chỉ là một int. Theo cách đó, hãy kiểm tra thời gian biên dịch rằng bạn không sử dụng id cho int hoặc viceversa trong một phần mã của bạn. Và với điều đó bạn có thể có find(int value)find(ID id).
Bakuriu

Câu trả lời:


68

Chắc chắn có một lý do tốt để đặt tên rõ ràng hơn.

Đây không phải là định nghĩa phương thức nên tự giải thích mà là sử dụng phương pháp . Và trong khi findById(string id)find(string id)cả hai đều tự giải thích, có một sự khác biệt rất lớn giữa findById("BOB")find("BOB"). Trong trường hợp trước, bạn biết rằng chữ ngẫu nhiên trên thực tế là một Id. Trong trường hợp sau, bạn không chắc chắn - nó thực sự có thể là một tên cụ thể hoặc một cái gì đó hoàn toàn khác.


9
Ngoại trừ trong 99% các trường hợp khác, nơi bạn có một biến hoặc tài sản tên là một điểm quy chiếu: findById(id)vs find(id). Bạn có thể đi một trong hai cách này.
Greg Burghardt

16
@GregBurghardt: Một giá trị cụ thể không nhất thiết được đặt tên theo cùng một cách cho một phương thức và trình gọi của nó. Ví dụ, xem xét double Divide(int numerator, int denominator)việc được sử dụng trong một phương thức : double murdersPerCapita = Divide(murderCount, citizenCount). Bạn không thể dựa vào hai phương pháp sử dụng cùng một tên biến vì có rất nhiều trường hợp không phải là trường hợp đó (hoặc khi đó là trường hợp, đó là cách đặt tên xấu)
Flater 26/11/18

1
@Flater: Đưa ra mã trong câu hỏi (tìm nội dung từ một loại lưu trữ liên tục) Tôi tưởng tượng bạn có thể gọi phương thức này bằng "killeredCitizenId" hoặc "civenId" ... Tôi thực sự không nghĩ rằng đối số hoặc tên biến là mơ hồ đây. Và thành thật mà nói tôi có thể đi một trong hai cách này. Đó thực sự là một câu hỏi rất quan tâm.
Greg Burghardt

4
@GregBurghardt: Bạn không thể chắt lọc một quy tắc toàn cầu từ một ví dụ duy nhất. Câu hỏi của OP nói chung , không cụ thể với ví dụ đã cho. Vâng, có một số trường hợp sử dụng cùng một tên có ý nghĩa, nhưng cũng có những trường hợp không. Do đó, câu trả lời này, tốt nhất là nhắm đến sự thống nhất ngay cả khi không cần thiết trong một tập hợp con của các trường hợp sử dụng.
Flater

1
Các tham số tên giải quyết sự nhầm lẫn sau khi sự nhầm lẫn đã tồn tại , vì bạn cần xem xét định nghĩa phương thức, trong khi các phương thức được đặt tên rõ ràng hoàn toàn tránh sự nhầm lẫn bằng cách có tên trong lệnh gọi phương thức. Ngoài ra, bạn không thể có hai phương thức có cùng tên và loại đối số nhưng tên đối số khác nhau, có nghĩa là bạn sẽ cần tên rõ ràng trong một số trường hợp nhất định.
Darkhogg

36

Ưu điểm của FindById () .

  1. Tương lai và cách nhiệt : Nếu bạn bắt đầu với Find(int), và sau đó có thêm các phương pháp khác ( FindByName(string), FindByLegacyId(int), FindByCustomerId(int), FindByOrderId(int), vv), những người như tôi có xu hướng dành nhiều lứa tuổi tìm kiếm FindById(int). Không thực sự là một vấn đề nếu bạn có thể và sẽ thay đổi Find(int)thành FindById(int)một khi nó trở nên cần thiết - bằng chứng trong tương lai là về những điều này nếu s.

  2. Dễ đọc hơn . Findlà hoàn toàn tốt đẹp nếu nhìn cuộc gọi như record = Find(customerId);Tuy FindByIdlà hơi dễ dàng hơn cho việc đọc nếu nó record = FindById(AFunction());.

  3. Kiên định . Bạn luôn có thể áp dụng FindByX(int)/ FindByY(int)mẫu ở mọi nơi, nhưng Find(int X)/ Find(int Y)không thể vì chúng xung đột.

Ưu điểm của Tìm ()

  • HÔN. Findrất đơn giản và dễ hiểu, và bên cạnh operator[]đó là một trong 2 tên hàm được mong đợi nhất trong ngữ cảnh này. (Một số lựa chọn thay thế phổ biến là get, lookuphoặc fetch, tùy thuộc vào ngữ cảnh).
  • Theo nguyên tắc thông thường, nếu bạn có một tên hàm là một từ nổi tiếng duy nhất mô tả chính xác những gì hàm làm, hãy sử dụng nó. Ngay cả khi có một tên nhiều từ dài hơn thì tốt hơn một chút để mô tả chức năng này làm gì. Ví dụ: Độ dài so với NumberOfElements . Có một sự đánh đổi, và nơi để vẽ đường là chủ đề của một cuộc tranh luận đang diễn ra.
  • Nói chung là tốt để tránh dư thừa. Nếu chúng ta nhìn vào FindById(int id), chúng ta có thể dễ dàng loại bỏ sự dư thừa bằng cách thay đổi nó Find(int id), nhưng có một sự đánh đổi - chúng ta mất đi một số sự rõ ràng.

Ngoài ra, bạn có thể nhận được những lợi thế của cả hai bằng cách sử dụng Id được gõ mạnh:

CustomerRecord Find(Id<Customer> id) 
// Or, depending on local coding standards
CustomerRecord Find(CustomerId id) 

Triển khai Id<>: Nhập mạnh các giá trị ID trong C #

Nhận xét ở đây, cũng như trong liên kết ở trên, đưa ra nhiều mối quan tâm về Id<Customer>việc tôi muốn giải quyết:

  • Mối quan tâm 1: Đó là một sự lạm dụng thuốc generic. CustomerIdOrderIDlà các loại khác nhau ( customerId1 = customerId2;=> tốt, customerId1 = orderId1;=> xấu), nhưng cách triển khai của chúng gần giống nhau, vì vậy chúng tôi có thể thực hiện chúng bằng cách sao chép dán hoặc với siêu lập trình. Mặc dù có giá trị trong một cuộc thảo luận về việc phơi bày hoặc che giấu cái chung, nhưng siêu lập trình là những gì khái quát dành cho.
  • Mối quan tâm 2: Nó không dừng lại những lỗi đơn giản. / Đây là một giải pháp tìm kiếm vấn đề Vấn đề chính được loại bỏ bằng cách sử dụng Id được gõ mạnh là thứ tự đối số sai trong lệnh gọi DoSomething(int customerId, int orderId, int productId). Id được gõ mạnh cũng ngăn chặn các vấn đề khác, bao gồm cả OP đã hỏi về.
  • Mối quan tâm 3: Nó thực sự chỉ che khuất mã. Thật khó để biết nếu một id được tổ chức int aVariable. Thật dễ dàng để nói rằng Id được giữ Id<Customer> aVariablevà thậm chí chúng ta có thể nói rằng đó là Id khách hàng.
  • Mối quan tâm 4: Những Id này không có loại mạnh, chỉ là các hàm bao. Stringchỉ là một bao bọc xung quanh byte[]. Gói, hoặc đóng gói, không xung đột với gõ mạnh.
  • Mối quan tâm 5: Đó là quá kỹ thuật. Đây là phiên bản tối thiểu, mặc dù tôi cũng khuyên bạn nên thêm operator==operator!=nếu bạn không muốn chỉ dựa vào Equals:

.

public struct Id<T>: {
    private readonly int _value ;
    public Id(int value) { _value = value; }
    public static explicit operator int(Id<T> id) { return id._value; }
}

10

Một cách nghĩ khác về điều này là sử dụng loại an toàn của ngôn ngữ.

Bạn có thể thực hiện một phương pháp như:

Find(FirstName name);

Trong đó FirstName là một đối tượng đơn giản bao bọc một chuỗi chứa tên đầu tiên và có nghĩa là không thể nhầm lẫn về phương thức đang làm gì, cũng như trong các đối số mà nó được gọi.


4
Không chắc câu trả lời của bạn là gì cho câu hỏi OP. Bạn có khuyên bạn nên sử dụng một tên như "Tìm" bằng cách dựa vào loại đối số không? Hoặc bạn có khuyên bạn chỉ nên sử dụng các tên đó khi có một loại rõ ràng cho (các) đối số và sử dụng một tên rõ ràng hơn như "FindById" ở nơi khác không? Hoặc bạn có đề xuất giới thiệu các loại rõ ràng để làm cho một tên như "Tìm" khả thi hơn không?
Doc Brown

2
@DocBrown Tôi nghĩ cái sau, và tôi thích nó. Nó thực sự giống với câu trả lời của Peter, iiuc. Cơ sở lý luận theo tôi hiểu là hai lần: (1) Rõ ràng từ kiểu đối số mà hàm thực hiện; (2) Bạn không thể phạm sai lầm như. Điều gì string name = "Smith"; findById(name);là có thể nếu bạn sử dụng các loại chung không mô tả.
Peter - Phục hồi Monica

5
Mặc dù nói chung tôi là một fan hâm mộ của an toàn loại thời gian biên dịch, hãy cẩn thận với các loại trình bao bọc. Việc giới thiệu các lớp trình bao bọc vì mục đích an toàn loại đôi khi có thể làm phức tạp nghiêm trọng API của bạn nếu thực hiện vượt quá. ví dụ: toàn bộ vấn đề "nghệ sĩ trước đây gọi là int" mà winapi có; Về lâu dài tôi sẽ nói rằng hầu hết mọi người chỉ nhìn vào DWORD LPCSTRbản sao vô tận, v.v. và nghĩ rằng "đó là một int / chuỗi / v.v.", nó đến điểm mà bạn dành nhiều thời gian để dựng lên các công cụ của mình hơn là bạn thực sự thiết kế mã .
jrh

1
@jrh Đúng. Thử nghiệm giấy quỳ của tôi để giới thiệu các loại "danh nghĩa" (chỉ khác nhau về tên) là khi các hàm / phương thức phổ biến không có ý nghĩa gì trong trường hợp sử dụng của chúng tôi, ví dụ: ints thường được tóm tắt, nhân lên, v.v. vì vậy tôi sẽ làm cho IDkhác biệt từ int. Điều này có thể đơn giản hóa một API, bằng cách thu hẹp những gì chúng ta có thể làm với một giá trị (ví dụ: nếu chúng ta có một ID, nó sẽ chỉ hoạt động với find, chứ không phải ví dụ agehoặc highscore). Ngược lại, nếu chúng ta thấy mình chuyển đổi nhiều hoặc viết cùng một chức năng / phương thức (ví dụ find) cho nhiều loại, đó là dấu hiệu cho thấy sự khác biệt của chúng ta quá tốt
Warbo 26/11/18

1

Tôi sẽ bỏ phiếu cho tuyên bố rõ ràng như FindByID .... Phần mềm nên được xây dựng cho Thay đổi. Nó nên được mở và đóng (RẮN). Vì vậy, lớp được mở để thêm phương thức tìm tương tự như giả sử FindByName .. vv

Nhưng FindByID đã bị đóng và việc triển khai của nó là đơn vị được thử nghiệm.

Tôi sẽ không đề xuất các phương thức với các vị từ, những phương thức này tốt ở cấp độ chung. Điều gì xảy ra nếu dựa trên trường (ByID), bạn có phương pháp hoàn chỉnh khác nhau.


0

Tôi ngạc nhiên không ai đề nghị sử dụng một vị ngữ như sau:

User Find(Predicate<User> predicate)

Với cách tiếp cận này, không chỉ bạn giảm bề mặt API mà còn cung cấp thêm quyền kiểm soát cho người dùng sử dụng nó.

Nếu điều đó là không đủ, bạn luôn có thể mở rộng nó theo nhu cầu của bạn.


1
Vấn đề là nó kém hiệu quả hơn do thực tế là nó không thể tận dụng những thứ như chỉ số.
Solomon Ucko
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.