Các phương thức không phải là các hàm thuần túy của vùng Viking và tương tác với các API hoặc phần cứng bên ngoài có phải là tĩnh không?


8

Khi đọc về việc khi nào nên tạo một phương thức tĩnh hay không, tôi đã thấy một nguyên tắc chung, như được tóm tắt bởi bài đăng này , rằng một phương thức chỉ nên tĩnh nếu nó không sửa đổi trạng thái và kết quả của nó chỉ phụ thuộc vào các tham số được cung cấp cho nó Tuy nhiên, câu trả lời được bình chọn nhiều nhất trong bài đăng này nói rằng các phương pháp tĩnh nên được sử dụng bất cứ khi nào có thể. Nhiều câu trả lời trong bài này nói rằng người ta nên làm bất cứ điều gì hợp lý nhất.

Trong trường hợp của tôi, tôi có ~ 15 phương thức trong một lớp tiện ích chung vì chúng được sử dụng ở nhiều nơi và không có ý nghĩa như là một thành viên của bất kỳ mô hình hoặc mô hình xem nào (sử dụng CVM MVVM). Có các phương thức void tương tác với các thành phần phần cứng khác nhau bằng cách sử dụng các gói của chúng (ví dụ: Dụng cụ Quốc gia, máy khách OPC, v.v.). Ngoài ra còn có các phương thức lấy một số đầu vào, thực hiện GET hoặc PUT cho API của chúng tôi và sau đó trả về phản hồi - các phương thức này sử dụng HttpClient hoặc một cái gì đó tương tự. Các phương thức này không phải là toán tử đơn giản trên đầu vào, như Math.Sqrt()và không thay đổi trạng thái.

Vì vậy, các phương thức như vậy (và, trong trường hợp này, toàn bộ lớp) có phải là tĩnh không? Có một lợi ích rõ ràng của việc có một lớp tĩnh với các phương thức tĩnh: nó an toàn và nhanh chóng vì bạn không phải tạo một đối tượng. Ngoài ra, mỗi phương thức tĩnh này sử dụng các phương thức và lớp tĩnh hơn cho các tương tác API và phần cứng. Nhược điểm duy nhất mà tôi thấy là tôi sẽ phải viết các bài kiểm tra đơn vị với khung trả phí, như TypeMock Isolator. Chi phí không phải là vấn đề nếu câu trả lời là chế nhạo họ bằng cách sử dụng một cái gì đó như TypeMock Isolator hoặc một số dịch vụ trả phí tương tự, vì vậy nếu đó là sự đồng thuận, điều đó tốt. Tôi chỉ muốn đưa ra một quyết định sẽ mở rộng quy mô và để lại một ít nợ kỹ thuật khi chúng tôi mang đến các nhà phát triển mới và khi dự án của chúng tôi phát triển.

Hãy cho tôi biết nếu tôi cần cung cấp thêm thông tin để làm cho điều này rõ ràng hơn!


3
"tương tác với các thành phần phần cứng khác nhau", "Ngoài ra còn có các phương thức lấy một số đầu vào, thực hiện GET hoặc PUT cho API của chúng tôi và sau đó trả lời phản hồi" -> đây là những ví dụ con về việc tương tác với trạng thái
Caleth

2
Để mở rộng nhận xét của @ Caleth: Hãy nhớ rằng "trạng thái" không chỉ áp dụng cho các số và số 0 trong bộ nhớ. Nếu bạn gọi một chức năng di chuyển cánh tay của robot, bạn đã thay đổi trạng thái --- trong thế giới thực --- cùng với tất cả các phân nhánh làm việc đó.
Greg Burghardt

1
"An toàn và nhanh chóng vì bạn không phải tạo một đối tượng". Trong trường hợp API mạng, bạn chắc chắn đang tạo nhiều đối tượng bên trong cuộc gọi . Và, trong mọi trường hợp, thời gian để tạo đối tượng của bạn là nhỏ so với độ trễ mạng.
dùng949300


Bạn sẽ làm gì khi bạn có nhiều hơn một thành phần phần cứng giống nhau? Sao chép / dán tất cả các phương pháp?
dùng253751

Câu trả lời:


15

Vì vậy, các phương thức như vậy (và, trong trường hợp này, toàn bộ lớp) có phải là tĩnh không?

Không. Hoặc ít nhất, bạn không nên sử dụng chúng trực tiếp làm thống kê.

Nếu bạn đang làm việc với các thành phần phần cứng khác nhau, bạn rất có thể muốn chế giễu chúng, sử dụng các thành phần mới và chọn cái nào bạn đang sử dụng. Rằng tất cả đều hét lên vì có một giao diện (hoặc trừu tượng hóa khác) vì vậy phần còn lại của mã của bạn có thể nhanh chóng bỏ qua rằng có một số thành phần phần cứng nào đó. Và thống kê trong hầu hết các ngôn ngữ chơi rất kém với các giao diện (và các cơ chế trừu tượng khác).


1
Hoặc ít nhất, thông thường có các tập hợp các phương thức tĩnh thực hiện các cuộc gọi thực tế nói chuyện với các công cụ bên ngoài, nhưng sau đó để xây dựng một lớp API trạng thái trên đầu trang và hiển thị lớp đó như là cách để hoàn thành công việc và đó là lớp bị chế giễu thay vì các giao diện bên dưới, tức là mẫu mặt tiền.
tên gì là

Cảm ơn vi đa trả lơi! Đây dường như là sự đồng thuận, vì vậy tôi sẽ tuân thủ nguyên tắc này trong tương lai.
adjordan

4

Các mẩu lời khuyên mâu thuẫn mà bạn đã đọc đều có ý nghĩa theo một cách nào đó, nhưng tất cả chúng đều đưa ra các giả định (khác nhau) hoặc các cụm từ mơ hồ. Đó là một trong những kiểu phân biệt "nói cùng một từ với các từ khác nhau".

một phương thức chỉ nên tĩnh nếu nó không sửa đổi trạng thái

Lưu ý sự mơ hồ của "sửa đổi một trạng thái". Ví dụ sau đây vi phạm quy tắc này (theo nghĩa đen), nhưng nó bảo tồn tinh thần (nghĩa bóng) của quy tắc:

public static void SetUserNameToBob(User u)
{
    u.Name = "Bob";
}

Điều này sửa đổi trạng thái của Userđối tượng, vì vậy trên thực tế nó "sửa đổi một trạng thái".

Tuy nhiên, phương pháp này không dựa vào một trạng thái nội bộ cụ thể để quyết định luồng logic riêng của nó (ví dụ: u.Name = currentlySelectedDefaultName()sẽ là vi phạm vì tên được chọn là trạng thái được chọn). Và tôi nghĩ đó là những gì có nghĩa là: không có trạng thái nội bộ được sửa đổi.

[một phương thức chỉ nên tĩnh nếu] kết quả của nó chỉ phụ thuộc vào các tham số được cung cấp cho nó

Nhìn vào mục trước, cái này có khá nhiều điều tương tự. Điều đó có nghĩa là gì:

public static string currentlySelectedDefaultName; 
public static void SetUserNameToBob(User u)
{
    u.Name = currentlySelectedDefaultName;
}

không nên tĩnh, vì tên "mặc định hiện tại" là trạng thái và do đó, nó không phải là biến / phương thức toàn cục.

Hãy nghĩ về những gì sẽ xảy ra nếu hai luồng riêng biệt đang chạy: một trong số chúng muốn mặc định là "Bob", cái còn lại muốn mặc định là "Jim". Họ sẽ kết thúc cuộc chiến về giá trị, có trách nhiệm tạo ra các vấn đề sửa lỗi lớn và hành vi bất ngờ.
Tuy nhiên, nếu mọi luồng đều có DefaultNameSetterđối tượng riêng , thì chúng không chiến đấu trên cùng một tài nguyên.

Tuy nhiên, câu trả lời được bình chọn nhiều nhất trong bài đăng này nói rằng các phương pháp tĩnh nên được sử dụng bất cứ khi nào có thể.

Đây là loại thực thi một quy tắc bằng cách thử / thất bại. Chúng ta có thể đặt phương thức này thành tĩnh không?

  • Vâng =>tốt! Cư giữ như thê đi!
  • Không =>Điều này chứng tỏ rằng mã đang dựa vào một giá trị không tĩnh ở đâu đó và do đó không nên được tạo thành tĩnh.

Để gián tiếp trích dẫn Jeff Goldblum trong Công viên kỷ Jura , bạn không nên tranh luận về sự cần thiết phải làm một cái gì đó chỉ bằng cách chứng minh rằng nó có thể được thực hiện.

Một lần nữa, cách tiếp cận không nhất thiết (hoặc luôn luôn) sai, nhưng nó giả định một cách mù quáng rằng các phương pháp đã được viết là bất khả tri về mặt logic như có thể, điều này đơn giản không phải là trường hợp.

Ngay cả khi bạn đăng ký theo phương pháp phương pháp này, bạn vẫn chỉ có thể áp dụng nó khi dự án không còn được phát triển. Nếu dự án vẫn đang được phát triển, các phương thức hiện tại có thể là giữ chỗ cho việc thực hiện trong tương lai. Có thể có thể tạo Foo()tĩnh hôm nay, nhưng không phải ngày mai, nếu logic phụ thuộc vào trạng thái đơn giản chưa được triển khai.

Nhiều câu trả lời trong bài này nói rằng người ta nên làm bất cứ điều gì hợp lý nhất.

Chà, họ không sai; nhưng đây không phải là một cụm từ nhỏ nói "làm điều đúng"? Đó không phải là lời khuyên hữu ích, trừ khi bạn đã biết khi nào nên sử dụng statics và khi nào nên tránh chúng. Đó là một cái bẫy 22.


Vậy khi nào bạn nên sử dụng statics?

Như bạn đã nhận thấy, không phải ai cũng đồng ý với quy tắc này, hoặc ít nhất là về cách diễn đạt quy tắc. Tôi sẽ thêm một nỗ lực khác ở đây, nhưng lưu ý rằng điều này thực sự tạo ra một tiêu chuẩn khác :

nhập mô tả hình ảnh ở đây

Ghi nhớ nó trong tâm trí.

Thống kê là những sự thật phổ quát.

Đó là mục đích của một không gian tên toàn cầu: những thứ chính xác trên toàn bộ lớp ứng dụng.

Có một đối số dốc trơn trượt ở đây. Vài ví dụ:

var myConfigKey = ConfigurationManager.AppSettings["myConfigKey"];

Đây là một ví dụ cắt rất rõ ràng. Cấu hình ứng dụng vốn đã ngụ ý rằng cấu hình đó là toàn cầu đối với ứng dụng và do đó, một phương pháp statis được bảo hành.

bool datesOverlap = DateHelper.HasOverlap(myDateA_Start, myDateA_End, myDateB_Start, myDateB_End);

Phương pháp này là phổ biến chính xác. Nó không quan tâm ngày bạn đang so sánh. Phương pháp không quan tâm đến ý nghĩa của ngày. Cho dù chúng là ngày làm việc, ngày hợp đồng, ... không quan trọng đối với thuật toán của phương pháp.

Lưu ý sự tương đồng về ngữ nghĩa giữa ngữ cảnhtrạng thái . Cả hai loại đều đề cập đến một trạng thái "không phổ quát". Điều này chứng tỏ rằng sự khác biệt theo ngữ cảnh do đó phụ thuộc vào nhà nước và không phù hợp để được thực hiện tĩnh.

var newPersonObject = Person.Create();

Đây là một sự thật phổ quát. Quá trình tạo tương tự được sử dụng trên tất cả các ứng dụng.

Tuy nhiên, dòng này có thể bị mờ:

var newManager = Person.CreateManager();
var newJanitor = Person.CreateJanitor();

Từ góc độ kỹ thuật, không có gì thay đổi. Trình quản lý (và người gác cổng) được tạo theo cùng một cách trên tất cả các ứng dụng. Tuy nhiên, điều này đã khéo léo tạo ra một nhà nước (quản lý / người gác cổng), mà chậm nhưng đều đặn làm giảm đi bao quát sự thật thực sự là.

Nó có thể được thực hiện? Từ góc độ kỹ thuật; vâng .
Có nên làm vậy không? Đó là vấn đề cho dù bạn đang tranh luận về nguyên tắc thuần túy, hay tính đến việc thỏa hiệp cần phải được thực hiện để không phấn đấu một cách vô nghĩa cho sự hoàn hảo hợp lý. Vì vậy, tôi sẽ nói nếu nó không tạo ra một vấn đề lớn hơn vấn đề dự định giải quyết .

Khi các tùy chọn mở rộng (người quản lý, người gác cổng, kế toán, nhân viên bán hàng, ...), vấn đề sẽ tăng lên. Đối với một vấn đề đủ lớn, một mô hình nhà máy là mong muốn.

Nếu bạn chỉ có hai tùy chọn và bạn không có lý do để nghi ngờ rằng danh sách các tùy chọn sẽ tăng lên, bạn có thể lập luận rằng các phương thức tạo tĩnh đủ. Một số có thể không đồng ý, và tôi cũng thấy quan điểm của họ. Nhưng tôi có xu hướng thực tế và không quá cầu toàn trong cách tiếp cận của mình.


3

Tuy nhiên, câu trả lời được bình chọn nhiều nhất trong bài đăng này nói rằng các phương pháp tĩnh nên được sử dụng bất cứ khi nào có thể ...

Các phương thức tĩnh ảnh hưởng đến trạng thái mà chúng được ghép trực tiếp là một ý tưởng rất tồi. Chúng dẫn đến trạng thái toàn cầu - "hành động ma quái ở xa" - các vấn đề, mã spaghetti và những thứ tương tự. Họ rất khó để kiểm tra, gỡ lỗi và bảo trì. Việc tôi đọc câu trả lời được đánh giá cao nhất là không khuyến khích bất kỳ điều này, vì vậy tất cả đều tốt.

Vì vậy, các phương thức như vậy (và, trong trường hợp này, toàn bộ lớp) có phải là tĩnh không?

Mà phụ thuộc. Hãy lấy một ví dụ về " các phương thức void tương tác với các thành phần phần cứng khác nhau bằng các gói của chúng ". Các phương thức đó có thể là tĩnh, nhưng chỉ khi chúng được tách rời khỏi các gói đó. Một cách để đạt được điều đó là vượt qua các gói đó dưới dạng tham số. Bạn muốn thử nghiệm phương pháp mà không cần phần cứng? Dễ dàng, vượt qua trong một hình nộm, giả, gói ghi lại các hành động, thay vì truy cập phần cứng thông qua tham số. Điều tương tự cũng áp dụng cho các phương thức HTTP; miễn là các phương tiện truy cập API thực tế cũng được truyền vào như một tham số.

Nhưng, bạn có thực sự đạt được bất cứ điều gì theo cách này qua việc tạo một cá thể và tiêm các gói, các lớp HTTP, v.v. vào lúc xây dựng không? Chắc là không. Chắc chắn, chúng là các phương thức tĩnh, vì vậy bạn không cần một thể hiện. Nhưng bạn có thêm sự phức tạp của việc cần phải phơi bày tất cả các gói đó, v.v. trong toàn bộ mã để truyền chúng dưới dạng tham số. Thay vào đó, việc tạo một cá thể đơn giản và thay vào đó sẽ dễ dàng hơn rất nhiều.


Bạn đang nói để vượt qua một trường hợp duy nhất của bất kỳ lớp "UtilityMethods.cs" nào tôi tạo ra? Tôi hiện đang sử dụng phương pháp tiêm phụ thuộc với SimpleIoC, điều mà tôi nghĩ là cùng một ý tưởng.
adjordan

2
@adjordan, thực sự. Cho dù bạn sử dụng DI thuần túy (người nghèo) hay khung IoC để hỗ trợ tiêm thuốc là tùy thuộc vào bạn. Các pincipl vẫn giữ nguyên: chỉ dễ dàng vượt qua một thể hiện IUtilityMethodshơn là vượt qua nhiều thể hiện cần được truyền cho các hàm tĩnh.
David Arno

1

Những gì tôi khuyên và những gì tôi đang sử dụng trong nhóm của mình là sử dụng các phương thức / lớp tĩnh nếu có nhu cầu thực sự cho nó. Nó không phải là tùy chọn đầu tiên để sử dụng các phương thức / lớp tĩnh. Nó phải là một lý do rất tốt cho điều đó. Nhiều nhà phát triển thấy tùy chọn tĩnh nhanh hơn và do đó rẻ hơn. Nhưng đó có thể là vào đầu. Chi phí là để xây dựng một Bài kiểm tra đơn vị phức tạp hơn và thiếu khả năng sử dụng hợp đồng (giao diện) với nhiều triển khai. Đối với các phương thức / lớp tương tác với phần cứng. Đây không nên là tĩnh. Đầu tiên và cho hầu hết, không có lý do. Thứ hai, cần phải giả định các lớp này trong các bài kiểm tra đơn vị để có mã kiểm tra đơn giản hơn. Thứ ba, có thể thay thế việc thực hiện trong tương lai.


0

Các hàm tĩnh sẽ khó khăn hơn khi bạn muốn cho phép nhiều truy cập song song.

Ví dụ, nó hoàn toàn hợp lý và có thể là một ý tưởng tốt, cho phép nhiều cuộc gọi HTTP thực hiện cùng một lúc. Với một đối tượng thể hiện, bạn có thể dễ dàng duy trì trạng thái khác nhau cho các cuộc gọi đó - tiêu đề, mã kết quả, bộ đệm, v.v ...

Với một phương thức tĩnh duy nhất, cuối cùng, bạn có thể tạo một số thể hiện bên trong để giữ trạng thái đó. Hoặc bạn đồng bộ hóa mọi thứ (Tôi đang sử dụng thuật ngữ Java, YMMV), dễ bị lỗi. Hoặc sử dụng một hàng đợi. Trong mọi trường hợp, nếu không có một số bước chân ưa thích, bạn sẽ mất khả năng chạy song song. Nếu một cuộc gọi HTTP bị hỏng do trục trặc mạng, tất cả những người khác phải chờ.

Tương tự cho phần cứng của bạn - trong một số trường hợp, có thể có ý nghĩa khi cho phép truy cập song song.

Vì những lý do này, tôi không đồng ý với câu trả lời 8 tuổi mà bạn đã trích dẫn.


4
Một hàm tĩnh không nên chứa trạng thái nào cả.
Pieter B

1
@Pieter B Tất nhiên (khác với một quầy). Trong ví dụ HTTP, nó sẽ phải tạo một đối tượng MyHTTPHelper cho mỗi cuộc gọi để giữ trạng thái. Tại thời điểm đó, có thể đơn giản hơn để người gọi ban đầu tạo một đối tượng, không sử dụng một phương thức tĩnh nào cả.
user949300
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.