Có bao giờ là một ý tưởng tốt để giá trị mã hóa cứng vào các ứng dụng của chúng tôi? Hoặc luôn luôn là điều đúng đắn để gọi các loại giá trị này một cách linh hoạt trong trường hợp chúng cần thay đổi?
pi
có thể thay đổi ...
Có bao giờ là một ý tưởng tốt để giá trị mã hóa cứng vào các ứng dụng của chúng tôi? Hoặc luôn luôn là điều đúng đắn để gọi các loại giá trị này một cách linh hoạt trong trường hợp chúng cần thay đổi?
pi
có thể thay đổi ...
Câu trả lời:
Có, nhưng làm cho nó rõ ràng .
Làm:
Đừng:
diameter = 2 * radius
hay diameter = RADIUS_TO_DIAMETER_FACTOR * radius
? Thực sự có những trường hợp góc mà một con số ma thuật có thể là giải pháp tốt hơn.
diameter = radius << 1
? Tôi cho rằng đó cũng có thể là diameter = radius << RADIUS_TO_DIAMETER_BITS_TO_SHIFT
.
diameter = radius.toDiameter()
Điều tôi thấy kỳ lạ về câu hỏi và trả lời này cho đến nay là không ai thực sự cố gắng xác định rõ ràng "mã cứng" hay quan trọng hơn là các lựa chọn thay thế.
tl; dr: Vâng, đôi khi , đó là một ý tưởng tốt cho các giá trị mã cứng, nhưng không có quy tắc đơn giản nào khi nào ; nó phụ thuộc hoàn toàn vào bối cảnh.
Câu hỏi thu hẹp nó thành các giá trị , mà tôi nghĩ là số ma thuật , nhưng câu trả lời cho dù chúng có phải là một ý tưởng tốt hay không liên quan đến những gì chúng thực sự được sử dụng cho!
Một số ví dụ về các giá trị "mã hóa cứng" là:
Giá trị cấu hình
Tôi co rúm mỗi khi thấy câu nói như thế command.Timeout = 600
. Tại sao 600? Ai quyết định điều đó? Có phải đã hết thời gian trước đó và ai đó đã tăng thời gian chờ là hack thay vì khắc phục vấn đề hiệu năng cơ bản? Hoặc nó thực sự là một số kỳ vọng đã biết và được ghi nhận cho thời gian xử lý?
Chúng không nên là số ma thuật hoặc hằng số, chúng nên được đặt bên ngoài trong tệp cấu hình hoặc cơ sở dữ liệu ở đâu đó với một tên có ý nghĩa, bởi vì giá trị tối ưu của chúng được xác định chủ yếu hoặc hoàn toàn bởi môi trường mà ứng dụng đang chạy.
Các công thức toán học
Các công thức thường có xu hướng khá tĩnh, do đó bản chất của các giá trị không đổi bên trong không thực sự đặc biệt quan trọng. Thể tích của một hình chóp là (1/3) b * h. Chúng ta có quan tâm 1 hoặc 3 đến từ đâu không? Không hẳn vậy. Một nhà bình luận trước đây đã chỉ ra rằng diameter = radius * 2
có lẽ tốt hơn diameter = radius * RADIUS_TO_DIAMETER_CONVERSION_FACTOR
- nhưng đó là một sự phân đôi giả.
Những gì bạn nên làm cho loại kịch bản này là tạo ra một chức năng . Tôi không cần biết bạn đã nghĩ ra công thức như thế nào nhưng tôi vẫn cần biết nó dùng để làm gì . Nếu, thay vì bất kỳ điều vô nghĩa nào được viết ở trên, tôi viết volume = GetVolumeOfPyramid(base, height)
thì đột nhiên mọi thứ trở nên rõ ràng hơn rất nhiều và hoàn toàn ổn khi có các số ma thuật bên trong hàm ( return base * height / 3
) vì rõ ràng chúng chỉ là một phần của công thức.
Chìa khóa ở đây tất nhiên là có chức năng ngắn và đơn giản . Điều này không hoạt động cho các hàm với 10 đối số và 30 dòng tính toán. Sử dụng thành phần chức năng hoặc hằng số trong trường hợp đó.
Quy tắc tên miền / doanh nghiệp
Cái này luôn là vùng màu xám vì nó phụ thuộc vào chính xác giá trị là gì. Hầu hết thời gian, đó là những con số ma thuật đặc biệt là ứng cử viên để biến thành hằng số, bởi vì điều đó làm cho chương trình dễ hiểu hơn mà không làm phức tạp logic chương trình. Xem xét bài kiểm tra if Age < 19
so với if Age < LegalDrinkingAge
; bạn có thể có thể tìm ra những gì đang diễn ra mà không có hằng số, nhưng nó dễ dàng hơn với tiêu đề mô tả.
Đây cũng có thể trở thành ứng cử viên cho sự trừu tượng hóa chức năng, ví dụ function isLegalDrinkingAge(age) { return age >= 19 }
. Điều duy nhất là logic kinh doanh của bạn thường phức tạp hơn thế rất nhiều và có thể không có ý nghĩa gì khi bắt đầu viết ra hàng tá hàm với 20-30 tham số mỗi hàm. Nếu không có sự trừu tượng rõ ràng dựa trên các đối tượng và / hoặc các chức năng thì việc sử dụng các hằng số là ổn.
Thông báo trước là, nếu bạn đang làm việc cho bộ phận thuế, nó sẽ trở nên thực sự, thực sự nặng nề và vô nghĩa khi viết AttachForm(FORM_CODE_FOR_SINGLE_TAXPAYER_FILING_JOINTLY_FOR_DEPRECIATION_ON_ARMPIT_HAIR)
. Bạn sẽ không làm điều đó, bạn sẽ làm AttachForm("B-46")
bởi vì mọi nhà phát triển đã từng làm việc hoặc sẽ làm việc ở đó sẽ biết rằng "B-46" là mã mẫu cho một người nộp thuế duy nhất nộp đơn blah blah blah - mã hình thức là một phần của chính miền, chúng không bao giờ thay đổi, vì vậy chúng không thực sự là những con số kỳ diệu.
Vì vậy, bạn phải sử dụng hằng số một cách tiết kiệm trong logic kinh doanh; về cơ bản, bạn phải hiểu liệu "số ma thuật" đó có thực sự là số ma thuật hay không nếu đó là khía cạnh nổi tiếng của miền. Nếu đó là tên miền, thì bạn không mã hóa nó trừ khi có cơ hội thực sự tốt nó sẽ thay đổi.
Mã lỗi và cờ trạng thái
Những điều này không bao giờ ổn đối với mã cứng, vì bất kỳ tên khốn tội nghiệp nào đã từng bị tấn công Previous action failed due to error code 46
đều có thể nói với bạn. Nếu ngôn ngữ của bạn hỗ trợ nó, bạn nên sử dụng loại liệt kê. Mặt khác, thông thường bạn sẽ có toàn bộ tệp / mô-đun chứa đầy các hằng số chỉ định các giá trị hợp lệ cho một loại lỗi cụ thể.
Đừng bao giờ để tôi nhìn thấy return 42
trong một trình xử lý lỗi, capiche? Không có lời bào chữa.
Tôi có thể bỏ qua một số kịch bản nhưng tôi nghĩ rằng nó bao gồm hầu hết các kịch bản.
Vì vậy, yeah, đôi khi nó được chấp nhận thực hành đối với các công cụ mã cứng. Đừng lười biếng về điều đó; nó phải là một quyết định có ý thức chứ không phải là mã cẩu thả cũ.
Có nhiều lý do khác nhau để gán một định danh cho một số.
Điều này cung cấp cho chúng tôi các tiêu chí cho chữ nghĩa mã hóa cứng. Chúng nên bất biến, không khó gõ, chỉ xảy ra ở một nơi hoặc bối cảnh và với ý nghĩa dễ nhận biết. Chẳng có điểm nào trong việc xác định 0 là ARRAY_BEGINNING, ví dụ, hoặc 1 là ARRAY_INCREMENT.
Như một bổ sung cho câu trả lời khác. Sử dụng hằng cho chuỗi khi có thể. Tất nhiên, bạn không muốn có
const string server_var="server_var";
nhưng bạn nên có
const string MySelectQuery="select * from mytable;";
(giả sử bạn thực sự có một truy vấn mà bạn muốn nhận tất cả các kết quả từ một bảng cụ thể)
Ngoài ra, sử dụng hằng số cho bất kỳ số nào ngoài 0 (thường). Nếu bạn cần bitmask quyền 255, không sử dụng
const int 8th_bit=255; //or some other obscure naming scheme that equates to 255.
thay vì sử dụng
const int AllowGlobalRead=255;
Tất nhiên, cùng với hằng số, biết khi nào nên sử dụng liệt kê. Các trường hợp trên có lẽ sẽ phù hợp với một.
typedef enum {init_state=0, parse_state=1, evaluation_state=2, ... }
Nó phụ thuộc vào những gì bạn xem xét mã hóa cứng. Nếu bạn cố gắng tránh bất kỳ và tất cả mọi thứ hardcoded, bạn kết thúc trong softcoding lãnh thổ, và thực hiện một hệ thống mà chỉ người tạo có thể quản lý (và đó là hardcode cuối cùng)
Rất nhiều thứ được mã hóa cứng trong bất kỳ khuôn khổ hợp lý nào và chúng hoạt động. tức là không có lý do kỹ thuật tại sao tôi không thể thay đổi điểm vào của ứng dụng C # (static void Main), nhưng mã hóa cứng không tạo ra bất kỳ vấn đề nào cho bất kỳ người dùng nào (ngoại trừ câu hỏi SO không thường xuyên )
Quy tắc ngón tay cái tôi sử dụng là bất cứ điều gì có thể và sẽ thay đổi, mà không ảnh hưởng đến trạng thái của toàn hệ thống, đều có thể gây nhầm lẫn.
Vì vậy, IMHO, thật ngớ ngẩn khi không mã hóa những thứ không bao giờ thay đổi (pi, hằng số hấp dẫn, một hằng số trong một công thức toán học - nghĩ về thể tích của một khối cầu).
Ngoài ra, thật ngớ ngẩn khi không mã hóa những thứ hoặc quy trình có ảnh hưởng đến hệ thống của bạn sẽ yêu cầu lập trình trong mọi trường hợp, nghĩa là thật lãng phí khi cho phép người dùng thêm trường động vào biểu mẫu, nếu bất kỳ trường được thêm nào sẽ yêu cầu nhà phát triển bảo trì đi vào và viết một số kịch bản sẽ làm cho điều đó làm việc. Ngoài ra, thật ngu ngốc (và tôi đã thấy nó một vài lần trong môi trường doanh nghiệp) để tạo ra một số công cụ cấu hình, do đó, không có gì là mã hóa cứng, tuy nhiên, chỉ có các nhà phát triển trong bộ phận CNTT có thể sử dụng nó và nó chỉ dễ sử dụng hơn một chút để làm điều đó trong Visual Studio.
Vì vậy, điểm mấu chốt, liệu một thứ có nên được mã hóa cứng hay không là một hàm của hai biến:
Có bao giờ là một ý tưởng tốt để giá trị mã hóa cứng vào các ứng dụng của chúng tôi?
Tôi chỉ sử dụng các giá trị mã hóa cứng nếu các giá trị được chỉ định trong Thông số kỹ thuật (trên bản phát hành cuối cùng của thông số kỹ thuật), ví dụ: Phản hồi HTTP OK sẽ luôn luôn 200
(trừ khi nó thay đổi trong RFC), vì vậy, bạn sẽ thấy (trong một số mã của tôi ) hằng số như:
public static final int HTTP_OK = 200;
Mặt khác, tôi lưu trữ hằng trong tệp thuộc tính.
Lý do tại sao tôi chỉ định thông số kỹ thuật, là việc thay đổi hằng số trong thông số kỹ thuật yêu cầu quản lý thay đổi, trong đó, các bên liên quan sẽ xem xét thay đổi và phê duyệt / không chấp thuận. Nó không bao giờ xảy ra qua đêm và mất nhiều tháng / năm để phê duyệt. Đừng quên rằng nhiều nhà phát triển sử dụng thông số kỹ thuật (ví dụ HTTP) để thay đổi nó có nghĩa là phá vỡ hàng triệu hệ thống.
Tôi đã nhận thấy rằng bất cứ khi nào bạn có thể trích xuất dữ liệu từ mã của mình, nó sẽ cải thiện những gì còn lại. Bạn bắt đầu nhận thấy các cấu trúc lại mới và cải thiện toàn bộ các phần của mã của bạn.
Đó chỉ là một ý tưởng tốt để làm việc với việc trích xuất các hằng số, đừng coi đó là một quy tắc ngu ngốc, hãy coi đó là một cơ hội để viết mã tốt hơn.
Ưu điểm lớn nhất là cách bạn có thể thấy các hằng số tương tự là sự khác biệt duy nhất trong các nhóm mã - trừu tượng hóa chúng thành mảng đã giúp tôi giảm một số tệp xuống 90% kích thước của chúng và khắc phục khá nhiều lỗi sao chép và dán trong khi đó .
Tôi vẫn chưa thấy một lợi thế duy nhất để không trích xuất dữ liệu.
Gần đây tôi đã mã hóa một hàm MySQL để tính toán chính xác khoảng cách giữa hai cặp lat / long. Bạn không thể chỉ làm pythagorus; các đường kinh độ gần nhau hơn khi vĩ độ tăng dần về các cực, do đó, có một số trig lông có liên quan. Vấn đề là, tôi đã khá rách về việc liệu để mã hóa cứng các giá trị đại diện cho bán kính của trái đất trong dặm.
Cuối cùng tôi đã làm nó, mặc dù thực tế là, các dòng lat / lng gần nhau hơn nhiều, giả sử, mặt trăng. Và chức năng của tôi sẽ làm giảm đáng kể khoảng cách giữa các điểm trên Sao Mộc. Tôi cho rằng tỷ lệ cược của trang web tôi đang xây dựng có một vị trí ngoài trái đất được nhập vào là khá mỏng.
Vâng, nó phụ thuộc nếu ngôn ngữ của bạn được biên dịch. Nếu nó không được biên dịch, nó không phải là vấn đề lớn, bạn chỉ cần chỉnh sửa mã nguồn, ngay cả khi nó sẽ hơi tinh tế đối với một người không lập trình.
Nếu bạn đang lập trình với một ngôn ngữ được biên dịch, đây rõ ràng không phải là một ý tưởng tốt, bởi vì nếu các biến thay đổi, bạn phải biên dịch lại, đó là một sự lãng phí lớn thời gian nếu bạn muốn điều chỉnh biến này.
Bạn không cần tạo một số thanh trượt hoặc giao diện để tự động thay đổi biến của mình, nhưng điều tối thiểu bạn có thể làm là một tệp văn bản.
Ví dụ với dự án ogre của tôi, tôi luôn sử dụng lớp ConfigFile để tải một biến tôi đã ghi vào tệp cấu hình.
Hai lần có hằng số (ít nhất là theo ý kiến của tôi) OK:
Các hằng số liên quan đến không có gì khác; bạn có thể thay đổi các hằng số đó bất cứ khi nào bạn muốn mà không phải thay đổi bất cứ điều gì khác. Ví dụ: Chiều rộng mặc định của cột lưới.
Các hằng số hoàn toàn bất biến, chính xác, rõ ràng, như "số ngày mỗi tuần". days = weeks * 7
Thay thế 7
bằng một hằng số DAYS_PER_WEEK
hầu như không cung cấp bất kỳ giá trị.
Tôi hoàn toàn đồng ý với Jonathan nhưng vì tất cả các quy tắc đều có ngoại lệ ...
"Số ma thuật trong thông số kỹ thuật: Số ma thuật trong mã"
Về cơ bản nói rằng bất kỳ số ma thuật nào còn lại trong thông số kỹ thuật sau những nỗ lực hợp lý để có được bối cảnh mô tả cho chúng nên được phản ánh như vậy trong mã. Nếu số ma thuật vẫn còn trong mã, mọi nỗ lực nên được thực hiện để cô lập chúng và làm cho chúng liên kết rõ ràng với điểm gốc của chúng.
Tôi đã thực hiện một vài hợp đồng giao thoa trong đó cần phải điền các thông điệp với các giá trị được ánh xạ từ cơ sở dữ liệu. Trong hầu hết các trường hợp, ánh xạ khá đơn giản và sẽ phù hợp với các dòng hướng dẫn chung của Jonathan nhưng tôi đã gặp các trường hợp trong đó cấu trúc thông điệp đích chỉ đơn giản là khủng khiếp. Hơn 80% các giá trị phải được truyền lại trong cấu trúc là các hằng số được thi hành theo đặc tả của hệ thống ở xa. điều này cùng với thực tế là cấu trúc thông điệp rất to lớn khiến cho rất nhiều hằng số như vậy phải được đưa vào. Trong hầu hết các trường hợp, họ không cung cấp ý nghĩa hoặc lý do, chỉ nói "đặt M ở đây" hoặc "đặt 4.10.53.10100.889450.4452 tại đây". Tôi đã không cố gắng để đặt một bình luận bên cạnh tất cả chúng, nó sẽ khiến mã kết quả không thể đọc được.
Điều đó nói rằng, khi bạn nghĩ về nó ... nó gần như là tất cả về việc làm cho nó rõ ràng ...
Nếu bạn đang mã hóa giá trị của hằng số hấp dẫn của trái đất, sẽ không có ai quan tâm. Nếu bạn mã hóa địa chỉ IP của máy chủ proxy của mình, bạn sẽ gặp rắc rối.
Chủ yếu là không, nhưng tôi nghĩ rằng đáng chú ý là bạn sẽ gặp nhiều vấn đề nhất khi bạn bắt đầu sao chép giá trị mã hóa cứng. Nếu bạn không sao chép nó (ví dụ: sử dụng nó chỉ một lần trong quá trình thực hiện một lớp) thì việc không sử dụng hằng số có thể ổn.