Sử dụng một cái khác sau ngoại lệ (hoặc không)


9

Hãy xem xét đoạn mã này:

if (x == 1)
{
  throw "no good; aborting" ;
}

[... more code ...]

Bây giờ hãy xem xét mã này:

if (x == 1)
{
  throw "no good; aborting" ;
}
else
{
  [... more code ...]
}

Hai trường hợp hoạt động chính xác theo cùng một cách. Trường hợp đầu tiên có lợi thế là bạn không phải "bọc" phần còn lại của mã trong một else. Thứ hai có lợi thế sau khi thực hành rõ ràng có một elsecho mọi if.

Bất cứ ai cũng có thể cung cấp bất kỳ đối số vững chắc có lợi cho nhau hơn?


5
Các thực hành bạn trích dẫn ủng hộ rõ ràng elsedường như không có thật. Rất thường xuyên, đơn giản là không có gì để đưa vào elsekhối trừ khi bạn uốn cong về phía sau.

Tính nhất quán? Cho phép mạnh mẽ khi đối mặt với sự thay đổi mã? Dễ đọc?
Thomas Eding

1
Ehhhh, tôi không phải là một fan hâm mộ của ý tưởng mà mọi người ifcần else. Lập trình viên cuối cùng làm việc trên cơ sở mã của chúng tôi đã tuân theo điều đó một cách cứng nhắc ( tốt, đôi khi ... đó là loại tâm thần phân liệt ). Kết quả là, chúng ta có rất nhiều else { /* do nothing */ }khối hoàn toàn vô nghĩa xả rác mã ...
KChaloux

4
"Một cái khác cho mọi thứ nếu" có vẻ như một lời tuyên bố kỳ quái được đưa ra bởi một kiến ​​trúc sư đám mây nhân danh sự nhất quán (một sự ngu ngốc). Tôi thấy không có lợi thế để tuân theo thực tiễn đó và thậm chí chưa bao giờ nghe về nó ở bất cứ đâu.
Erik Dietrich

Đó là một thứ dư thừa. Nếu bạn đang làm việc với .NET stack, hãy cài đặt ReSharper và nó sẽ nhắc bạn xóa tất cả các câu lệnh dự phòng khác.
CodeART

Câu trả lời:


16

Bạn không nên thêm elsevào sau khi ifcác nhánh phá vỡ dòng điều khiển vô điều kiện , chẳng hạn như các nhánh chứa a throwhoặc a return. Điều này sẽ cải thiện khả năng đọc chương trình của bạn bằng cách loại bỏ mức lồng không cần thiết được giới thiệu bởi elsechi nhánh.

Những gì trông ít nhiều OK với một lần throwtrở nên thực sự xấu xí khi một vài cú ném liên tiếp được gọi ra:

void myMethod(int arg1, int arg2, int arg3) {
    // This is demonstrably ugly - do not code like that!
    if (!isValid(arg1)) {
        throw new ArgumentException("arg1 is invalid");
    } else {
        if (!isValid(arg2)) {
            throw new ArgumentException("arg2 is invalid");
        } else {
            if (!isValid(arg3)) {
                throw new ArgumentException("arg3 is invalid");
            } else {
                // The useful code starts here
            }
        }
    }
}

Đoạn mã này làm điều tương tự, nhưng nó có vẻ tốt hơn nhiều:

void myMethod(int arg1, int arg2, int arg3) {
    if (!isValid(arg1)) {
        throw new ArgumentException("arg1 is invalid");
    }
    if (!isValid(arg2)) {
        throw new ArgumentException("arg2 is invalid");
    }
    if (!isValid(arg3)) {
        throw new ArgumentException("arg3 is invalid");
    }
    // The useful code starts here
}

+1 Đúng. Trường hợp OP thứ hai buộc bạn phải đọc kỹ, sau đó để lại cho bạn một WTF. Nhưng ... luôn cố gắng làm cho các phương pháp ngắn lại. Một sự trở lại ở giữa một phương thức 200 dòng cũng là xấu.
Tulains Córdova

1
Để công bằng, nếu bạn chỉ sử dụng lặp đi lặp lại nếu bạn có thể làm else if.
Guvante

2
@Guvante: Mỗi ifthử nghiệm cho một điều kiện duy nhất và xử lý nếu điều kiện đó là đúng và trừ khi có một số điều thay thế phải xảy ra nếu điều kiện đó là sai, else ifs là không cần thiết. Chúng tôi có một thuật ngữ xung quanh văn phòng của tôi về mã như đoạn trích đầu tiên của dasblinkenlight: " máy pachinko ."
Blrfl

@Blrfl pachinko máy hah, sự tương tự hoàn hảo +1
Jimmy Hoffa

@Blrfl: Tôi đã tham khảo rằng ifs lặp đi lặp lại là một ví dụ tồi tệ của quá nhiều lồng. Dù sao thì bạn cũng không nên làm tổ lặp đi lặp lại. Tôi đồng ý rằng nói chung trừ khi bạn đang nói về một lượng nhỏ mã, không có lý do gì để đưa vào else.
Guvante

5

Tôi sẽ gọi thực tiễn "rõ ràng khác" mà bạn đề cập đến như là một mô hình chống, vì nó che khuất thực tế rằng không có mã trường hợp đặc biệt nào khác như nếu bạn sử dụng.

Khả năng đọc / bảo trì nói chung được cải thiện khi bạn hầu như không có gì ngoài các cấu trúc dòng mã cần thiết và bạn giảm thiểu chúng. Điều này có nghĩa là elses dư thừa và nếu đó sẽ thêm một phạm vi cho toàn bộ chức năng thì việc theo dõi và duy trì nó trở nên khó khăn hơn.

Nói ví dụ bạn có chức năng này:

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (_validColors.Contains(oblogonToConfigure.Color))
    {
        oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
    }
    else
    {
        oblogonToConfigure.Color = _validColors[0];
        oblogonToConfigure.ColorIndex = 0;
    }
}

Bây giờ yêu cầu xuất hiện là trong quá trình cấu hình, bạn cũng nên xác định chỉ mục loại / loại của oblogon, có nhiều phạm vi mà ai đó có thể đặt mã đó và kết thúc bằng mã không hợp lệ, tức là

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (!_validOblogons.Contains(oblogonToConfigure.Type))
    {
        oblogonToConfigure.Type = _validOblogons[0];
        oblogonToConfigure.TypeIndex = 0;
        if (_validColors.Contains(oblogonToConfigure.Color))
        {
            oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
        }
        else
        {
            oblogonToConfigure.Color = _validColors[0];
            oblogonToConfigure.ColorIndex = 0;
        }
    }
    else
    {
        oblogonToConfigure.TypeIndex = _validOblogons.IndexOf(oblogonToConfigure.Type);
    }
}

So sánh điều này với nếu mã gốc được viết với các cấu trúc luồng điều khiển tối thiểu cần thiết và tối thiểu hóa các cấu trúc đó.

public void ConfigureOblogon(Oblogon oblogonToConfigure)
{
    if (!_validColors.Contains(oblogonToConfigure.Color))
    {
        oblogonToConfigure.Color = _validColors[0];
    }

    oblogonToConfigure.ColorIndex = _validColors.IndexOf(oblogonToConfigure.Color);
}

Bây giờ sẽ khó khăn hơn nhiều khi vô tình đặt một cái gì đó vào phạm vi sai hoặc kết thúc phạm vi đầy hơi gây ra sự trùng lặp trong tăng trưởng dài hạn và duy trì chức năng này. Thêm vào đó, rõ ràng những gì có thể chảy qua chức năng này là rất dễ đọc.

Tôi biết, ví dụ này hơi khó hiểu, nhưng tôi đã thấy nhiều lần

SomeFunction()
{
    if (isvalid)
    {
        /* ENTIRE FUNCTION */
    }
    /* Nothing should go here but something does on accident, and an invalid scenario is created. */
}

Vì vậy, chính thức hóa các quy tắc về cấu trúc dòng điều khiển tôi nghĩ có thể giúp mọi người phát triển trực giác cần thiết để ngửi thứ gì đó khi họ bắt đầu viết mã như thế. Sau đó, họ sẽ bắt đầu viết ..

SomeFunction()
{
    if (!isvalid)
    {
        /* Nothing should go here, and it's so small no one will likely accidentally put something here */
        return;
    }

    /* ENTIRE FUNCTION */
}
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.