Mã hóa Soft Soft là gì, thực sự?


87

Trong bài viết này của Alex Papadimoulis, bạn có thể thấy đoạn trích này:

private void attachSupplementalDocuments()
{
  if (stateCode == "AZ" || stateCode == "TX") {

    //SR008-04X/I are always required in these states
    attachDocument("SR008-04X");
    attachDocument("SR008-04XI");
  }

  if (ledgerAmnt >= 500000) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
  }

  if (coInsuredCount >= 5  && orgStatusCode != "CORP") {
    //Non-CORP orgs with 5 or more co-ins require AUTHCNS-1A
    attachDocument("AUTHCNS-1A");
  }
}

Tôi thực sự không hiểu bài viết này.

Tôi trích dẫn:

Nếu mỗi hằng số quy tắc kinh doanh đã được lưu trữ trong một số tập tin cấu hình, cuộc sống sẽ được nhiều [chi tiết ( sic )] khó khăn đối với tất cả mọi người duy trì phần mềm: có muốn được rất nhiều các tập tin mã mà chia sẻ một, tập tin lớn (hoặc, điều ngược lại, toàn bộ rất nhiều tập tin cấu hình nhỏ); triển khai các thay đổi đối với các quy tắc nghiệp vụ không yêu cầu mã mới, nhưng thay đổi thủ công các tệp cấu hình; và gỡ lỗi là khó khăn hơn nhiều.

Đây là một đối số chống lại việc có số nguyên không đổi "500000" trong tệp cấu hình hoặc "AUTHCNS-1A" và các hằng chuỗi khác.

Làm thế nào điều này có thể là một thực hành xấu?

Trong đoạn trích này, "500000" không phải là một con số. Nó không, ví dụ, giống như:

int doubleMe(int a) { return a * 2;}

trong đó 2, là một số không cần phải trừu tượng hóa. Việc sử dụng nó là rõ ràng và nó không đại diện cho một cái gì đó có thể được sử dụng lại sau này.

Ngược lại, "500000" không chỉ đơn giản là một con số. Đó là một giá trị quan trọng, một giá trị đại diện cho ý tưởng về một điểm dừng trong chức năng. Số này có thể được sử dụng ở nhiều nơi, nhưng đó không phải là số bạn đang sử dụng; đó là ý tưởng về giới hạn / đường biên giới, bên dưới quy tắc này được áp dụng và trên quy tắc khác.

Làm thế nào đề cập đến nó từ một tệp cấu hình, hoặc thậm chí là #define, consthoặc bất cứ điều gì ngôn ngữ của bạn cung cấp, tệ hơn bao gồm cả giá trị của nó? Nếu sau này trong chương trình, hoặc một số lập trình viên khác, cũng yêu cầu đường biên đó, để phần mềm đưa ra lựa chọn khác, bạn sẽ bị lừa (vì khi nó thay đổi, không có gì đảm bảo cho bạn rằng nó sẽ thay đổi trong cả hai tệp). Điều đó rõ ràng tồi tệ hơn cho việc gỡ lỗi.

Ngoài ra, nếu ngày mai, chính phủ yêu cầu "Từ 5/3/2050, bạn cần thêm AUTHLDG-122B thay vì AUTHLDG-1A", hằng số chuỗi này không phải là hằng chuỗi đơn giản. Đó là một ý tưởng đại diện cho một ý tưởng; đó chỉ là giá trị hiện tại của ý tưởng đó (đó là "thứ bạn thêm nếu sổ cái trên 500k").

Hãy để tôi làm rõ. Tôi không nói rằng bài báo đó là sai; Tôi chỉ không nhận được nó; có lẽ nó không được giải thích quá rõ (ít nhất là theo suy nghĩ của tôi).

Tôi hiểu rằng việc thay thế mọi giá trị bằng chữ hoặc số có thể bằng một hằng số, định nghĩa hoặc biến cấu hình, không chỉ không cần thiết, mà còn quá phức tạp, nhưng ví dụ cụ thể này dường như không thuộc danh mục này. Làm thế nào để bạn biết rằng bạn sẽ không cần nó sau này? Hoặc người khác cho vấn đề đó?


21
Chơi câu đố: cái gì sẽ là một cái tên hay cho những con số đó? Tôi nghĩ rằng bạn sẽ thấy rằng tên đó không có giá trị gì, hoặc nó mô tả mọi thứ mà mã đã mô tả và thường trong khi thêm sự mơ hồ ("LedgerLimitForAuthDlg1A"?). Tôi tìm thấy bài viết tuyệt vời chính xác bởi vì nó có liên quan như thế nào. Tôi đã duy trì các hệ thống đã sử dụng cả hai cách tiếp cận và tôi có thể nói với bạn rằng các quy tắc kinh doanh thuộc về mã - nó giúp chúng dễ dàng theo dõi, duy trì và hiểu hơn nhiều. Khi bạn sử dụng cấu hình, tốt hơn hết là bạn nên tính - nó đắt hơn nhiều.
Luaan

2
Cấu hình nên được dành riêng cho những thứ cần được cấu hình. Nếu các quy tắc kinh doanh nói chung không thể cấu hình được, thì việc đặt các bit của nó trong cấu hình dù sao cũng không giúp bạn mua được gì.
biziclop

Đối với các ngôn ngữ tiên tiến phù hợp, cấu hình có dạng chương trình con thực tế chứ không phải chuỗi.
Thorbjørn Ravn Andersen

Câu trả lời:


100

Tác giả đang cảnh báo chống trừu tượng sớm.

Dòng này if (ledgerAmt > 500000)trông giống như loại quy tắc kinh doanh mà bạn mong đợi sẽ thấy đối với các sytem kinh doanh phức tạp lớn có yêu cầu cực kỳ phức tạp nhưng chính xác và được ghi chép đầy đủ.

Thông thường những loại yêu cầu đó là trường hợp ngoại lệ / cạnh hơn là logic có thể tái sử dụng hữu ích. Những yêu cầu đó thường được sở hữu và duy trì bởi các nhà phân tích kinh doanh và chuyên gia về chủ đề, thay vì các kỹ sư

(Lưu ý rằng 'quyền sở hữu' các yêu cầu của Chuyên gia / Chuyên gia Kinh doanh trong các trường hợp này thường xảy ra khi các nhà phát triển làm việc trong các lĩnh vực chuyên gia không có đủ chuyên môn về miền; yêu cầu bằng văn bản mơ hồ hoặc kém.)

Khi duy trì các hệ thống có yêu cầu được đóng gói đầy đủ các trường hợp cạnh và logic rất phức tạp, thường không có cách nào để trừu tượng hóa logic đó một cách hữu ích hoặc làm cho nó dễ bảo trì hơn; những nỗ lực để cố gắng xây dựng trừu tượng có thể dễ dàng gây tác dụng ngược - không chỉ dẫn đến lãng phí thời gian mà còn dẫn đến mã ít được bảo trì hơn.

Làm thế nào đề cập đến nó từ một tệp cấu hình, hoặc thậm chí là #define, const hoặc bất cứ thứ gì ngôn ngữ của bạn cung cấp, tệ hơn bao gồm cả giá trị của nó? Nếu sau này trong chương trình, hoặc một số lập trình viên khác, cũng yêu cầu đường biên đó, để phần mềm đưa ra lựa chọn khác, bạn sẽ bị lừa (vì khi nó thay đổi, không có gì đảm bảo cho bạn rằng nó sẽ thay đổi trong cả hai tệp). Điều đó rõ ràng tồi tệ hơn cho việc gỡ lỗi.

Loại mã này có xu hướng được bảo vệ bởi thực tế là chính mã có thể có ánh xạ một-một đến các yêu cầu; tức là khi một nhà phát triển biết rằng 500000con số xuất hiện hai lần trong các yêu cầu, nhà phát triển đó cũng biết rằng nó xuất hiện hai lần trong mã.

Xem xét kịch bản khác (có khả năng như nhau) 500000xuất hiện ở nhiều nơi trong tài liệu yêu cầu, nhưng Chuyên gia về vấn đề chủ đề quyết định chỉ thay đổi một trong số chúng; ở đó bạn có một rủi ro thậm chí còn tồi tệ hơn khi ai đó thay đổi constgiá trị có thể không nhận ra 500000được sử dụng để chỉ những thứ khác nhau - vì vậy nhà phát triển thay đổi nó ở một nơi và chỉ nơi anh ta / cô ấy tìm thấy nó trong mã, và cuối cùng phá vỡ thứ gì đó mà họ không nhận ra họ đã thay đổi.

Kịch bản này xảy ra rất nhiều trong phần mềm pháp lý / tài chính bespoke (ví dụ logic báo giá bảo hiểm) - những người viết các tài liệu đó không phải là kỹ sư và họ không có vấn đề sao chép + dán toàn bộ khối thông số, sửa đổi một vài từ / số, nhưng để lại hầu hết nó như nhau.

Trong các kịch bản đó, cách tốt nhất để xử lý các yêu cầu sao chép-dán là viết mã sao chép-dán và làm cho mã trông giống với các yêu cầu (bao gồm mã hóa cứng tất cả dữ liệu) càng tốt.

Thực tế của các yêu cầu như vậy là chúng thường không được sao chép + dán lâu và các giá trị đôi khi thay đổi thường xuyên, nhưng chúng thường không thay đổi song song, vì vậy cố gắng hợp lý hóa hoặc trừu tượng hóa các yêu cầu đó ra hoặc đơn giản hóa bất kỳ cách nào họ kết thúc việc tạo ra nhiều vấn đề đau đầu về bảo trì hơn là chỉ dịch các yêu cầu nguyên văn thành mã.


28
Ngôn ngữ cụ thể miền (DSL) có thể là một cách tốt để làm cho mã được đọc giống như tài liệu yêu cầu.
Ian

13
Một ưu điểm khác của DSL là cũng khiến việc vô tình trộn lẫn ứng dụng, trình bày hoặc logic bền bỉ với các quy tắc kinh doanh trở nên khó khăn hơn.
Erik Eidt

16
Nghĩ rằng ứng dụng của bạn đủ đặc biệt để đảm bảo DSL của chính nó thường là sự kiêu ngạo.
brian_o

8
Those requirements are typically owned and maintained by business analysts and subject matter experts, rather than by engineersđó không phải lúc nào cũng là một ý tưởng tốt Đôi khi hành động biến các yêu cầu đó thành mã sẽ tiết lộ các trường hợp góc trong đó các yêu cầu không được xác định rõ hoặc được xác định theo cách nó đi ngược lại lợi ích kinh doanh. Nếu các nhà phân tích và phát triển kinh doanh có thể hợp tác để đạt được một mục tiêu chung, thì rất nhiều vấn đề có thể tránh được.
kasperd

4
@BenCottrell Tôi đã đề nghị thay đổi các quy tắc để viết phần mềm dễ dàng hơn. Nhưng khi bạn có nhiều điều kiện trong các quy tắc thì hoàn toàn có thể xảy ra một số tương tác giữa các quy tắc khi xác định quy tắc ở vị trí đầu tiên. Nhưng khi bạn biến đặc tả thành mã, nhà phát triển buộc phải nhận thấy rằng có thể có sự tương tác giữa các điều kiện đó. Tại thời điểm này, có thể nhà phát triển nhận thấy rằng việc giải thích chặt chẽ về đặc tả dẫn đến một mức giá không chủ ý sẽ cho phép khách hàng chơi trò chơi hệ thống.
kasperd

44

Bài viết có một điểm tốt. Làm thế nào có thể là một thực tiễn xấu để trích xuất các hằng số vào một tệp cấu hình? Nó có thể là một thực hành xấu nếu nó làm phức tạp mã không cần thiết. Có một giá trị trực tiếp trong mã đơn giản hơn nhiều so với việc phải đọc nó từ tệp cấu hình và mã như được viết rất dễ thực hiện.

Ngoài ra, ngày mai, chính phủ sẽ "Từ ngày 5/3/2050, bạn cần thêm AUTHLDG-122B thay vì AUTHLDG-1A".

Vâng, sau đó bạn thay đổi mã. Điểm của bài viết là việc thay đổi mã không phức tạp hơn thay đổi tệp cấu hình.

Cách tiếp cận được mô tả trong bài viết không mở rộng nếu bạn có logic phức tạp hơn, nhưng vấn đề là bạn phải thực hiện một cuộc gọi phán xét, và đôi khi giải pháp đơn giản nhất chỉ đơn giản là tốt nhất.

Làm thế nào để bạn biết rằng bạn sẽ không cần nó sau này? Hoặc người khác cho vấn đề đó?

Đây là điểm của nguyên tắc YAGNI. Đừng thiết kế cho một tương lai không xác định có thể tạo ra sự khác biệt hoàn toàn, thiết kế cho hiện tại. Bạn đúng rằng nếu giá trị 500000 được sử dụng ở một số vị trí trong chương trình thì tất nhiên nó phải được trích xuất thành một hằng số. Nhưng đây không phải là trường hợp trong mã trong câu hỏi.

Mã hóa mềm thực sự là một câu hỏi về sự quan tâm . Bạn thông tin mã mềm mà bạn biết có thể thay đổi độc lập với logic ứng dụng cốt lõi. Bạn sẽ không bao giờ mã hóa chuỗi kết nối tới cơ sở dữ liệu, bởi vì bạn biết nó có thể thay đổi độc lập với logic ứng dụng và bạn sẽ cần phân biệt nó cho các môi trường khác nhau. Trong một ứng dụng web, chúng tôi muốn tách logic kinh doanh khỏi các mẫu html và biểu định kiểu, bởi vì chúng có thể thay đổi độc lập và thậm chí được thay đổi bởi những người khác nhau.

Nhưng trong trường hợp trong mẫu mã, các chuỗi và số được mã hóa cứng là một phần không thể thiếu của logic ứng dụng. Có thể hiểu rằng một tệp có thể thay đổi tên do một số thay đổi chính sách nằm ngoài sự kiểm soát của bạn, nhưng có thể hiểu được rằng chúng ta cần thêm một kiểm tra nhánh nhánh mới cho một điều kiện khác. Trích xuất tên tập tin và số thực sự phá vỡ sự gắn kết trong trường hợp này.


4
Thường thì việc thay đổi mã sẽ phức tạp hơn nhiều so với tệp cấu hình. Bạn có thể cần một nhà phát triển và một chu trình phát hành / hệ thống xây dựng cho cái trước, trong khi cái sau chỉ yêu cầu thay đổi một số trong một hộp trong giao diện người dùng cấu hình thân thiện.
OrangeDog

6
@OrangeDog Vâng, đó là cái nhìn đầu tiên. Nhưng nếu bạn làm những việc như thế này, giao diện người dùng cấu hình sẽ là bất cứ điều gì nhưng thân thiện, với hàng trăm hoàn toàn vô nghĩa văn bản hộp yêu cầu bạn cho ai biết được những gì. Và bây giờ bạn phải xây dựng giao diện người dùng và ghi lại nó. Nhắc bạn, điều đó không có nghĩa là cấu hình không bao giờ là một cách tốt để thực hiện - có những trường hợp đó hoàn toàn là lựa chọn đúng đắn. Nhưng không phải trong bất kỳ ví dụ trong bài viết. Và lần cuối cùng một đạo luật chỉ thay đổi con số là khi nào? Lần cuối cùng quy tắc VAT thay đổi ở đây, chúng tôi phải làm lại tất cả các tính toán.
Luaan

2
@OrangeDog: Bạn đang giả sử, ở đây, cấu hình của phần mềm cung cấp cho bạn các móc cần thiết cho kiểm tra bạn cần thực hiện. Lưu ý làm thế nào trong OP mỗi và mọi ifdựa trên một biến khác nhau! Nếu biến bạn cần không thể truy cập được từ cấu hình, bạn vẫn cần sửa đổi phần mềm.
Matthieu M.

2
@OrangeDog vì vậy bạn đang đề xuất rằng cần có những thay đổi đáng kể đối với logic của ứng dụng phần mềm, không có chu kỳ phát hành / qa / phát hành và thử nghiệm phù hợp?
NPSF3000

3
@OrangeDog: OK bạn sử dụng YAML để cấu hình logic trong ví dụ. Vì logic bao gồm các quy tắc có điều kiện, bạn tìm cách thể hiện các điều kiện này trong YAML. Xin chúc mừng, bạn đã phát minh lại Python. Tại sao không viết toàn bộ ứng dụng bằng Python?
JacquesB

26

Bài báo tiếp tục nói về 'Công cụ quy tắc doanh nghiệp' có lẽ là một ví dụ tốt hơn về những gì anh ta đang tranh cãi.

Logic là bạn có thể khái quát hóa đến mức mà cấu hình của bạn trở nên phức tạp đến mức nó chứa ngôn ngữ lập trình riêng của nó.

Ví dụ, mã trạng thái để ánh xạ tài liệu trong ví dụ có thể được chuyển sang tệp cấu hình. Nhưng sau đó bạn sẽ cần thể hiện một mối quan hệ phức tạp.

<statecode id="AZ">
    <document id="SR008-04X"/>
    <document id="SR008-04XI"/>
</statecode>

Có lẽ bạn cũng sẽ đặt số lượng sổ cái vào?

<statecode id="ALL">
    <document id="AUTHLDG-1A" rule="ledgerAmt >= 50000"/>
</statecode>

Chẳng mấy chốc bạn thấy rằng bạn đang lập trình bằng một ngôn ngữ mới mà bạn đã phát minh và lưu mã đó trong các tệp cấu hình không có nguồn hoặc kiểm soát thay đổi.

Cần lưu ý rằng bài viết này là từ năm 2007 khi loại điều này là một cách tiếp cận phổ biến.

Ngày nay chúng ta có thể sẽ giải quyết vấn đề bằng cách tiêm phụ thuộc (DI). Tức là bạn sẽ có một 'mã hóa cứng'

InvoiceRules_America2007 : InvoiceRules

mà bạn sẽ thay thế bằng một mã hóa cứng, hoặc cấu hình nhiều hơn

InvoiceRules_America2008 : InvoiceRules

khi luật pháp hoặc yêu cầu kinh doanh thay đổi.


4
Có lẽ bạn nên định nghĩa "DI". Và có thể giải thích thêm một chút.
Basil Bourque

9
Tại sao tập tin đó không có trong hệ thống kiểm soát nguồn?
JDługosz

2
Nếu là ứng dụng khách cụ thể, phiên bản được mã hóa có một mớ ifcâu lệnh khổng lồ để đưa ra các giá trị khác nhau cho mỗi khách hàng không? Nghe có vẻ như là một cái gì đó nên có trong một tập tin cấu hình. Ở trong một loại tệp này hay loại khác, tất cả các loại khác đều bằng nhau, không phải là lý do để không kiểm soát / theo dõi / sao lưu tệp. @ewan dường như đang nói rằng tệp của DSL không thể được lưu như một phần của dự án vì một số lý do, ngay cả khi các tài sản không phải mã như hình ảnh và tệp âm thanh và tài liệu chắc chắn .
JDługosz

2
Bạn thực sự nên cấu trúc lại giá trị "50000" ra khỏi XML của mình và đặt nó vào một tệp cấu hình riêng biệt, bạn nghĩ sao? ... và nhân tiện, nó được cho là 500000.
tự đại diện

1
@jdlugosz khái niệm về ERE là bạn mua hệ thống và sau đó định cấu hình nó cho nhu cầu của bạn. có lẽ bởi vì các nhà phát triển nội bộ đã cạnh tranh với các hệ thống 'linh hoạt' này nên họ sẽ cố gắng mô phỏng chúng. thay đổi quyền kiểm soát cấu hình, ngay cả trong các hệ thống từ các công ty lớn như IBM thường là một suy nghĩ lại. Điểm bán đã thay đổi nhanh chóng
Ewan

17

Ngược lại, "500000" không chỉ đơn giản là một con số. Đó là một giá trị quan trọng, một giá trị đại diện cho ý tưởng về một điểm dừng trong chức năng. Số này có thể được sử dụng ở nhiều nơi, nhưng đó không phải là số bạn đang sử dụng, đó là ý tưởng về giới hạn / đường biên giới, bên dưới quy tắc này được áp dụng và trên quy tắc khác.

Và điều đó được thể hiện bằng cách có (và tôi có thể lập luận rằng ngay cả bình luận là dư thừa):

 if (ledgerAmnt >= 500000) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
  }

Đây chỉ là lặp lại những gì mã đang làm:

LEDGER_AMOUNT_REQUIRING_AUTHLDG1A=500000
if (ledgerAmnt >= LEDGER_AMOUNT_REQUIRING_AUTHLDG1A) {
    //Ledger of 500K or more requires AUTHLDG-1A
    attachDocument("AUTHLDG-1A");
}

Lưu ý rằng tác giả giả định rằng ý nghĩa của 500000 được gắn với quy tắc này; nó không phải là một giá trị đang hoặc có khả năng được sử dụng lại ở nơi khác:

Thay đổi quy tắc kinh doanh một và duy nhất mà Mã hóa mềm trước đó có thể giải thích là thay đổi số lượng sổ cái yêu cầu mẫu AUTHLDG-1A. Bất kỳ thay đổi quy tắc kinh doanh nào khác sẽ yêu cầu nhiều công việc hơn - cấu hình, tài liệu, mã, v.v.

Quan điểm chính của bài viết, theo quan điểm của tôi, là đôi khi một con số chỉ là một con số: nó không có ý nghĩa gì khác ngoài những gì được truyền tải trong mã và nó không có khả năng được sử dụng ở nơi khác. Do đó, việc lúng túng tóm tắt những gì mã đang làm (bây giờ) trong một tên biến chỉ nhằm mục đích tránh các giá trị được mã hóa cứng là sự lặp lại không cần thiết tốt nhất.


2
Nếu bạn giới thiệu hằng LEDGER_AMOUNT_REQUIRING_AUTHLDG1A, bạn sẽ không viết bình luận vào mã nữa. Nhận xét không được duy trì tốt bởi các lập trình viên. Nếu số tiền từng thay đổi, ifđiều kiện và nhận xét sẽ không đồng bộ. Trái lại, hằng số LEDGER_AMOUNT_REQUIRING_AUTHLDG1Akhông bao giờ không đồng bộ với chính nó và nó giải thích mục đích của nó mà không cần bình luận.
ZeroOne

2
@ZeroOne: Ngoại trừ nếu quy tắc kinh doanh thay đổi thành "Sổ cái từ 500K trở lên yêu cầu AUTHLDG-1A và AUTHLDG-2B", rất có thể người thêm attachDocument("AUTHLDG-2B");dòng sẽ không cập nhật tên liên tục cùng một lúc. Trong trường hợp này, tôi nghĩ rằng mã này khá rõ ràng, không có bình luận hay biến giải thích. (Mặc dù nó có thể làm cho tinh thần để có một quy ước của chỉ phần thích hợp của các tài liệu yêu cầu kinh doanh thông qua mã comments Theo một quy ước như vậy, một lời nhận xét mã nào. Rằng sẽ thích hợp ở đây.)
ruakh

@ruakh, OK, sau đó tôi sẽ cấu trúc lại hằng số được gọi LEDGER_AMOUNT_REQUIRING_ADDITIONAL_DOCUMENTS(điều mà có lẽ tôi nên thực hiện ở vị trí đầu tiên). Tôi cũng có thói quen đặt ID yêu cầu nghiệp vụ vào các thông điệp cam kết Git, chứ không phải vào mã nguồn.
ZeroOne

1
@ZeroOne: Nhưng đối với AUTHLDG-3C, số lượng sổ cái thực sự là tối đa . Và đối với AUTHLDG-4D, số lượng sổ cái phù hợp tùy thuộc vào tiểu bang. (Bạn đã hiểu rõ chưa? Đối với loại mã này, bạn muốn mã của mình phản ánh các quy tắc kinh doanh, chứ không phải một số cố gắng trừu tượng hóa các quy tắc kinh doanh, bởi vì không có lý do gì để mong đợi sự phát triển của các quy tắc kinh doanh phù hợp với trừu tượng bạn đã áp dụng.)
ruakh

2
Cá nhân, tôi không phản đối việc đưa số ma thuật vào mã, tôi phản đối việc cấu trúc mã để nó cần những bình luận này. Nếu là tôi, tôi sẽ biến mỗi tài liệu thành một ví dụ enum với attachIfNecessary()phương thức riêng của nó và chỉ lặp qua tất cả chúng.
David Moles

8

Các câu trả lời khác là chính xác, và chu đáo. Nhưng đây là câu trả lời ngắn gọn và ngọt ngào của tôi.

  Rule/value          |      At Runtime, rule/value…
  appears in code:    |   …Is fixed          …Changes
----------------------|------------------------------------
                      |                 |
  Once                |   Hard-code     |   Externalize
                      |                 |   (soft-code)
                      |                 |
                      |------------------------------------
                      |                 |
  More than once      |   Soft-code     |   Externalize
                      |   (internal)    |   (soft-code)
                      |                 |
                      |------------------------------------

Nếu các quy tắc và giá trị đặc biệt xuất hiện ở một nơi trong mã và không thay đổi trong thời gian chạy, thì mã cứng như trong câu hỏi.

Nếu các quy tắc hoặc giá trị đặc biệt xuất hiện ở nhiều nơi trong mã và không thay đổi trong thời gian chạy, thì mã mềm. Mã hóa mềm cho một quy tắc có thể giúp tôi xác định một lớp / phương thức cụ thể hoặc sử dụng mẫu Builder . Đối với các giá trị, mã hóa mềm có thể có nghĩa là xác định một hằng số hoặc enum cho giá trị được sử dụng trên mã của bạn.

Nếu các quy tắc hoặc giá trị đặc biệt có thể thay đổi trong thời gian chạy, thì bạn phải xuất ngoại chúng. Nó thường được thực hiện bằng cách cập nhật các giá trị trong cơ sở dữ liệu. Hoặc cập nhật giá trị trong bộ nhớ theo cách thủ công của người dùng nhập dữ liệu. Nó cũng được thực hiện bằng cách lưu trữ các giá trị trong một tệp văn bản (XML, JSON, văn bản thuần túy, bất cứ thứ gì) được quét liên tục để thay đổi ngày giờ thay đổi tệp.


1
Tôi thích câu trả lời của bạn, nhưng tôi nghĩ bạn cũng nên xem xét liệu nó có thay đổi khi thực hiện hay không. Điều này chủ yếu có liên quan nếu sản phẩm đó là sản phẩm sẽ được sử dụng trong nhiều tổ chức, ví dụ, có thể có các quy tắc khác nhau về việc người giám sát có cần phê duyệt hoàn tiền cho X hay không, v.v.
Bloke Down The Pub

Đồng ý cả với câu trả lời này và nhận xét về việc thực hiện. Những thứ tôi làm việc được thực hiện bởi nhiều tổ chức và nhiều trong số chúng có các giá trị khác nhau cần thiết. Chúng tôi có xu hướng lưu trữ các 'cài đặt' này trong cơ sở dữ liệu thay vì tệp cấu hình, nhưng nguyên tắc là chúng tôi không muốn tạo các bản dựng phần mềm khác nhau cho mỗi công ty thực hiện nó (sau đó lặp lại các bản dựng khác nhau đó mỗi lần họ nâng cấp) .
RosieC

7

Đây là cái bẫy chúng ta rơi vào khi chúng ta sử dụng một vấn đề đồ chơi và sau đó chỉ đưa ra các giải pháp ống hút , khi chúng ta đang cố gắng minh họa một vấn đề thực sự.

Trong ví dụ đã cho, nó không tạo ra sự khác biệt nào cho dù các giá trị được cung cấp được mã hóa cứng dưới dạng giá trị nội tuyến hay được định nghĩa là hằng số.

Đó là mã xung quanh sẽ làm cho ví dụ trở thành nỗi kinh hoàng về bảo trì và mã hóa. Nếu có không có mã xung quanh, sau đó đoạn là tốt, ít nhất là trong một môi trường refactoring liên tục. Trong một môi trường mà việc tái cấu trúc có xu hướng không xảy ra, những người duy trì mã đó đã chết, vì những lý do sẽ sớm trở nên rõ ràng.

Xem, nếu có mã xung quanh nó, thì những điều xấu rõ ràng xảy ra.

Điều tồi tệ đầu tiên là giá trị 50000 được sử dụng cho một giá trị khác ở đâu đó, giả sử, số tiền sổ cái mà thuế suất thay đổi ở một số bang ... sau đó khi thay đổi xảy ra, người bảo trì không có cách nào biết được, khi anh ta tìm thấy hai trường hợp 50000 trong mã, cho dù chúng có nghĩa là 50k giống nhau hoặc 50ks hoàn toàn không liên quan. Và bạn cũng nên tìm kiếm 49999 và 50001, trong trường hợp ai đó cũng sử dụng chúng như hằng số? Đây không phải là một cuộc gọi để plonk các biến đó trong một tệp cấu hình của một dịch vụ riêng biệt: nhưng mã hóa chúng nội tuyến rõ ràng cũng sai. Thay vào đó, chúng phải là hằng số, được định nghĩa và nằm trong phạm vi lớp hoặc tệp mà chúng được sử dụng. Nếu hai trường hợp 50k sử dụng cùng một hằng số, thì chúng có thể đại diện cho cùng một hạn chế lập pháp; nếu không, có lẽ họ không làm thế; và bằng cách nào đó, họ sẽ có một cái tên,

Tên tệp đang được chuyển đến một hàm - Đính kèm () - chấp nhận tên tệp cơ sở dưới dạng chuỗi, không có đường dẫn hoặc phần mở rộng. Tên tệp về cơ bản là các khóa ngoại đối với một số hệ thống tệp hoặc cơ sở dữ liệu hoặc bất cứ nơi nào Đính kèm tệp () lấy các tệp từ đó. Nhưng các chuỗi không cho bạn biết gì về điều này - có bao nhiêu tệp? Những loại tập tin họ là? Làm thế nào để bạn biết, khi mở vào một thị trường mới, liệu bạn có cần cập nhật chức năng này không? Những loại điều họ có thể được đính kèm? Người bảo trì bị bỏ lại hoàn toàn trong bóng tối và tất cả những gì anh ta có là một chuỗi, có thể xuất hiện nhiều lần trong mã và có nghĩa là những thứ khác nhau mỗi khi nó xuất hiện. Ở một nơi, "SR008-04X" là một mã gian lận. Mặt khác, đó là một lệnh để đặt mua bốn tên lửa đẩy SR008. Đây này' tên tập tin? Những thứ này có liên quan không? Ai đó vừa thay đổi chức năng đó để đề cập đến một tệp khác, "CLIENT". Sau đó, bạn, người bảo trì kém, đã được thông báo rằng tệp "KHÁCH HÀNG" cần được đổi tên thành "KHÁCH HÀNG". Nhưng chuỗi "CLIENT" xuất hiện 937 lần trong mã ... bạn thậm chí bắt đầu tìm kiếm ở đâu?

Các vấn đề đồ chơi là các giá trị đều không bình thường và có thể được đảm bảo một cách hợp lý là duy nhất trong các mã. Không phải "1" hay "10" mà là "50.000". Không phải "khách hàng" hay "báo cáo" mà là "SR008-04X".

Người rơm là cách duy nhất khác để giải quyết vấn đề của các hằng số mờ đục không thể chấp nhận được là đưa chúng vào tập tin cấu hình của một số dịch vụ không liên quan.

Cùng nhau, bạn có thể sử dụng hai ngụy biện này để chứng minh bất kỳ đối số nào là đúng.


2
Không phải là một vấn đề đồ chơi, không phải là một người rơm. Đây là thứ bạn sẽ thấy mọi lúc trong các loại ứng dụng kinh doanh này. Không có "mở cửa vào một thị trường mới", không có việc sử dụng lại cùng một con số (rốt cuộc, điều đó sẽ mang lại ý nghĩa khác) và trong mọi trường hợp, bài báo không nói gì với DRY - nếu có hai phụ thuộc vào giá trị, thì nó sẽ được chuyển vào một phương thức hoặc một hằng số. Hiển thị các ví dụ về cách các hằng số đó (của cài đặt cấu hình, điều đó không thực sự quan trọng) nên được đặt tên và nơi chúng nên được lưu trữ theo cách chứng minh trong tương lai và rõ ràng hơn mã.
Luaan

4
Ví dụ này không bị hỏng vì đó là vấn đề về đồ chơi. Các mã xung quanh sẽ luôn luôn khủng khiếp vì các quy tắc kinh doanh mà phần mềm phải thực hiện là kinh dị . Nỗ lực đẩy mạnh thách thức cơ bản này với các công cụ quy tắc và DSL và thường không phải là sự trì hoãn của lập trình viên , bởi vì giải quyết các vấn đề CS thú vị hơn là giải quyết các vấn đề phức tạp của các hình thức thuế. Nỗ lực để đạt được "sự thanh lịch" thường là những việc lặt vặt vì nhiệm vụ cuối cùng của phần mềm là mô hình hóa một thảm họa phức tạp.
whatsisname

Các quy tắc kinh doanh có thể là kinh dị, nhưng bản thân nó không phải là một cái cớ để viết loại mã thủ tục tầm thường này. (Tôi có xu hướng đồng ý với Papadimoulis rằng nó dễ dàng hơn để mô hình hóa và duy trì các quy tắc trong mã hơn trong cấu hình, tôi chỉ nghĩ rằng nó phải được mã tốt hơn.) Vấn đề DRY tôi thấy không phải là những con số kỳ diệu, nó lặp đi lặp lại if (...) { attachDocument(...); }.
David Moles

2

Có một số vấn đề trong việc này.

Một vấn đề là thời tiết nên xây dựng một công cụ quy tắc để làm cho tất cả các quy tắc có thể dễ dàng cấu hình bên ngoài chương trình. Câu trả lời trong các trường hợp tương tự như vậy thường là không. Các quy tắc sẽ được thay đổi theo những cách kỳ lạ khó dự đoán, điều đó có nghĩa là công cụ quy tắc phải được mở rộng bất cứ khi nào có thay đổi.

Một vấn đề khác là làm thế nào để xử lý các quy tắc này và những thay đổi của chúng trong kiểm soát phiên bản của bạn. Giải pháp tốt nhất ở đây là chia các quy tắc thành một lớp cho mỗi quy tắc.

Điều đó cho phép mỗi quy tắc có hiệu lực riêng, một số quy tắc thay đổi mỗi năm, một số thay đổi tùy thuộc vào thời điểm giấy phép được cấp hoặc hóa đơn được cấp. Bản thân quy tắc chứa kiểm tra xem phiên bản nào phải áp dụng.

Ngoài ra, hằng số là riêng tư, nó không thể bị sử dụng sai ở bất kỳ nơi nào khác trong mã.

Sau đó có một danh sách tất cả các quy tắc và áp dụng danh sách.

Một vấn đề nữa là làm thế nào để xử lý các hằng số. 500000 có thể trông không rõ ràng nhưng phải được chăm sóc rất cẩn thận để đảm bảo nó được chuyển đổi chính xác. Nếu bất kỳ số học dấu phẩy động nào được áp dụng, nó có thể được chuyển đổi thành 500,000.00001, do đó, việc so sánh với 500,000.00000 có thể thất bại. Hoặc thậm chí tệ hơn 500000 luôn hoạt động như dự định, nhưng bằng cách nào đó, 565000 không thành công khi chuyển đổi. Hãy chắc chắn rằng việc chuyển đổi là rõ ràng và được thực hiện bởi bạn không phải do trình biên dịch đoán. Thường thì điều này được thực hiện bằng cách chuyển đổi nó thành một số BigInteger hoặc BigDecimal trước khi nó được sử dụng.


2

Mặc dù nó không được đề cập trực tiếp trong câu hỏi, tôi muốn lưu ý rằng điều quan trọng không phải là chôn vùi logic kinh doanh trong mã.

Mã, giống như ví dụ ở trên, mã hóa các yêu cầu nghiệp vụ được chỉ định bên ngoài phải thực sự sống trong một phần riêng biệt của cây nguồn, có thể được đặt tên businesslogichoặc một cái gì đó tương tự, và cần chú ý để đảm bảo rằng nó chỉ mã hóa các yêu cầu kinh doanh một cách đơn giản, dễ đọc và chính xác nhất có thể, với tối thiểu nồi hơi và với ý kiến ​​rõ ràng và nhiều thông tin.

Nó nên không được trộn lẫn với "cơ sở hạ tầng" mã mà thực hiện các chức năng cần thiết để thực hiện các logic kinh doanh, chẳng hạn như, nói, việc thực hiện các attachDocument()phương pháp trong ví dụ này, hoặc ví dụ như giao diện người dùng, khai thác gỗ hoặc mã cơ sở dữ liệu nói chung. Mặc dù một cách để thực thi sự tách biệt này là "mã mềm" tất cả logic nghiệp vụ trong một tệp cấu hình, nhưng điều này khác xa với phương thức duy nhất (hoặc tốt nhất).

Mã logic kinh doanh như vậy cũng nên được viết đủ rõ ràng rằng, nếu bạn đưa nó cho một chuyên gia trong lĩnh vực kinh doanh không có kỹ năng mã hóa, họ sẽ có thể hiểu ý nghĩa của nó. Ít nhất, nếu và khi các yêu cầu nghiệp vụ thay đổi, mã mã hóa chúng phải đủ rõ ràng để ngay cả một lập trình viên mới không có gia đình trước với codebase cũng có thể dễ dàng định vị, xem xét và cập nhật logic nghiệp vụ, giả sử rằng không có chức năng mới định tính được yêu cầu.

Lý tưởng nhất, mã như vậy cũng sẽ được viết bằng ngôn ngữ dành riêng cho tên miền để thực thi sự tách biệt giữa logic kinh doanh và cơ sở hạ tầng bên dưới, nhưng điều đó có thể phức tạp không cần thiết đối với một ứng dụng nội bộ cơ bản. Điều đó nói rằng, nếu bạn ví dụ bán phần mềm cho nhiều khách hàng, mỗi khách hàng cần một bộ quy tắc kinh doanh tùy chỉnh của riêng họ, thì một ngôn ngữ kịch bản dành riêng cho tên miền đơn giản (có lẽ dựa trên hộp cát Lua ) có thể chỉ là một thứ.


Đây chính xác là những gì tôi đã nghĩ !!! Khi logic được chôn sâu trong mã, làm thế nào một chuyên gia về lĩnh vực / chủ đề hoặc người dùng doanh nghiệp có thể thấy các giá trị và logic được sử dụng để đảm bảo chúng đúng và chẩn đoán hành vi của hệ thống? Một điều mà một tập tin cấu hình làm là làm cho các cài đặt hiển thị . Phải có một số phương tiện để thúc đẩy khả năng hiển thị của các quy tắc kinh doanh - ngay cả khi điều đó làm cho việc mã hóa trở nên "khó khăn" hơn. Tôi có thể chấp nhận một lớp mỏng hoặc tập hợp các lớp thực hiện công việc, mà không trộn lẫn vào các mối quan tâm khác - miễn là người dùng doanh nghiệp có phương tiện để truy cập và hiểu chúng.
ErikE
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.