Là việc sử dụng các khối phạm vi nội bộ trong một phong cách xấu?


10

Có một số trường hợp (khá hiếm) có nguy cơ:

  • sử dụng lại một biến không có ý định sử dụng lại (xem ví dụ 1),

  • hoặc sử dụng một biến thay vì một biến khác, gần đúng về mặt ngữ nghĩa (xem ví dụ 2).

Ví dụ 1:

var data = this.InitializeData();
if (this.IsConsistent(data, this.state))
{
    this.ETL.Process(data); // Alters original data in a way it couldn't be used any longer.
}

// ...

foreach (var flow in data.Flows)
{
    // This shouldn't happen: given that ETL possibly altered the contents of `data`, it is
    // not longer reliable to use `data.Flows`.
}

Ví dụ 2:

var userSettingsFile = SettingsFiles.LoadForUser();
var appSettingsFile = SettingsFiles.LoadForApp();

if (someCondition)
{
    userSettingsFile.Destroy();
}

userSettingsFile.ParseAndApply(); // There is a mistake here: `userSettingsFile` was maybe
                                  // destroyed. It's `appSettingsFile` which should have
                                  // been used instead.

Rủi ro này có thể được giảm thiểu bằng cách giới thiệu một phạm vi:

Ví dụ 1:

// There is no `foreach`, `if` or anything like this before `{`.

{
    var data = this.InitializeData();
    if (this.IsConsistent(data, this.state))
    {
        this.ETL.Process(data);
    }
}

// ...

// A few lines later, we can't use `data.Flows`, because it doesn't exist in this scope.

Ví dụ 2:

{
    var userSettingsFile = SettingsFiles.LoadForUser();

    if (someCondition)
    {
        userSettingsFile.Destroy();
    }
}

{
    var appSettingsFile = SettingsFiles.LoadForApp();

    // `userSettingsFile` is out of scope. There is no risk to use it instead of
    // `appSettingsFile`.
}

Trông nó có sai không? Bạn sẽ tránh cú pháp như vậy? Có khó hiểu bởi người mới bắt đầu?


7
Bạn có thể lập luận rằng mỗi "khối phạm vi" độc lập thay vào đó nên là phương thức hoặc chức năng riêng của nó?
Dan Pichelman

Tôi sẽ nói "thường" không được thực hiện tốt như nó có thể (nhưng có lẽ không phải là vấn đề "thiết kế"). Đôi khi theo thiết kế, điều đó là không thể tránh khỏi (ví dụ phạm vi ngoại lệ / tài nguyên).
JustinC

Câu trả lời:


34

Nếu chức năng của bạn quá dài đến nỗi bạn không thể nhận ra bất kỳ tác dụng phụ không mong muốn nào hoặc tái sử dụng bất hợp pháp các biến nữa, thì đã đến lúc chia nó thành các hàm nhỏ hơn - điều này làm cho phạm vi bên trong trở nên vô nghĩa.

Để hỗ trợ điều này bằng một số kinh nghiệm cá nhân: vài năm trước tôi đã kế thừa một dự án kế thừa C ++ với ~ 150K dòng mã và nó chứa một vài phương thức sử dụng chính xác kỹ thuật này. Và đoán xem - tất cả những phương pháp đó quá dài. Khi chúng tôi tái cấu trúc hầu hết mã đó, các phương thức trở nên nhỏ hơn và nhỏ hơn và tôi khá chắc chắn rằng không còn phương thức "phạm vi nội bộ" nào nữa; họ chỉ đơn giản là không cần thiết.


4
Nhưng tại sao nó xấu? Có nhiều chức năng được đặt tên cũng khó hiểu.
Kris Van Bael

1
+1 Chính xác, nếu bạn cần một phạm vi, có khả năng bạn đang thực hiện một hành động cụ thể có thể được trích xuất thành một chức năng. Kris, không phải là về việc tạo ra nhiều và nhiều chức năng, mà là khi những trường hợp này xảy ra (trong đó phạm vi của một khối có thể xung đột với các khối khác) thường là một chức năng nên được sử dụng. Tôi thấy điều này trong các ngôn ngữ khác (cụ thể là JavaScript, với các chức năng cũ của IIFE thay vì các chức năng cũ).
Benjamin Gruenbaum

10
@KrisVanBael: "Có nhiều chức năng được đặt tên cũng khó hiểu" - nếu bạn sử dụng tên mô tả, hoàn toàn ngược lại là đúng. Tạo các hàm quá lớn là lý do hàng đầu khiến mã trở nên khó bảo trì và thậm chí khó hơn trong phạm vi.
Doc Brown

2
Nếu có quá nhiều chức năng khiến việc đặt tên chúng rõ ràng trở thành một vấn đề, có thể một số trong số chúng có thể được chuyển thành một loại mới vì chúng có thể có sự độc lập đáng kể với cơ thể chức năng ban đầu của chúng. Một số có thể liên quan chặt chẽ đến mức chúng có thể bị loại bỏ. Đột nhiên, không có quá nhiều chức năng.
JustinC

3
Vẫn không thuyết phục. Thường thì chức năng dài là xấu thực sự. Nhưng nói rằng mọi nhu cầu về phạm vi đòi hỏi một chức năng riêng biệt là quá đen và trắng đối với tôi.
Kris Van Bael

3

Người mới bắt đầu (và bất kỳ lập trình viên) nào cũng dễ dàng nghĩ đến:

  • không sử dụng biến không liên quan

hơn là nghĩ về:

  • thêm các cấu trúc mã nhằm ngăn chặn bản thân không sử dụng một biến không liên quan.

Hơn nữa, từ quan điểm của người đọc, các khối như vậy không có vai trò rõ ràng: loại bỏ chúng không có tác động đến việc thực hiện. Người đọc có thể gãi đầu cố gắng đoán những gì lập trình viên muốn đạt được.


2

Sử dụng một phạm vi là đúng kỹ thuật. Nhưng nếu bạn muốn làm cho mã của mình dễ đọc hơn thì bạn nên trích xuất phần đó trong một phương thức khác và đặt tên đầy đủ đó. Theo cách này, bạn có thể đưa ra một phạm vi các biến của mình và cũng đưa ra một tên của nhiệm vụ của bạn.


0

Tôi đồng ý rằng việc tái sử dụng biến thường xuyên hơn không phải là mùi mã. Có lẽ mã như vậy nên được cấu trúc lại trong các khối nhỏ hơn, khép kín.

Có một kịch bản cụ thể, OTOH, trong C #, khi chúng có xu hướng bật lên - đó là khi sử dụng các cấu trúc trường hợp chuyển đổi. Tình huống được giải thích rất độc đáo trong: C # Chuyển câu lệnh có / không có dấu ngoặc nhọn Cách khác biệt?

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.