Khả năng đọc so với khả năng duy trì, trường hợp đặc biệt viết các lệnh gọi hàm lồng nhau


57

Kiểu mã hóa của tôi cho các lệnh gọi hàm lồng nhau như sau:

var result_h1 = H1(b1);
var result_h2 = H2(b2);
var result_g1 = G1(result_h1, result_h2);
var result_g2 = G2(c1);
var a = F(result_g1, result_g2);

Gần đây tôi đã thay đổi thành một bộ phận mà phong cách mã hóa sau đây được sử dụng rất nhiều:

var a = F(G1(H1(b1), H2(b2)), G2(c1));

Kết quả của cách mã hóa của tôi là, trong trường hợp chức năng bị sập, Visual Studio có thể mở kết xuất tương ứng và chỉ ra dòng xảy ra sự cố (tôi đặc biệt lo ngại về vi phạm truy cập).

Tôi sợ rằng, trong trường hợp xảy ra sự cố do cùng một vấn đề được lập trình theo cách đầu tiên, tôi sẽ không thể biết chức năng nào đã gây ra sự cố.

Mặt khác, bạn càng xử lý nhiều dòng trên một dòng, bạn càng nhận được nhiều logic trên một trang, điều này giúp tăng cường khả năng đọc.

Là nỗi sợ của tôi là chính xác hay tôi đang thiếu một cái gì đó, và nói chung, được ưa thích trong một môi trường thương mại? Khả năng đọc hay bảo trì?

Tôi không biết nó có liên quan không, nhưng chúng tôi đang làm việc trong C ++ (STL) / C #.


17
@gnat: bạn đề cập đến một câu hỏi chung, trong khi tôi đặc biệt quan tâm đến trường hợp được đề cập của các lệnh gọi hàm lồng nhau và hậu quả trong trường hợp phân tích kết xuất sự cố, nhưng nhờ liên kết, nó chứa khá nhiều thông tin thú vị.
Dominique

9
Lưu ý rằng nếu ví dụ này được áp dụng cho C ++ (như bạn đề cập đến việc này đang được sử dụng trong dự án của bạn) thì đây không chỉ là một câu hỏi về phong cách, vì thứ tự đánh giá HXvà các GXyêu cầu có thể thay đổi trong một lớp lót, như thứ tự đánh giá các đối số chức năng là không xác định. Nếu bạn vì một lý do nào đó phụ thuộc vào thứ tự các tác dụng phụ (vô tình hay hữu ý) trong các yêu cầu, thì việc tái cấu trúc theo phong cách này có thể ảnh hưởng nhiều hơn là chỉ đọc / bảo trì.
dfri

4
Là tên biến result_g1mà bạn thực sự sử dụng hoặc giá trị này thực sự đại diện cho một cái gì đó với một tên hợp lý; ví dụ percentageIncreasePerSecond. Đó thực sự sẽ là thử nghiệm của tôi để quyết định giữa hai người
Richard Tingle

3
Bất kể cảm xúc của bạn về phong cách mã hóa là gì, bạn nên tuân theo quy ước đã có sẵn trừ khi nó sai rõ ràng (có vẻ như không phải trong trường hợp này).
n00b

4
@ t3chb0t Bạn có quyền bỏ phiếu theo cách bạn thích, nhưng vui lòng lưu ý đến việc khuyến khích các câu hỏi chủ đề hay, hữu ích trên trang web này (và không khuyến khích những câu hỏi xấu), rằng mục đích của việc bỏ phiếu hoặc bỏ phiếu là để cho biết liệu một câu hỏi có hữu ích và rõ ràng hay không, vì vậy bỏ phiếu cho các lý do khác như sử dụng phiếu bầu làm phương tiện để đưa ra lời chỉ trích về một số mã ví dụ được đăng để hỗ trợ bối cảnh của câu hỏi thường không hữu ích trong việc duy trì chất lượng của trang web : softwareengineering.stackexchange.com/help/privileges/vote-down
Ben Cottrell

Câu trả lời:


111

Nếu bạn cảm thấy bắt buộc phải mở rộng một lớp lót như

 a = F(G1(H1(b1), H2(b2)), G2(c1));

Tôi sẽ không trách bạn Điều đó không chỉ khó đọc, mà còn khó gỡ lỗi.

Tại sao?

  1. Nó dày đặc
  2. Một số trình gỡ lỗi sẽ chỉ làm nổi bật toàn bộ sự việc cùng một lúc
  3. Nó không có tên mô tả

Nếu bạn mở rộng nó với kết quả trung gian, bạn nhận được

 var result_h1 = H1(b1);
 var result_h2 = H2(b2);
 var result_g1 = G1(result_h1, result_h2);
 var result_g2 = G2(c1);
 var a = F(result_g1, result_g2);

và nó vẫn khó đọc. Tại sao? Nó giải quyết hai trong số các vấn đề và giới thiệu thứ tư:

  1. Nó dày đặc
  2. Một số trình gỡ lỗi sẽ chỉ làm nổi bật toàn bộ sự việc cùng một lúc
  3. Nó không có tên mô tả
  4. Nó lộn xộn với những cái tên không mô tả

Nếu bạn mở rộng nó với các tên thêm ý nghĩa mới, tốt, ngữ nghĩa, thậm chí tốt hơn! Một cái tên hay giúp tôi hiểu.

 var temperature = H1(b1);
 var humidity = H2(b2);
 var precipitation = G1(temperature, humidity);
 var dewPoint = G2(c1);
 var forecast = F(precipitation, dewPoint);

Bây giờ ít nhất điều này kể một câu chuyện. Nó sửa chữa các vấn đề và rõ ràng tốt hơn bất cứ điều gì khác được cung cấp ở đây nhưng nó đòi hỏi bạn phải đưa ra các tên.

Nếu bạn làm điều đó với những cái tên vô nghĩa như result_thisresult_thatvì đơn giản là bạn không thể nghĩ ra những cái tên hay thì tôi thực sự muốn bạn dành cho chúng tôi sự lộn xộn của cái tên vô nghĩa và mở rộng nó bằng cách sử dụng một khoảng trắng cũ tốt:

int a = 
    F(
        G1(
            H1(b1), 
            H2(b2)
        ), 
        G2(c1)
    )
;

Nó chỉ có thể đọc được, nếu không phải như vậy, so với tên có kết quả vô nghĩa (không phải các tên hàm này là tuyệt vời).

  1. Nó dày đặc
  2. Một số trình gỡ lỗi sẽ chỉ làm nổi bật toàn bộ sự việc cùng một lúc
  3. Nó không có tên mô tả
  4. Nó lộn xộn với những cái tên không mô tả

Khi bạn không thể nghĩ ra những cái tên hay, điều đó cũng tốt như vậy.

Vì một số lý do, trình gỡ lỗi yêu thích các dòng mới, vì vậy bạn nên thấy rằng việc gỡ lỗi này không khó:

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

Nếu điều đó là không đủ, hãy tưởng tượng G2()được gọi ở nhiều nơi và sau đó điều này đã xảy ra:

Exception in thread "main" java.lang.NullPointerException
    at composition.Example.G2(Example.java:34)
    at composition.Example.main(Example.java:18)

Tôi nghĩ thật tuyệt vì mỗi G2()cuộc gọi sẽ nằm trên đường dây riêng, nên phong cách này sẽ đưa bạn trực tiếp đến cuộc gọi vi phạm chính.

Vì vậy, vui lòng không sử dụng các vấn đề 1 và 2 như một cái cớ để gắn bó với chúng tôi với vấn đề 4. Sử dụng tên hay khi bạn có thể nghĩ về chúng. Tránh những cái tên vô nghĩa khi bạn không thể.

Lightness Races trong bình luận của Orbit chỉ ra một cách chính xác rằng các chức năng này là nhân tạo và đã có những cái tên đáng thương. Vì vậy, đây là một ví dụ về việc áp dụng phong cách này cho một số mã từ tự nhiên:

var user = db.t_ST_User.Where(_user => string.Compare(domain,  
_user.domainName.Trim(), StringComparison.OrdinalIgnoreCase) == 0)
.Where(_user => string.Compare(samAccountName, _user.samAccountName.Trim(), 
StringComparison.OrdinalIgnoreCase) == 0).Where(_user => _user.deleted == false)
.FirstOrDefault();

Tôi ghét nhìn vào dòng tiếng ồn đó, ngay cả khi không cần gói từ. Đây là cách nó trông theo phong cách này:

var user = db
    .t_ST_User
    .Where(
        _user => string.Compare(
            domain, 
            _user.domainName.Trim(), 
            StringComparison.OrdinalIgnoreCase
        ) == 0
    )
    .Where(
        _user => string.Compare(
            samAccountName, 
            _user.samAccountName.Trim(), 
            StringComparison.OrdinalIgnoreCase
        ) == 0
    )
    .Where(_user => _user.deleted == false)
    .FirstOrDefault()
;

Như bạn có thể thấy, tôi đã thấy phong cách này hoạt động tốt với mã chức năng di chuyển vào không gian hướng đối tượng. Nếu bạn có thể đưa ra những cái tên hay để làm điều đó theo phong cách trung gian thì sẽ tiếp thêm sức mạnh cho bạn. Cho đến lúc đó tôi đang sử dụng cái này. Nhưng trong mọi trường hợp, xin vui lòng, tìm cách nào đó để tránh tên kết quả vô nghĩa. Chúng làm mắt tôi đau.


20
@Steve và tôi không nói với bạn là không. Tôi đang cầu xin một cái tên có ý nghĩa. Tôi thường thấy phong cách trung gian được thực hiện một cách không suy nghĩ. Tên xấu đốt cháy bộ não của tôi nhiều hơn so với mã thưa thớt trên mỗi dòng. Tôi không để những cân nhắc về chiều rộng hoặc chiều dài thúc đẩy tôi làm cho mã của mình dày đặc hoặc tên của tôi ngắn lại. Tôi để họ thúc đẩy tôi phân hủy nhiều hơn. Nếu những cái tên tốt sẽ không xảy ra, hãy cân nhắc công việc này để tránh tiếng ồn vô nghĩa.
candied_orange

6
Tôi thêm vào bài viết của bạn: Tôi đã có một quy tắc nhỏ: Nếu bạn không thể đặt tên cho nó, đó có thể là một dấu hiệu cho thấy nó không được xác định rõ. Tôi sử dụng nó trên các thực thể, thuộc tính, biến, mô-đun, menu, lớp trình trợ giúp, phương thức, v.v. Trong nhiều tình huống, quy tắc nhỏ này đã tiết lộ một lỗ hổng nghiêm trọng trong thiết kế. Vì vậy, theo một cách nào đó, việc đặt tên tốt không chỉ góp phần vào khả năng đọc và bảo trì, nó giúp bạn xác minh thiết kế. Tất nhiên có những ngoại lệ cho mọi quy tắc đơn giản.
Alireza

4
Phiên bản mở rộng trông xấu xí. Có quá nhiều khoảng trắng ở đó làm giảm sseinceeverythingisem phasizedwithit, có nghĩa là nhẹ nhàng.
Mateen Ulhaq

5
@MateenUlhaq Khoảng trắng thêm duy nhất có một vài dòng mới và một số vết lõm, và tất cả đều được đặt cẩn thận ở các ranh giới có ý nghĩa . Nhận xét của bạn thay vì đặt khoảng trắng ở ranh giới không có ý nghĩa. Tôi đề nghị bạn nên nhìn gần hơn và cởi mở hơn.
jpmc26

3
Không giống như @MateenUlhaq Tôi ở hàng rào về khoảng trắng trong ví dụ cụ thể này với các tên hàm như vậy, nhưng với các tên hàm thực (có độ dài hơn hai ký tự, phải không?) Đó có thể là điều tôi muốn làm.
Cuộc đua nhẹ nhàng với Monica

50

Mặt khác, bạn càng xử lý nhiều dòng trên một dòng, bạn càng nhận được nhiều logic trên một trang, điều này giúp tăng cường khả năng đọc.

Tôi hoàn toàn không đồng ý với điều này. Chỉ cần nhìn vào hai ví dụ mã của bạn gọi điều này là không chính xác:

var a = F(G1(H1(b1), H2(b2)), G2(c1));

được nghe để đọc. "Khả năng đọc" không có nghĩa là mật độ thông tin; nó có nghĩa là "dễ đọc, hiểu và duy trì".

Đôi khi, mã là đơn giản và nó có ý nghĩa để sử dụng một dòng duy nhất. Những lần khác, làm như vậy chỉ làm cho nó khó đọc hơn, không có lợi ích rõ ràng ngoài việc nhồi nhét nhiều hơn vào một dòng.

Tuy nhiên, tôi cũng gọi bạn bằng cách tuyên bố rằng "dễ chẩn đoán sự cố" có nghĩa là mã dễ duy trì. Mã không sụp đổ là dễ dàng hơn để duy trì. "Dễ bảo trì" đạt được chủ yếu thông qua mã dễ đọc và dễ hiểu, được sao lưu bằng một bộ kiểm tra tự động tốt.

Vì vậy, nếu bạn đang biến một biểu thức đơn thành một biểu thức nhiều dòng có nhiều biến chỉ vì mã của bạn thường gặp sự cố và bạn cần thông tin gỡ lỗi tốt hơn, thì hãy dừng việc đó và thay vào đó làm cho mã mạnh hơn. Bạn nên viết mã không cần gỡ lỗi trên mã dễ gỡ lỗi.


37
Mặc dù tôi đồng ý F(G1(H1(b1), H2(b2)), G2(c1))là khó đọc, nhưng điều này không liên quan gì đến việc bị nhồi nhét quá dày đặc. (Không chắc bạn có ý muốn nói điều đó không, nhưng nó có thể được hiểu theo cách này.) Việc lồng ba hoặc bốn hàm trong một dòng có thể hoàn toàn dễ đọc, đặc biệt nếu một số hàm là toán tử infix đơn giản. Đây là những cái tên không mô tả là vấn đề ở đây, nhưng vấn đề đó thậm chí còn tồi tệ hơn trong phiên bản nhiều dòng, trong đó những cái tên không mô tả hơn được giới thiệu. Chỉ thêm nồi hơi hầu như không bao giờ hỗ trợ khả năng đọc.
rẽ trái

23
@leftaroundabout: Đối với tôi, khó khăn là không rõ liệu G1có 3 tham số hay chỉ 2 và G2là một tham số khác F. Tôi phải nheo mắt và đếm các dấu ngoặc đơn.
Matthieu M.

4
@MatthieuM. đây có thể là một vấn đề, mặc dù nếu các hàm được biết đến thì thường rõ ràng phải mất bao nhiêu đối số. Cụ thể, như tôi đã nói, đối với các hàm infix , ngay lập tức rõ ràng rằng chúng có hai đối số. (Ngoài ra, cú pháp trong ngoặc-tuples nhất ngôn ngữ sử dụng làm trầm trọng thêm vấn đề này, trong một ngôn ngữ mà lại thích currying nó tự động rõ ràng hơn: F (G1 (H1 b1) (H2 b2)) (G2 c1).)
leftaroundabout

5
Cá nhân tôi thích hình thức nhỏ gọn hơn, miễn là có kiểu dáng xung quanh giống như trong nhận xét trước của tôi, bởi vì nó đảm bảo ít trạng thái theo dõi về mặt tinh thần - result_h1không thể được sử dụng lại nếu nó không tồn tại và hệ thống ống nước giữa 4 biến là hiển nhiên
Izkata

8
Tôi đã thấy rằng mã dễ gỡ lỗi nói chung là mã không cần gỡ lỗi.
Rob K

25

Ví dụ đầu tiên của bạn, dạng đơn gán, không thể đọc được vì các tên được chọn hoàn toàn vô nghĩa. Đó có thể là một yếu tố của việc cố gắng không tiết lộ thông tin nội bộ về phía bạn, mã thực sự có thể ổn về mặt đó, chúng tôi không thể nói. Dù sao, nó dài dòng do mật độ thông tin cực kỳ thấp, thường không cho vay để dễ hiểu.

Ví dụ thứ hai của bạn được cô đọng đến một mức độ vô lý. Nếu các hàm có tên hữu ích, điều đó có thể tốt và dễ đọc vì không có quá nhiều , nhưng nó có gây nhầm lẫn theo hướng khác.

Sau khi giới thiệu những cái tên có ý nghĩa, bạn có thể xem liệu một trong những hình thức có vẻ tự nhiên hay nếu có một khoảng giữa vàng để chụp.

Bây giờ bạn đã có mã có thể đọc được, hầu hết các lỗi sẽ rõ ràng và những lỗi khác ít nhất có một thời gian khó khăn hơn để ẩn bạn.


17

Như mọi khi, khi nói đến khả năng đọc, thất bại là ở cực đoan . Bạn có thể thực hiện bất kỳ lời khuyên lập trình tốt, biến nó thành một quy tắc tôn giáo và sử dụng nó để tạo ra mã hoàn toàn không thể đọc được. (Nếu bạn không tin tôi về vấn đề này, hãy kiểm tra hai IOCCC chiến thắng borsanyiGoren và hãy nhìn vào cách khác nhau họ sử dụng chức năng để làm cho đoạn code hoàn toàn không đọc được Gợi ý:. Borsanyi sử dụng đúng một chức năng, Goren nhiều, nhiều hơn nữa ...)

Trong trường hợp của bạn, hai thái cực là 1) chỉ sử dụng các câu lệnh biểu thức duy nhất và 2) nối mọi thứ thành các câu lệnh lớn, ngắn gọn và phức tạp. Hoặc là cách tiếp cận được thực hiện đến mức cực đoan khiến mã của bạn không thể đọc được.

Nhiệm vụ của bạn, là một lập trình viên, là đạt được sự cân bằng . Đối với mỗi câu lệnh bạn viết, nhiệm vụ của bạn là trả lời câu hỏi: "Câu lệnh này có dễ nắm bắt không và nó có phục vụ để làm cho chức năng của tôi có thể đọc được không?"


Vấn đề là, không có độ phức tạp của câu lệnh có thể đo lường nào có thể quyết định, điều gì là tốt để được đưa vào một câu lệnh. Lấy ví dụ dòng:

double d = sqrt(square(x1 - x0) + square(y1 - y0));

Đây là một tuyên bố khá phức tạp, nhưng bất kỳ lập trình viên nào xứng đáng với muối của họ sẽ có thể ngay lập tức nắm bắt được những gì nó làm. Đó là một mô hình khá nổi tiếng. Như vậy, nó dễ đọc hơn nhiều so với tương đương

double dx = x1 - x0;
double dy = y1 - y0;
double dxSquare = square(dx);
double dySquare = square(dy);
double dSquare = dxSquare + dySquare;
double d = sqrt(dSquare);

trong đó phá vỡ mô hình nổi tiếng thành một số bước đơn giản dường như vô nghĩa. Tuy nhiên, tuyên bố từ câu hỏi của bạn

var a = F(G1(H1(b1), H2(b2)), G2(c1));

đối với tôi có vẻ quá phức tạp, mặc dù đó là một thao tác ít hơn tính toán khoảng cách . Tất nhiên, đó là một hệ quả trực tiếp của tôi không biết bất cứ điều gì về F(), G1(), G2(), H1(), hoặc H2(). Tôi có thể quyết định khác nhau nếu tôi biết nhiều hơn về họ. Nhưng đó chính xác là vấn đề: Độ phức tạp có thể khuyên của một tuyên bố phụ thuộc mạnh mẽ vào bối cảnh và vào các hoạt động liên quan. Và bạn, với tư cách là một lập trình viên, là người phải xem xét bối cảnh này và quyết định những gì cần đưa vào một tuyên bố duy nhất. Nếu bạn quan tâm đến khả năng đọc, bạn không thể giảm trách nhiệm này sang một số quy tắc tĩnh.


14

@Dominique, tôi nghĩ trong phân tích câu hỏi của bạn, bạn đang mắc sai lầm rằng "khả năng đọc" và "khả năng duy trì" là hai điều riêng biệt.

Có thể có mã có thể duy trì nhưng không thể đọc được? Ngược lại, nếu mã cực kỳ dễ đọc, tại sao nó lại trở nên không thể hiểu được trên tài khoản có thể đọc được? Tôi chưa bao giờ nghe thấy bất kỳ lập trình viên nào chơi các yếu tố này với nhau, phải chọn cái này hay cái kia!

Về việc quyết định có nên sử dụng các biến trung gian cho các lệnh gọi hàm lồng nhau hay không, trong trường hợp có 3 biến được đưa ra, gọi tới 5 hàm riêng biệt và một số lệnh gọi được lồng sâu 3, tôi sẽ có xu hướng sử dụng ít nhất một số biến trung gian để phá vỡ nó, như bạn đã làm

Nhưng tôi chắc chắn không đi xa để nói các cuộc gọi chức năng không bao giờ được lồng nhau. Đó là một câu hỏi về sự phán xét trong hoàn cảnh.

Tôi sẽ nói những điểm sau đây về phán quyết:

  1. Nếu các hàm được gọi đại diện cho các hoạt động toán học tiêu chuẩn, chúng có khả năng được lồng vào nhau hơn các hàm đại diện cho một số logic miền tối nghĩa mà kết quả không thể đoán trước và người đọc không nhất thiết phải đánh giá về mặt tinh thần.

  2. Một hàm có một tham số duy nhất có khả năng tham gia vào một tổ (có thể là hàm bên trong hoặc bên ngoài) hơn là một hàm có nhiều tham số. Trộn các chức năng của các loại hương liệu khác nhau ở các cấp độ làm tổ khác nhau có xu hướng để lại mã trông giống như tai lợn.

  3. Một hàm các hàm mà các lập trình viên đã quen nhìn thấy được thể hiện theo một cách cụ thể - có lẽ vì nó đại diện cho một kỹ thuật hoặc phương trình toán học tiêu chuẩn, có triển khai tiêu chuẩn - có thể khó đọc và xác minh hơn nếu nó được chia thành các biến trung gian.

  4. Một tổ chức nhỏ của các lệnh gọi thực hiện chức năng đơn giản và đã rõ ràng để đọc, và sau đó bị phá vỡ quá mức và bị nguyên tử hóa, có khả năng khó đọc hơn một thứ không bị phá vỡ.


3
+1 thành "Có thể có mã có thể duy trì nhưng không thể đọc được không?". Đó là suy nghĩ đầu tiên của tôi quá.
RonJohn

4

Cả hai đều là tối ưu. Xem xét Nhận xét.

// Calculating torque according to Newton/Dominique, 4th ed. pg 235
var a = F(G1(H1(b1), H2(b2)), G2(c1));

Hoặc các chức năng cụ thể chứ không phải chung chung:

var a = Torque_NewtonDominique(b1,b2,c1);

Khi quyết định đánh vần kết quả nào, hãy ghi nhớ chi phí (sao chép so với tham chiếu, giá trị l so với giá trị r), khả năng đọc và rủi ro, riêng cho từng tuyên bố.

Ví dụ: không có giá trị gia tăng nào từ việc chuyển các chuyển đổi đơn vị / loại đơn giản sang các dòng riêng của chúng, bởi vì những thứ này dễ đọc và cực kỳ khó thất bại:

var radians = ExtractAngle(c1.Normalize())
var a = Torque(b1.ToNewton(),b2.ToMeters(),radians);

Liên quan đến lo lắng của bạn về việc phân tích các bãi đổ vỡ, xác thực đầu vào thường quan trọng hơn nhiều - sự cố thực tế rất có thể xảy ra bên trong các chức năng này thay vì đường dây gọi chúng, và thậm chí nếu không, bạn thường không cần phải nói chính xác mọi thứ nổ tung. Điều quan trọng hơn là phải biết nơi mọi thứ bắt đầu tan vỡ, hơn là để biết cuối cùng chúng đã nổ tung ở đâu, đó là những gì xác thực đầu vào bắt được.


Re chi phí của việc vượt qua một arg: Có hai quy tắc tối ưu hóa. 1) Đừng. 2) (đối với các chuyên gia chỉ) Đừng chưa .
RubberDuck

1

Khả năng đọc là phần chính của khả năng bảo trì. Nghi ngờ tôi? Chọn một dự án lớn bằng ngôn ngữ mà bạn không biết (chắc chắn cả ngôn ngữ lập trình và ngôn ngữ của các lập trình viên) và xem bạn sẽ tiến hành tái cấu trúc nó như thế nào ...

Tôi sẽ đặt khả năng đọc như ở đâu đó giữa 80 và 90 khả năng bảo trì. 10-20% còn lại là khả năng tái cấu trúc.

Điều đó nói rằng, bạn có hiệu quả chuyển 2 biến vào hàm cuối cùng (F). 2 biến đó được tạo bằng 3 biến khác. Bạn nên chuyển b1, b2 và c1 vào F, nếu F đã tồn tại, thì hãy tạo D để tạo thành phần cho F và trả về kết quả. Vào thời điểm đó, vấn đề chỉ là đặt cho D một cái tên hay, và việc bạn sử dụng phong cách nào sẽ không thành vấn đề.

Về một không liên quan, bạn nói rằng logic hơn trên trang hỗ trợ khả năng đọc. Điều đó không chính xác, số liệu không phải là trang, nó là phương thức và logic LESS mà phương thức chứa, nó càng dễ đọc hơn.

Có thể đọc được có nghĩa là lập trình viên có thể giữ logic (đầu vào, đầu ra và thuật toán) trong đầu của họ. Càng làm nhiều, LESS một lập trình viên có thể hiểu nó. Đọc lên về độ phức tạp chu kỳ.


1
Tôi đồng ý với tất cả những gì bạn nói về khả năng đọc. Nhưng tôi không đồng ý rằng việc bẻ khóa một hoạt động logic thành các phương thức riêng biệt, nhất thiết làm cho nó dễ đọc hơn việc bẻ khóa nó thành các dòng riêng biệt (cả hai kỹ thuật có thể , khi bị lạm dụng, làm cho logic đơn giản trở nên dễ đọc hơn và làm cho toàn bộ chương trình trở nên lộn xộn hơn) - nếu bạn bẻ khóa mọi thứ quá xa vào các phương thức, cuối cùng bạn sẽ mô phỏng các macro ngôn ngữ lắp ráp và đánh mất cách chúng tích hợp toàn bộ. Ngoài ra, trong phương pháp riêng biệt này, bạn vẫn sẽ phải đối mặt với cùng một vấn đề: lồng các cuộc gọi hoặc bẻ khóa chúng thành các biến trung gian.
Steve

@Steve: Tôi không nói là luôn luôn làm điều đó, nhưng nếu bạn đang nghĩ về việc sử dụng 5 dòng để có một giá trị duy nhất, thì rất có thể một chức năng sẽ tốt hơn. Đối với nhiều dòng so với dòng phức tạp: nếu đó là một hàm có tên tốt thì cả hai sẽ hoạt động tốt như nhau.
jmoreno

1

Bất kể bạn đang ở trong C # hay C ++, miễn là bạn đang ở trong bản dựng gỡ lỗi, một giải pháp khả thi là gói các hàm

var a = F(G1(H1(b1), H2(b2)), G2(c1));

Bạn có thể viết biểu thức trực tuyến và vẫn nhận được vấn đề chỉ đơn giản bằng cách nhìn vào dấu vết ngăn xếp.

returnType F( params)
{
    returnType RealF( params);
}

Tất nhiên, nếu trường hợp bạn gọi cùng một chức năng nhiều lần trong cùng một dòng, bạn không thể biết chức năng nào, tuy nhiên bạn vẫn có thể xác định chức năng đó:

  • Nhìn vào các tham số chức năng
  • Nếu các tham số giống hệt nhau và chức năng không có tác dụng phụ thì hai cuộc gọi giống nhau sẽ trở thành 2 cuộc gọi giống nhau, v.v.

Đây không phải là một viên đạn bạc, nhưng là một nửa không quá tệ.

Chưa kể rằng nhóm các hàm thậm chí có thể có lợi hơn cho khả năng đọc mã:

type CallingGBecauseFTheorem( T b1, C b2)
{
     return G1( H1( b1), H2( b2));
}

var a = F( CallingGBecauseFTheorem( b1,b2), G2( c1));

1

Theo tôi, mã tự viết tài liệu tốt hơn cho cả khả năng duy trì và khả năng đọc, bất kể ngôn ngữ.

Tuyên bố được đưa ra ở trên là dày đặc, nhưng "tự ghi lại":

double d = sqrt(square(x1 - x0) + square(y1 - y0));

Khi được chia thành các giai đoạn (dễ dàng hơn để thử nghiệm, chắc chắn) sẽ mất tất cả bối cảnh như đã nêu ở trên:

double dx = x1 - x0;
double dy = y1 - y0;
double dxSquare = square(dx);
double dySquare = square(dy);
double dSquare = dxSquare + dySquare;
double d = sqrt(dSquare);

Và rõ ràng sử dụng tên biến và hàm cho biết mục đích của chúng rõ ràng là vô giá.

Ngay cả các khối "nếu" có thể tốt hoặc xấu trong việc tự viết tài liệu. Điều này là xấu vì bạn không thể dễ dàng buộc 2 điều kiện đầu tiên để kiểm tra thứ ba ... tất cả đều không liên quan:

if (Bill is the boss) && (i == 3) && (the carnival is next weekend)

Điều này làm cho ý nghĩa "tập thể" hơn và dễ dàng hơn để tạo điều kiện thử nghiệm:

if (iRowCount == 2) || (iRowCount == 50) || (iRowCount > 100)

Và tuyên bố này chỉ là một chuỗi ký tự ngẫu nhiên, được nhìn từ góc độ tự ghi lại:

var a = F(G1(H1(b1), H2(b2)), G2(c1));

Nhìn vào tuyên bố trên, khả năng bảo trì vẫn là một thách thức lớn nếu cả hai chức năng H1 và H2 đều thay đổi cùng một "biến trạng thái hệ thống" thay vì được hợp nhất thành một chức năng "H", bởi vì cuối cùng ai đó sẽ thay đổi H1 mà không nghĩ rằng có một Chức năng H2 để xem xét và có thể phá vỡ H2.

Tôi tin rằng thiết kế mã tốt là rất khó khăn vì không có quy tắc khó và nhanh nào có thể được phát hiện và thi hành một cách có hệ thống.

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.