Thuật ngữ (hoặc mô hình lồng tiếng?) Đối với nhóm Làm gì đó nếu chưa thực hiện được [đóng]


54

Nghe có vẻ khá cơ bản, tôi biết, nhưng gần đây tôi có một đồng nghiệp nói với tôi rằng một phương pháp được gọi startHttpServerlà quá phức tạp để hiểu bởi vì nó chỉ khởi động máy chủ nếu nó chưa chạy. Tôi thấy tôi gặp rắc rối khi trả lời, "Nghiêm túc sao? Tôi đã làm điều này trong nhiều thập kỷ - đó là một mô hình phổ biến trong lập trình." Thường xuyên hơn tôi quan tâm thừa nhận anh ta trở lại với một số bằng chứng tài liệu cho thấy rằng toàn bộ cộng đồng lập trình đứng sau quan điểm của anh ta và cuối cùng tôi cảm thấy ngượng ngùng.

Câu hỏi : Có một mẫu thiết kế tài liệu nào đằng sau khái niệm phương pháp là không có nếu hành động cần thiết đã có hiệu lực không? Hoặc, nếu không phải là một mẫu, nó cũng có một cái tên? Và nếu không, có lý do nào để nghĩ rằng nó quá phức tạp để xem xét viết một phương pháp theo cách này không?


6
nghe có vẻ như bộ nhớ đệm - và nó thường được coi là phức tạp thực sự (xem thêm Làm thế nào để tôi giải thích $ {Something} với $ {someone}? )
gnat

8
Bạn nhận được 3 câu trả lời khác nhau, nhưng câu trả lời tốt nhất sẽ là, áp dụng tất cả: đổi tên hàm ban đầu, chia mã của nó thành hai hàm (trong đó một trong hai bây giờ có tên startHttpServer) và vâng, thuật ngữ "idempotent" áp dụng ở đây tốt.
Doc Brown

8
Đồng nghiệp của bạn cung cấp loại bằng chứng phản biện nào? Bạn có thể cung cấp một liên kết đến nó?
Robert Harvey

5
Tôi tò mò muốn xem từ đâu đồng nghiệp của bạn nguồn trích dẫn của mình. Ít nhất là xung quanh Stack Overflow và Software Engineering, chức năng này sẽ được đặt tên xấu nhất nhưng sẽ không có bất kỳ hành vi bất thường nào. Hãy nhớ rằng không phải vì ai đó ở đâu đó đưa một cái gì đó lên blog mà nó đại diện cho toàn bộ quan điểm của cộng đồng lập trình. Ngay cả những tên tuổi lớn như Martin Fowler thỉnh thoảng cũng nói một số điều rất kỳ lạ. Tất cả chúng ta chỉ là con người.
T. Sar - Tái lập Monica

4
Tôi nghĩ rằng một cách tốt hơn để suy nghĩ về "Làm điều gì đó nếu nó chưa được thực hiện" sẽ là "Đặt hệ thống ở trạng thái này". Tất nhiên, nếu hệ thống đã ở trạng thái đó, phương thức sẽ không làm gì cả - đó sẽ là hành vi được mong đợi.
T. Sar - Tái lập Monica

Câu trả lời:


127

Như NickWilliams đã nói : khái niệm mà OP mô tả được gọi là idempotent (danh từ Idempotency ). Đó thực sự là thực tế phổ biến, đặc biệt là trong các API cấp cao.

NHƯNG: Đổi tên chức năng.

Thay vì startHttpServergọi nó makeSureHttpServerIsRunninghay ensureHttpServerIsRunning.

Khi một chức năng được gọi startHttpServer, người đọc mong đợi nó sẽ khởi động máy chủ HTTP; Khi được gọi mười lần liên tiếp, tôi sẽ có mười máy chủ đang chạy. Chức năng của bạn không làm điều đó hầu hết thời gian. Ngoài ra, tên với "bắt đầu" gợi ý rằng nếu tôi chỉ muốn một máy chủ chạy, tôi sẽ phải theo dõi xem chức năng đã được gọi hay chưa.

Khi một hàm được gọi makeSureHttpServerIsRunning, tôi giả sử nó sẽ thực hiện những điều cần thiết để đảm bảo rằng máy chủ HTTP đang chạy, rất có thể bằng cách kiểm tra xem nó có đang chạy hay không và khởi động nó theo cách khác. Tôi cũng giả định rằng chức năng này đảm bảo rằng máy chủ đang thực sự chạy (bắt đầu một máy chủ có thể liên quan đến một số thời điểm mà nó chưa hoàn toàn chạy).


83
Điều này. Cá nhân tôi sử dụng "Đảm bảo" thay thế. Vấn đề là điều này sẽ trở thành một sơ đồ đặt tên được hiểu trong nhóm của bạn.
Euphoric

3
hoặc đã bắt đầu ném một ngoại lệ nếu nó đã bắt đầu. như sqlconnection.open
Ewan

9
Tôi luôn sử dụng chức năng "đảm bảo" cho chức năng "làm nếu chưa".
Kaz

3
Một tên khác tôi thường thấy là getOrCreateS Something, chỉ tạo trong cuộc gọi đầu tiên và sau đó chỉ cần trả lại một cái gì đó
Fabich

5
@aroth Bạn có chắc chắn bạn sẽ không mong muốn nó cố gắng tìm bất kỳ cổng có sẵn nào và trả lại không? Hay chỉ là viết xấu? ;)
jpmc26

33

Đổi tên nó thành EnsureServerRunning.

Hoàn toàn rõ ràng và rõ ràng rằng nó đảm bảo rằng nó đang chạy (nếu không) mà không ngụ ý khởi động lại nếu có.

(Thay thế : StartServerIfNotRunning?)


1
Hầu hết người gọi chỉ quan tâm rằng máy chủ đang chạy sau cuộc gọi. Làm thế nào đạt được, họ không quan tâm.
gnasher729

29

Không thực sự là một mẫu thiết kế nhưng tôi sẽ gọi phương thức của bạn là idempotent . Có thuật ngữ thường được sử dụng để chỉ các cuộc gọi từ xa nhưng mô tả có vẻ phù hợp với những gì bạn đang làm.

Phương pháp tạm thời. Các phương thức cũng có thể có thuộc tính "idempotence" trong đó (ngoài các vấn đề lỗi hoặc hết hạn), các tác dụng phụ của N> 0 yêu cầu giống hệt như đối với một yêu cầu. ( Từ W3.org )

Tác dụng phụ của máy chủ ở đây là máy chủ http được khởi động sau khi phương thức được gọi. Tôi thấy không có gì sai với một phương pháp làm điều này.

Nếu bạn cần một mẫu thiết kế, tôi đoán bạn có thể hiển thị httpServer của mình dưới dạng một singleton được bắt đầu khi khởi tạo.


5
Chức năng này không idempotent nếu gọi đó là một lần khởi động máy chủ http, và gọi đó là một lần thứ hai thì không. Đó là nghĩa đen đối lập của idempotence. Sẽ là bình thường nếu mỗi cuộc gọi bắt đầu một máy chủ http mới.
Polygnome

36
@Polygnome Wikipedia định nghĩa idempotence là f (f (x)) = f (x) thường khớp với mô tả của Nick. Ở đây, đầu vào và đầu ra của chức năng sẽ là trạng thái máy chủ ẩn, do đó, máy chủ của hệ thống đang chạy trạng thái có thể là một điểm cố định cho chức năng này. Có lẽ tôi đang hiểu nhầm bài viết Wikipedia ở đây, bạn có thể liên kết đến các tài liệu tham khảo khác không?
amon

16
@Polygnome: câu trả lời này là chính xác, idempotency đề cập đến các chức năng mà không quan trọng nếu chúng được gọi một lần hoặc nhiều lần, kết quả luôn giữ nguyên. Ở đây, kết quả là một dịch vụ http đang chạy, độc lập với số lần hàm được gọi.
Doc Brown

14
Sự nhầm lẫn xuất phát từ thực tế rằng tính không thay đổi ở cốt lõi của nó là một khái niệm toán học được áp dụng cho các hàm. Định nghĩa này là tốt và đơn giản cho những người và cũng hoạt động tốt cho các ngôn ngữ chức năng thuần túy. Ngay khi bạn giới thiệu các tác dụng phụ, nó sẽ trở nên phức tạp - bạn phải xem xét môi trường bạn thực thi hàm trong như một đối số (ẩn) và đầu ra của hàm. Nếu trạng thái đó không được sửa đổi bằng các cuộc gọi tiếp theo đến chức năng thì nó không hoạt động, nếu không thì không. Trong trường hợp này, trạng thái có liên quan là "máy chủ http đang chạy" - vì vậy chức năng này không hoạt động.
Voo

10
@Polygnome Xin lỗi, bạn sai rồi. Idempotent có nghĩa là yêu cầu có tác dụng tương tự cho dù nó được thực hiện một lần hay nhiều lần. Bắt đầu máy chủ N http nếu nhận được N bản sao của yêu cầu chắc chắn không phải là idempotent.
Kaz

7

Là một trong những người thực hiện điều này công cụ , startHttpServerbạn cần phải cố gắng để làm cho nó đơn giản nhất, trơn tru và liền mạch để sử dụng ...

Logic của hàm

Về mặt kỹ thuật, bằng cách tách startHttpServer's logic vào 2 chức năng gọi họ riêng biệt , tất cả những gì bạn làm là di chuyển startHttpServer ' s idempotency vào mã gọi cả hai chức năng thay vì ... Hơn nữa, trừ khi bạn quấn cả logic trong một chức năng thứ ba (đó là những gì hiện startHttpServerở vị trí đầu tiên), điều này buộc bạn phải viết mã chưa được mã hóa, sao chép nó theo cấp số nhân ở mọi nơi bạn phải gọi startHttpServer. Tóm lại, startHttpServer phải gọi chính nó là isHttpServerRunningchức năng.

Vì vậy, quan điểm của tôi là:

  • Thực hiện isHttpServerRunningchức năng bởi vì điều này có thể cần thiết một cách độc lập ...
  • Triển khai startHttpServerlàm cho nó sử dụng isHttpServerRunningđể xác định hành động tiếp theo của nó phù hợp ...

Tuy nhiên, bạn có thể startHttpServertrả về bất kỳ giá trị nào mà người dùng của chức năng này có thể cần, ví dụ:

  • 0 => máy chủ bắt đầu thất bại
  • 1 => máy chủ bắt đầu thành công
  • 2 => máy chủ đã được khởi động rồi

Chức năng đặt tên

Trước hết, mục tiêu chính của người dùng là gì? Để khởi động máy chủ HTTP , phải không?

Về cơ bản, không có vấn đề gì bằng cách dự định bắt đầu một cái gì đó đã được bắt đầu, AKA 1*1=1. Vì vậy, ít nhất với tôi, gọi nó là " ensureHttpServerIsRunning" dường như không cần thiết lắm, tôi quan tâm nhiều hơn đến việc tên của hàm dài bao nhiêu, tự nhiên và đáng nhớ.

Bây giờ nếu bạn muốn biết làm thế nào để làm việc chi tiết chức năng dưới mui xe, có tài liệu hoặc nguồn mã cho điều đó, ý tôi là giống như bất kỳ chức năng nào khác từ thư viện / framework / API / vv ...

Bạn học hàm một lần trong khi bạn viết nó nhiều lần ...

Vì vậy, dù sao, tôi sẽ gắn bó với startHttpServercái ngắn hơn, đơn giản hơn và rõ ràng hơn ensureHttpServerIsRunning.


1
Đặc biệt là vì phương thức phải lấy một số đối số cấu hình, như thư mục gốc, cài đặt bảo mật, có thể là số cổng, tôi thoải mái 100% với "bắt đầu" ở đây.
user949300

@ user949300: Người gọi của "notifyHttpServerIsRasty" không quan tâm đến cấu hình, thư mục gốc, v.v ... Đó là công việc của người thực hiện nó.
gnasher729

2
@ gnasher729 Giả sử máy chủ http là Singleton. Singletons là ác. Và có thể không phù hợp ở đây. Người ta có thể dễ dàng có nhiều máy chủ trên nhiều cổng. Nếu thực sự chỉ có một máy chủ http, thì toàn bộ phương pháp này là IMO, thiết kế xấu Tốt hơn là chỉ khởi động máy chủ một lần khi khởi tạo chương trình.
user949300

2

Tôi cho rằng đồng nghiệp của bạn có nghĩa startHttpServerlà đang làm quá nhiều:

  • Kiểm tra xem máy chủ đã chạy chưa,
  • Khởi động máy chủ nếu cần.

Đó là hai phần không liên quan của mã. Chẳng hạn, một tình huống tương tự tồn tại khi một ứng dụng máy tính để bàn phải đảm bảo rằng nó chưa chạy khi được khởi chạy; sẽ có một phần mã xử lý các trường hợp ứng dụng (ví dụ sử dụng mutex) và mã sẽ bắt đầu vòng lặp thông báo ứng dụng.

Điều này có nghĩa là bạn không phải có một, mà ít nhất là hai phương thức :

  • isHttpServerRunning: boolean
  • startHttpServer

Điểm vào ứng dụng sẽ gọi phương thức đầu tiên, và sau đó là phương thức thứ hai nếu giá trị trả về là false. Bây giờ, mọi phương pháp đang làm một và một điều, và rất dễ hiểu.


¹ Nếu logic cần biết nếu máy chủ đang chạy quá phức tạp, nó có thể yêu cầu phân tách thành nhiều phương thức.


21
Bây giờ một cái gì đó khác gọi hai chức năng đang làm hai việc, cộng với việc phơi bày các chức năng riêng biệt có thể đưa ra các điều kiện chủng tộc. Không ăn trưa miễn phí.
tên

1
Nếu đó là quá nhiều cho đồng nghiệp, chúc may mắn.
gnasher729

@ gnasher729: câu trả lời này không mâu thuẫn với bạn - hoàn toàn ngược lại, nó thực sự có thể là một ý tưởng tốt để kết hợp việc đổi tên phương thức với việc chia nó thành hai. Và miễn là chúng ta không nhìn thấy mã thực, chúng ta không biết mã phức tạp như thế nào.
Doc Brown

10
Câu trả lời này dường như phản đối sự trừu tượng và KHÔ. Điều gì nếu startHttpServerđược gọi nhiều hơn một nơi trong mã? Có nên sao chép nhiều dòng tương tự ở khắp mọi nơi? Điều này nên được thực hiện với tất cả các chức năng? Khá sớm chương trình của bạn sẽ có kích thước vô hạn.
JacquesB

3
Tôi nghĩ rằng tác dụng phụ đầu tiên của phương pháp này là sự khởi đầu của startHttpServerphương pháp sẽ trông gần giống như vậy if (isHttpServerRunning()){ return; }. Bạn đang khẳng định một quy tắc kinh doanh rằng "không hợp lệ để khởi động máy chủ http nếu nó đã chạy", nhưng sau đó khiến người khác có trách nhiệm thực thi quy tắc đó. Đặc biệt và liên tục ở mọi địa điểm nơi họ có thể gọi startHttpServer.
aroth

2

Vì bạn không chỉ định ngôn ngữ, trong JavaScript, nhiều thư viện có chức năng "một lần", ví dụ như gạch dưới . Vì vậy, nếu điều đó quen thuộc với bạn, hãy gọi nó là mẫu "một lần" và có thể đổi tên phương thức của bạn.

Bản thân tôi, đến nhiều hơn từ Java, các thuật ngữ "bộ nhớ đệm" hoặc "đánh giá lười biếng" xuất hiện trong tâm trí. "Idempotent" là đúng về mặt kỹ thuật và là một lựa chọn tốt, đặc biệt. nếu bạn có một nền tảng chức năng hơn.


Nó có thể không phải là "một lần", nếu máy chủ có thể bị dừng.
gnasher729

@ gnasher729. Tôi có thể tưởng tượng một restartHttpServer()phương pháp hiếm khi được sử dụng . Nhưng chỉ dừng lại - trường hợp sử dụng ở đó là gì? Bạn thích thất bại kết nối lẻ tẻ? :-)
user949300

Không cần phải có trường hợp sử dụng cho việc dừng máy chủ HTTP bởi vì nó có thể đã bị dừng bởi một thứ gì đó bên ngoài chương trình - máy chủ HTTP có thể đã bị lỗi và chết, một quản trị viên có thể đã dừng thủ công vì một số lý do, vv
Dave Sherohman

Dave, một vụ tai nạn là "đặc biệt" và nên được xử lý như vậy. Ngoài ra, vì một sự cố có thể xảy ra bất cứ lúc nào , phải ensureRunning()chăng mọi dòng mã của bạn đều phải như vậy? :-) Đối với quản trị viên dừng nó, việc mã khác này liên tục khởi động lại trong khi quản trị viên đang cố gắng sửa đổi hoặc sửa chữa một cái gì đó sẽ vô cùng khó chịu và sai. Hãy để quản trị viên khởi động lại nó, không phải mã.
dùng949300

-2

Tôi thích hơn startHttpServerIfNotIsRunning.

Bằng cách này, điều kiện được đề cập rõ ràng đã có trong tên phương thức. Ensurehoặc makeSurecó vẻ hơi mơ hồ với tôi, vì nó không phải là một biểu hiện kỹ thuật. Có vẻ như chúng ta không biết chính xác những gì sẽ xảy ra.


Tôi cho rằng bạn không phải là người bản ngữ? Bởi vì đảm bảo có định nghĩa chính xác về những gì chúng ta sẽ làm. Nhưng vẫn là một điểm công bằng rằng tài liệu và API không nên yêu cầu trình độ tiếng Anh bản ngữ để có thể hiểu được.
Voo

1
Cảm ơn tôi chính xác không Ensurecó nghĩa là gì . Tôi vẫn không thích từ này cho một biểu thức kỹ thuật. Ensurelà một cái gì đó của con người. Một hệ thống không thể đảm bảo bất cứ điều gì, nó chỉ làm những gì nó phải làm.
Herr Derb

2
@Voo - Tôi là người bản ngữ và tôi không chắc điều đó có nghĩa là gì.
Jonathan Cast

Vì mọi người đăng bài ở đây đều có quyền truy cập internet, tại sao họ không tìm kiếm định nghĩa của 'Đảm bảo' nếu họ không chắc chắn về ý nghĩa của nó? Một chút chuyện vặt vãnh ... nhiều người trao đổi cách sử dụng 'Đảm bảo' và 'Đảm bảo' mà không nhận ra rằng việc sử dụng một trong những từ đó có thể vô tình khiến họ 'chịu trách nhiệm pháp lý' trong khi những người khác thì không.
Dunk

@jcast: Nghiêm túc chứ?
gnasher729

-3

Điều mà đồng nghiệp của bạn nên nói với bạn là bạn không có kinh doanh viết phương pháp đó. Nó đã được viết rất nhiều lần và được viết tốt hơn khả năng bạn viết nó. Ví dụ: http://docs.ansible.com/ansible/latest/systemd_module.html https://docs.saltstack.com/en/latest/ref/states/all/salt.states.service.html

Từ góc độ kiến ​​trúc, có một chút mã tùy ý quản lý máy chủ web là công cụ của những cơn ác mộng. Trừ khi quản lý dịch vụ là độc quyền những gì mã của bạn làm. Nhưng tôi đoán bạn đã không viết monit (hoặc kubernetes hoặc ...).


2
Ngôn ngữ là Java và phương thức được thiết kế để bắt đầu một máy chủ nhúng sâu trong một ứng dụng Jersey độc lập. Tôi không thể lỗi chính xác của nhận xét của bạn, vì tôi đã không cung cấp cho bạn nhiều bối cảnh, nhưng bạn đã giả định rất nhiều trong việc đưa ra tuyên bố của mình. Hoàn toàn ổn khi có một phương thức gọi vào cầu cảng hoặc lớn để khởi động máy chủ.
John Calcote

1
Có hàng triệu ứng dụng kinh doanh bao gồm máy chủ HTTP tự lưu trữ để cung cấp API. Và tất cả những sẽ cần một số mã rất đơn giản mà thực sự thiết lập thư viện họ đang sử dụng và cung cấp thông tin như giao diện để ràng buộc trên, cổng nào cần sử dụng, thiết lập bảo mật, vv Có một startServerchức năng tương tự hoặc là bất cứ điều gì nhưng không phổ biến . Điều đó không có nghĩa là bạn sẽ viết các chi tiết cấp thấp quá mức.
Voo
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.