Bạn nên đặt hằng số ở đâu và tại sao?


34

Trong các ứng dụng chủ yếu lớn của chúng tôi, chúng tôi thường chỉ có một vài vị trí cho "hằng số":

  • Một lớp cho GUI và các yếu tố bên trong (tiêu đề Trang Tab, tiêu đề Hộp nhóm, hệ số tính toán, bảng liệt kê)
  • Một lớp cho các bảng và cột cơ sở dữ liệu (phần này được tạo mã) cộng với các tên có thể đọc được cho chúng (được gán thủ công)
  • Một lớp cho tin nhắn ứng dụng (ghi nhật ký, hộp thông báo, v.v.)

Các hằng số thường được phân tách thành các cấu trúc khác nhau trong các lớp đó. Trong các ứng dụng C ++ của chúng tôi, các hằng số chỉ được xác định trong tệp .h và các giá trị được gán trong tệp .cpp.

Một trong những lợi thế là tất cả các chuỗi, vv đều ở một vị trí trung tâm và mọi người đều biết tìm chúng ở đâu khi phải thay đổi thứ gì đó.

Điều này đặc biệt là thứ mà các nhà quản lý dự án dường như thích khi mọi người đến và đi và bằng cách này mọi người có thể thay đổi những thứ tầm thường như vậy mà không cần phải đào sâu vào cấu trúc của ứng dụng.

Ngoài ra, bạn có thể dễ dàng thay đổi tiêu đề của Hộp nhóm / Trang tab tương tự, v.v. Một khía cạnh khác là bạn chỉ có thể in lớp đó và đưa nó cho một người không phải là lập trình viên, người có thể kiểm tra xem các chú thích có trực quan không, và nếu các tin nhắn cho người dùng quá chi tiết hoặc quá khó hiểu, v.v.

Tuy nhiên, tôi thấy những nhược điểm nhất định:

  • Mỗi lớp duy nhất được liên kết chặt chẽ với các lớp hằng
  • Thêm / Xóa / Đổi tên / Di chuyển một hằng số yêu cầu biên dịch lại ít nhất 90% ứng dụng (Lưu ý: Thay đổi giá trị không, ít nhất là đối với C ++). Trong một trong các dự án C ++ của chúng tôi với 1500 lớp, điều này có nghĩa là khoảng 7 phút thời gian biên dịch (sử dụng các tiêu đề được biên dịch trước; không có chúng khoảng 50 phút) cộng với khoảng 10 phút liên kết với các thư viện tĩnh nhất định.
  • Xây dựng một bản phát hành được tối ưu hóa tốc độ thông qua Trình biên dịch Visual Studio mất tới 3 giờ. Tôi không biết nếu số lượng lớn các mối quan hệ giai cấp là nguồn nhưng nó cũng có thể.
  • Bạn bị điều khiển vào các chuỗi mã hóa tạm thời thẳng vào mã bởi vì bạn muốn kiểm tra một cái gì đó rất nhanh và không muốn đợi 15 phút chỉ cho thử nghiệm đó (và có thể là mọi thứ tiếp theo). Mọi người đều biết chuyện gì xảy ra với "Tôi sẽ sửa nó sau".
  • Việc sử dụng lại một lớp trong một dự án khác không phải lúc nào cũng dễ dàng (chủ yếu là do các khớp nối chặt chẽ khác, nhưng việc xử lý các hằng số không làm cho nó dễ dàng hơn.)

Nơi nào bạn sẽ lưu trữ hằng số như vậy? Ngoài ra những lý lẽ nào bạn sẽ đưa ra để thuyết phục người quản lý dự án của bạn rằng có những khái niệm tốt hơn cũng tuân thủ các lợi thế được liệt kê ở trên?

Hãy đưa ra câu trả lời cụ thể hoặc độc lập cho C ++.

Tái bút: Tôi biết câu hỏi này là loại chủ quan nhưng thực lòng tôi không biết nơi nào tốt hơn trang web này cho loại câu hỏi này.

Cập nhật về dự án này

Tôi có tin tức về điều thời gian biên dịch:
Theo các bài đăng của Caleb và gbjbaanb, tôi chia tệp hằng của tôi thành nhiều tệp khác khi tôi có thời gian. Cuối cùng tôi cũng chia dự án của mình thành nhiều thư viện mà giờ đây có thể dễ dàng hơn nhiều. Biên dịch điều này trong chế độ phát hành cho thấy tệp được tạo tự động chứa các định nghĩa cơ sở dữ liệu (bảng, tên cột và hơn 8000 ký hiệu) và tạo ra các giá trị băm nhất định gây ra thời gian biên dịch lớn trong chế độ phát hành.

Vô hiệu hóa trình tối ưu hóa của MSVC cho thư viện chứa các hằng số DB hiện cho phép chúng tôi giảm tổng thời gian biên dịch Dự án của bạn (một số ứng dụng) trong chế độ phát hành từ tối đa 8 giờ xuống dưới một giờ!

Chúng tôi vẫn chưa tìm ra lý do tại sao MSVC gặp khó khăn trong việc tối ưu hóa các tệp này, nhưng bây giờ thay đổi này làm giảm rất nhiều áp lực vì chúng tôi không còn phải chỉ dựa vào các bản dựng hàng đêm.

Thực tế đó - và các lợi ích khác, chẳng hạn như khớp nối ít chặt chẽ hơn, khả năng tái sử dụng tốt hơn, v.v. - cũng cho thấy rằng việc dành thời gian để chia tách "hằng số" rốt cuộc không phải là một ý tưởng tồi ;-)

Cập nhật2

Vì câu hỏi này vẫn nhận được một số chú ý:
Đây là những gì tôi đã làm trong vài năm qua:

Đặt chính xác mọi hằng số, biến số, v.v. trong phạm vi có liên quan đến nó: Nếu bạn chỉ sử dụng một hằng số trong một phương thức duy nhất, bạn có thể định nghĩa nó trong phương thức đó. Nếu một lớp duy nhất quan tâm đến nó, hãy để nó như một chi tiết triển khai riêng của lớp đó. Điều tương tự áp dụng cho không gian tên, mô-đun, dự án, phạm vi công ty. Tôi cũng sử dụng mô hình tương tự cho các chức năng của người trợ giúp và tương tự. (Điều này có thể không áp dụng 100% nếu bạn phát triển khung công khai.)

Làm điều này làm tăng khả năng sử dụng lại, khả năng kiểm tra và khả năng bảo trì ở một mức độ mà bạn không chỉ mất ít thời gian biên dịch (ít nhất là trong C ++), mà còn mất ít thời gian hơn cho việc sửa lỗi, giúp bạn có nhiều thời gian hơn để phát triển các tính năng mới. Đồng thời, việc phát triển các tính năng này sẽ diễn ra nhanh hơn vì bạn có thể sử dụng lại nhiều mã dễ dàng hơn. Điều này lớn hơn bất kỳ lợi thế nào mà tệp hằng số trung tâm có thể có độ lớn.

Hãy xem đặc biệt là Nguyên tắc phân chia giao diệnNguyên tắc trách nhiệm duy nhất nếu bạn muốn biết thêm.

Nếu bạn đồng ý, hãy đưa ra câu trả lời của Caleb vì bản cập nhật này về cơ bản là một cách tổng quát hơn về những gì anh ấy nói.


2
cá nhân tôi hoàn toàn không có tiêu đề UI hoặc chuỗi tin nhắn. Tôi muốn có chúng trong app.config
jk.

1
Tôi giống như cách bạn đang làm điều đó bây giờ - tôi hiểu nhược điểm của bạn nhưng chúng ta có thể phải đối phó với điều đó.
bigtang

1
Tôi đã thấy chính xác vấn đề tương tự trong một dự án Java lớn ... Một giao diện "hằng số" khổng lồ, thay đổi bất cứ điều gì trong đó và đợi 15 phút để nhật thực biên dịch lại. Tôi với Caleb: nhóm các hằng số nơi chúng thuộc về tự nhiên, gần với mã sử dụng chúng. Giữ chúng tách biệt vì chúng là hằng số OCD vô dụng.
Michael Borgwardt

Khớp nối chặt chẽ không phải là một vấn đề, bởi vì đó là những gì bạn thực sự muốn. Bạn muốn một thay đổi trong tệp hằng của bạn ảnh hưởng đến rất nhiều tệp nguồn. (Tất nhiên bạn cũng có những vấn đề khác).
gnasher729

@ gnasher729 điều đó chỉ đúng nếu rất nhiều lớp sử dụng cùng một hằng số. Bạn sẽ không bao giờ muốn các lớp được liên kết chặt chẽ với các hằng số mà chúng không liên quan. Ban đầu nó có vẻ không phải là vấn đề cho đến khi bạn thử sử dụng lại nó trong một dự án khác mà không sao chép nó hoặc chạy các thử nghiệm độc lập
Tim Meyer

Câu trả lời:


29

Các hằng số dành riêng cho một lớp nên đi trong giao diện của lớp đó.

Các hằng số thực sự là các tùy chọn cấu hình nên là một phần của lớp cấu hình. Nếu bạn cung cấp bộ truy cập cho các tùy chọn cấu hình trong lớp đó (và sử dụng chúng thay cho các hằng số ở nơi khác), bạn sẽ không phải biên dịch lại toàn bộ thế giới khi bạn thay đổi một vài tùy chọn.

Các hằng số được chia sẻ giữa các lớp nhưng không có nghĩa là có thể định cấu hình được nên có phạm vi hợp lý - hãy thử chia chúng thành các tệp có cách sử dụng cụ thể để các lớp riêng lẻ chỉ bao gồm những gì chúng thực sự cần. Điều này một lần nữa sẽ giúp giảm thời gian biên dịch khi bạn thay đổi một số hằng số đó.


1
Trong trường hợp bạn quan tâm: Tôi đã cập nhật câu hỏi của mình với những gì tôi đã đạt được, theo câu trả lời của bạn và những người khác
Tim Meyer

6

Tôi muốn nói một cách đơn giản rằng bạn muốn chia lớp hằng số khổng lồ của mình thành nhiều tệp nhỏ hơn, ví dụ một tệp cho mỗi biểu mẫu. Điều này đảm bảo bạn không có sự phụ thuộc quá lớn vào tệp hằng, do đó, việc thêm hoặc cập nhật chuỗi sẽ không yêu cầu tổng biên dịch lại. Bạn vẫn có thể lưu trữ các tệp này ở một vị trí trung tâm, nhưng (ví dụ) có 1 tệp với các hằng số cho mỗi hộp thoại, được đặt tên phù hợp. Sau đó, bạn chỉ có thể bao gồm các tệp đó trong các tệp hộp thoại có liên quan, giúp cắt giảm biên dịch lại một cách ồ ạt.

Tôi cũng khuyên bạn nên sử dụng một cái gì đó như các tiện ích GNU GetText để xử lý các chuỗi, nó được thiết kế để dịch, nhưng cũng hoạt động tốt chỉ đơn giản là thay đổi văn bản thành một thứ khác. Bạn có thể đặt chúng vào tài nguyên chuỗi, nhưng tôi thấy chúng khó hoạt động hơn khi chúng bị khóa bởi ID, các tiện ích GetText được khóa bởi một chuỗi gốc - làm cho mọi thứ rất dễ phát triển.


Tôi có thể cho nó một shot. Vì lớp hằng với các tiêu đề, vv được chia thành nhiều cấu trúc, tôi có thể có thể thực hiện một lớp trên mỗi cấu trúc như một khởi đầu. Không chắc chắn về điều GNU, chúng tôi thường không muốn thay đổi chuỗi của chúng tôi trong thời gian chạy, chỉ trong thời gian phát triển. Chúng tôi đang sử dụng cơ chế dịch thuật của Qt, tuy nhiên, trong trường hợp chúng tôi phải dịch sang ngôn ngữ khác trong tương lai.
Tim Meyer

Trong trường hợp bạn quan tâm: Tôi đã cập nhật câu hỏi của mình với những gì tôi đã đạt được, theo câu trả lời của bạn và những người khác
Tim Meyer

2

Lưu ý: Tôi không phải là nhà phát triển C ++ ... nhưng đây là suy nghĩ của tôi: Bạn cần xem xét nhận xét của @ jk về sự khác biệt giữa việc sử dụng tệp cấu hình. Trong DotNet, có tệp tài nguyên được sử dụng để lưu trữ thông tin đó. Trong Windows Forms, một tệp tài nguyên được duy trì từ VS cho mỗi biểu mẫu.

Tôi không thấy giá trị cho một hằng số được đặt ngoài phạm vi sử dụng của nó trừ khi đó là hằng số toàn cầu phải được chia sẻ. Như bạn đã đề cập, điều này sẽ khó duy trì trong quá trình phát triển. Ngoài ra, bạn có thể nhận được xung đột tên. Một điều nữa là có thể khó biết ai đang sử dụng một hằng số cho trước.

Bây giờ nếu bạn muốn người không lập trình xem lại thông tin thì đối với GUI, bạn chụp màn hình cho họ. Nếu bạn muốn họ xem lại các mục trong bảng dữ liệu, bạn có thể xuất dữ liệu sang Excel hoặc một cái gì đó tương tự.

Nếu bạn vẫn muốn thực hiện theo cách tiếp cận vị trí tập trung và bạn muốn đặt tất cả các hằng số của mình vào một tệp lớn, mỗi nhà phát triển có thể sử dụng tệp chia sẻ được cập nhật ở cuối mỗi khoảng cho một tệp trung tâm. Dữ liệu sẽ đến từ các tệp riêng lẻ được sử dụng trong quá trình phát triển. Điều này có thể dễ dàng tự động hoặc thực hiện bằng tay. Tuy nhiên, như tôi đã nói, đây có thể là một rủi ro mà bạn không phải chịu.


2

Không có giải pháp chung. Hãy tự hỏi về hiệu suất, khả năng sử dụng, bảo mật và vòng đời của một hằng số.

Càng gần họ được xác định phạm vi của họ, hiệu suất càng cao.

Chúng càng được nhóm hợp lý và nằm ngoài phạm vi của chúng, khả năng tái sử dụng càng cao.

Các chi phí càng ít truy cập, bảo mật càng cao.

Tuổi thọ của một hằng số càng cao, nó càng ít quan tâm đến nơi bạn đặt nó cho tính khả dụng.

Một hằng số như số phiên bản sẽ được xác định trong một số loại bảng kê khai. Mã lỗi của hàm lỗi sẽ được xác định bên trong lớp. Mã lỗi có thể là một cái gì đó có tuổi thọ cao (= hầu như không bao giờ thay đổi). Đặt nó trong một tệp không đổi chỉ spam tệp với những thứ không cần thiết.

Hằng số càng ít có ký tự của hằng số mà là một biến (như số phiên bản), bạn càng có thể đặt nó bên ngoài. Hằng số càng ít biến, vì vậy nó càng không đổi, nó càng được đặt trong phạm vi của nó. Trong quá trình gỡ lỗi, nên đặt nó bên ngoài để giảm thời gian biên dịch.

Tuy nhiên, vấn đề ban đầu của bạn là thời gian biên dịch. Vì vậy, câu hỏi là liệu bạn hỏi đúng câu hỏi. Nếu thời gian biên dịch ứng dụng của bạn quá cao, tốt hơn bạn nên nghĩ ra một cách để làm cho nó trở nên mô đun hơn để các bộ phận hoạt động độc lập với nhau. Một phần biên dịch nó và kiểm tra công cụ của bạn một cách độc lập. Nếu các bài kiểm tra đơn vị của bạn được thực hiện đúng và fullblown (thực sự là rất nhiều công việc), thì bạn có thể dễ dàng thay đổi công cụ xung quanh mà không phải lo lắng về điều đó. Và sau đó câu hỏi nhận được một ổ đĩa hoàn toàn khác.


1

Tôi sẽ đề nghị đặt tất cả các hằng số này vào một số loại tệp cấu hình. Đối với các ứng dụng Java, chúng tôi thường sử dụng các tệp .properations, một văn bản đơn giản với mỗi dòng được định dạng là "(key) = (value)". Thí dụ

MainPanel.Title = Chào mừng bạn đến với ứng dụng của chúng tôi
DB.table.users = TBL_USERS
log.filename = application.log

Sau đó, bạn tải tệp này trong thời gian chạy, điền vào bộ đệm cho phép bạn tra cứu khóa và lấy lại giá trị. Khi bạn cần một hằng bạn truy vấn bộ đệm. Bạn vẫn sẽ cần phải có khóa ở đâu đó và bộ đệm sẽ cần có thể truy cập được trên toàn cầu, nhưng khi bạn thay đổi giá trị thực của các hằng, không cần biên dịch lại, có thể chỉ cần khởi động lại ứng dụng (hoặc nếu bạn thực sự muốn có được sự ưa thích, có nhiều tệp và bộ nhớ cache và cung cấp cho ứng dụng khả năng tải lại bộ đệm trong thời gian chạy).

Để triển khai, tôi đã tìm thấy câu hỏi SO này: https://stackoverflow.com/questions/874052/properIES-file-l Library- for-c-or-c (đây là lần truy cập đầu tiên trên tìm kiếm của Google - Tôi chưa thực sự đã sử dụng phần mềm này cho mình).


Đối với các dự án C ++, chúng ta chỉ cần biên dịch lại tệp hằng nếu chúng ta thay đổi một giá trị. Điều này là do các giá trị được gán trong tệp .cpp. Tuy nhiên, việc thêm / xóa / đổi tên / di chuyển một hằng số vẫn cần phải xây dựng lại hoàn toàn
Tim Meyer

@TimMeyer: Nếu bạn chia các hằng số thành nhiều tệp, việc thêm / xóa hằng số sẽ chỉ ảnh hưởng đến các tệp phụ thuộc vào tệp cụ thể đó, đúng không?
Thất vọngWithFormsDesigner

Chính xác. Vấn đề chính là nếu tôi đề nghị điều đó, mọi người có xu hướng đề cập đến một trong những điều tôi liệt kê là "lợi thế" bị mất
Tim Meyer

Tuy nhiên, tôi thực sự không nghĩ đến việc đặt một số tệp hằng số vào cùng một vị trí
Tim Meyer

+1. @Tim Meyer: Với mục đích này, kiểm tra thời gian biên dịch chi phí nhiều hơn so với tiết kiệm. Bên cạnh đó, bạn sẽ làm gì nếu cần quốc tế hóa?
kevin cline
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.