Làm cách nào để tôi chỉnh sửa chuỗi if-if if if tuân thủ các nguyên tắc Clean Code của chú Bob?


45

Tôi đang cố gắng làm theo các đề xuất mã sạch của chú Bob và đặc biệt để giữ cho các phương thức ngắn.

Tôi thấy mình không thể rút ngắn logic này mặc dù:

if (checkCondition()) {addAlert(1);}
else if (checkCondition2()) {addAlert(2);}
else if (checkCondition3()) {addAlert(3);}
else if (checkCondition4()) {addAlert(4);}

Tôi không thể loại bỏ các elses và do đó tách toàn bộ thành các bit nhỏ hơn, vì "khác" trong "if if" giúp thực hiện - đánh giá các điều kiện đó là tốn kém và nếu tôi có thể tránh đánh giá các điều kiện bên dưới, gây ra một trong những điều kiện đầu tiên là sự thật, tôi muốn tránh chúng.

Ngay cả về mặt ngữ nghĩa, việc đánh giá điều kiện tiếp theo nếu đáp ứng trước đó không có ý nghĩa theo quan điểm kinh doanh.


chỉnh sửa: Câu hỏi này được xác định là một bản sao có thể có của các cách Thanh lịch để xử lý nếu (nếu khác) khác .

Tôi tin rằng đây là một câu hỏi khác nhau (bạn cũng có thể thấy điều đó bằng cách so sánh câu trả lời của những câu hỏi đó).

  • Câu hỏi của tôi là kiểm tra điều kiện chấp nhận đầu tiên để kết thúc nhanh chóng .
  • Câu hỏi được liên kết là cố gắng có tất cả các điều kiện để được chấp nhận để làm một cái gì đó. (thấy rõ hơn trong câu trả lời này cho câu hỏi đó: https://softwareengineering.stackexchange.com/a/122625/96955 )

46
Điều gì thực sự sai hoặc không rõ ràng về mã này trong bối cảnh của nó? Tôi không thể thấy làm thế nào nó có thể được rút ngắn hoặc đơn giản hóa! Mã đánh giá các điều kiện đã xuất hiện đầy đủ, như là phương thức được gọi là kết quả của quyết định. Bạn chỉ cần nhìn vào một số câu trả lời dưới đây, điều đó chỉ làm phức tạp mã!
Steve

38
Không có gì sai với mã này. Nó rất dễ đọc và dễ làm theo. Bất cứ điều gì bạn làm để thu nhỏ nó hơn nữa sẽ thêm sự gián tiếp và làm cho nó khó hiểu hơn.
17 của ngày 26

20
Mã của bạn là tốt. Đặt năng lượng còn lại của bạn vào một cái gì đó hiệu quả hơn là cố gắng rút ngắn nó hơn nữa.
Robert Harvey

5
Nếu nó thực sự chỉ có 4 điều kiện, điều này là tốt. Nếu nó thực sự giống như 12 hoặc 50 thì có lẽ bạn muốn cấu trúc lại ở mức cao hơn phương thức này.
JimmyJames

9
Để lại mã của bạn chính xác như nó là. Hãy lắng nghe những gì cha mẹ bạn luôn nói với bạn: Đừng tin bất kỳ người chú nào cung cấp đồ ngọt cho trẻ em trên đường phố. @Harvey Hài hước lắm, những nỗ lực khác nhau trong việc "cải thiện" mã đều khiến nó lớn hơn nhiều, phức tạp hơn và ít đọc hơn.
gnasher729

Câu trả lời:


81

Lý tưởng nhất là tôi nghĩ bạn nên trích xuất logic của mình để lấy mã / số cảnh báo vào phương thức riêng của nó. Vì vậy, mã hiện tại của bạn được giảm xuống

{
    addAlert(GetConditionCode());
}

và bạn có GetConditionCode () gói gọn logic để kiểm tra các điều kiện. Có lẽ cũng tốt hơn để sử dụng Enum hơn là một con số ma thuật.

private AlertCode GetConditionCode() {
    if (CheckCondition1()) return AlertCode.OnFire;
    if (CheckCondition2()) return AlertCode.PlagueOfBees;
    if (CheckCondition3()) return AlertCode.Godzilla;
    if (CheckCondition4()) return AlertCode.ZombieSharkNado;
    return AlertCode.None;
}

2
Nếu có thể gói gọn như bạn mô tả (tôi nghi ngờ có thể không, tôi nghĩ OP đang bỏ qua các biến để đơn giản), nó không thay đổi mã, bản thân nó cũng ổn, nhưng thêm công thái mã và một chút khả năng đọc + 1
opa

17
Với các mã cảnh báo đó, tôi cảm ơn mã chỉ có thể được trả lại một lần tại một thời điểm
Josh Phần

12
Đây dường như cũng là một kết hợp hoàn hảo để sử dụng câu lệnh chuyển đổi - nếu điều đó có sẵn bằng ngôn ngữ của OP.
Frank Hopkins

4
Có lẽ chỉ nên trích xuất nhận mã lỗi sang một phương thức mới nếu điều đó có thể được viết để có ích trong nhiều tình huống mà không cần phải cung cấp một loạt thông tin về tình huống cụ thể. Trên thực tế có một sự đánh đổi và một điểm hòa vốn khi nó có giá trị. Nhưng thường thì bạn sẽ thấy rằng trình tự xác nhận là cụ thể cho công việc trong tay, và tốt hơn là giữ chúng cùng với công việc đó. Trong những trường hợp như vậy, phát minh ra một loại mới để nói với một phần khác của mã, những gì cần phải làm là chấn lưu không mong muốn.
PJTraill

6
Một vấn đề với việc thực hiện lại này là nó làm cho chức năng addAlertcần kiểm tra tình trạng cảnh báo không có thật AlertCode.None.
David Hammen

69

Phép đo quan trọng là độ phức tạp của mã, không phải kích thước tuyệt đối. Giả sử rằng các điều kiện khác nhau thực sự chỉ là các lệnh gọi hàm đơn, giống như các hành động không phức tạp hơn những gì bạn đã thể hiện, tôi nói rằng không có gì sai với mã. Nó đã đơn giản như nó có thể được.

Bất kỳ nỗ lực nào để "đơn giản hóa" hơn nữa sẽ thực sự làm phức tạp mọi thứ.

Tất nhiên, bạn có thể thay thế elsetừ khóa bằng một từ khóa returnnhư những người khác đã đề xuất, nhưng đó chỉ là vấn đề về phong cách, không phải là sự thay đổi về độ phức tạp.


Qua một bên:

Lời khuyên chung của tôi là, đừng bao giờ tôn giáo về bất kỳ quy tắc nào về mã sạch: Hầu hết các lời khuyên về mã hóa bạn thấy trên internet đều tốt nếu được áp dụng trong ngữ cảnh phù hợp, nhưng áp dụng triệt để lời khuyên đó ở mọi nơi có thể giúp bạn tham gia các IOCCC . Bí quyết là luôn luôn đạt được sự cân bằng cho phép con người dễ dàng suy luận về mã của bạn.

Sử dụng các phương pháp quá lớn, và bạn bị vặn. Sử dụng các chức năng quá nhỏ, và bạn bị vặn. Tránh các biểu thức ternary, và bạn đang say sưa. Sử dụng các biểu thức ternary ở khắp mọi nơi, và bạn đang say sưa. Nhận ra rằng có những nơi gọi hàm một dòng và những nơi gọi hàm 50 dòng (vâng, chúng tồn tại!). Nhận ra rằng có những nơi gọi cho các if()câu lệnh và có những nơi gọi cho ?:toán tử. Sử dụng kho vũ khí đầy đủ theo ý của bạn và cố gắng luôn sử dụng công cụ phù hợp nhất bạn có thể tìm thấy. Và hãy nhớ, đừng có tôn giáo ngay cả về lời khuyên này.


2
Tôi tranh luận rằng việc thay thế else ifbằng một bên trong returntheo sau bởi một đơn giản if(loại bỏ else) có thể làm cho mã khó đọc hơn . Khi mã nói else if, tôi lập tức biết rằng mã trong khối tiếp theo sẽ chỉ thực hiện nếu mã trước đó không thực hiện. Không ầm ĩ, không ồn ào. Nếu đó là một đồng bằng ifthì nó có thể thực thi hoặc có thể không, bất kể cái trước có thực thi hay không . Bây giờ tôi sẽ phải dành một số nỗ lực tinh thần để phân tích khối trước đó để lưu ý rằng nó kết thúc bằng a return. Tôi muốn dành nỗ lực tinh thần đó để phân tích logic kinh doanh.
một CVn

1
Tôi biết, đó là một điều nhỏ, nhưng ít nhất với tôi, else iftạo thành một đơn vị ngữ nghĩa. (Nó không nhất thiết là một đơn vị cho trình biên dịch, nhưng điều đó không sao.) ...; return; } if (...Không; hãy để một mình nếu nó trải ra trên nhiều dòng. Đó là điều mà tôi thực sự phải nhìn vào để xem nó đang làm gì, thay vì có thể trực tiếp đưa nó vào bằng cách chỉ nhìn thấy cặp từ khóa else if.
một CVn

@ MichaelKjorling Full Ack. Bản thân tôi thích else ifcấu trúc này, đặc biệt vì dạng xích của nó là một mẫu nổi tiếng như vậy. Tuy nhiên, mã của biểu mẫu if(...) return ...;cũng là một mẫu nổi tiếng, vì vậy tôi sẽ không hoàn toàn lên án điều đó. Tuy nhiên, tôi thấy đây thực sự là một vấn đề nhỏ: Logic luồng điều khiển giống nhau trong cả hai trường hợp và một cái nhìn gần hơn về một if(...) { ...; return; }cái thang sẽ cho tôi biết rằng nó thực sự tương đương với một else ifcái thang. Tôi thấy cấu trúc của một thuật ngữ duy nhất, tôi suy ra ý nghĩa của nó, tôi nhận ra rằng nó lặp đi lặp lại ở mọi nơi và tôi biết những gì đang xảy ra.
cmaster

Đến từ JavaScript / node.js, một số người sẽ sử dụng mã "vành đai và treo" bằng cách sử dụng cả hai else if return . ví dụelse if(...) { return alert();}
user949300

1
"Và hãy nhớ, đừng có tôn giáo ngay cả về lời khuyên này." +1
Những từ như Jared

22

Điều gây tranh cãi là liệu điều này 'tốt hơn' so với đơn giản nếu ... cho bất kỳ trường hợp cụ thể nào. Nhưng nếu bạn muốn thử một cái gì đó khác thì đây là một cách phổ biến để làm điều đó.

Đặt điều kiện của bạn vào các đối tượng và đưa các đối tượng đó vào một danh sách

foreach(var condition in Conditions.OrderBy(i=>i.OrderToRunIn))
{
    if(condition.EvaluatesToTrue())
    {
        addAlert(condition.Alert);
        break;
    }
}

Nếu nhiều hành động được yêu cầu với điều kiện bạn có thể thực hiện một số đệ quy điên

void RunConditionalAction(ConditionalActionSet conditions)
{
    foreach(var condition in conditions.OrderBy(i=>i.OrderToRunIn))
    {
        if(condition.EvaluatesToTrue())
        {
            RunConditionalAction(condition);
            break;
        }
    }
}

Chắc chắn đúng. Điều này chỉ hoạt động nếu bạn có một mô hình logic của bạn. Nếu bạn cố gắng thực hiện một hành động đệ quy có điều kiện siêu chung thì việc thiết lập cho đối tượng sẽ phức tạp như câu lệnh if gốc. Bạn sẽ phát minh ra ngôn ngữ / khung mới của riêng bạn.

Nhưng ví dụ của bạn không có mẫu

Một trường hợp sử dụng phổ biến cho mẫu này sẽ là xác nhận. Thay vì :

bool IsValid()
{
    if(condition1 == false)
    {
        throw new ValidationException("condition1 is wrong!");
    }
    elseif(condition2 == false)
    {
    ....

}

Trở thành

[MustHaveCondition1]
[MustHaveCondition2]
public myObject()
{
    [MustMatchRegExCondition("xyz")]
    public string myProperty {get;set;}
    public bool IsValid()
    {
        conditions = getConditionsFromReflection()
        //loop through conditions
    }
}

27
Điều này chỉ di chuyển các if...elsebậc thang vào việc xây dựng Conditionsdanh sách. Lợi ích ròng là âm, vì việc xây dựng Conditionssẽ chỉ mất nhiều mã như mã OP, nhưng việc bổ sung thêm đi kèm với chi phí dễ đọc. Tôi chắc chắn thích một cái thang được mã hóa sạch sẽ.
cmaster

3
@cmaster vâng tôi nghĩ rằng tôi đã nói chính xác điều đó ", sau đó thiết lập cho các đối tượng sẽ được phức tạp như bản gốc câu lệnh if £
Ewan

7
Điều này ít đọc hơn bản gốc. Để tìm ra điều kiện nào thực sự được kiểm tra, bạn cần phải đi đào trong một số khu vực khác của mã. Nó thêm một mức độ không xác định không cần thiết làm cho mã khó hiểu hơn.
17 của ngày 26

8
Chuyển đổi một if .. other if .. other .. chain thành một bảng các vị từ và hành động có ý nghĩa, nhưng chỉ cho các ví dụ lớn hơn nhiều. Bảng thêm một số phức tạp và gián tiếp, vì vậy bạn cần có đủ các mục để khấu hao chi phí thụ thai này. Vì vậy, đối với 4 cặp vị ngữ / hành động, hãy giữ mã gốc đơn giản, nhưng nếu bạn có 100, chắc chắn đi cùng với bảng. Điểm giao nhau là một nơi nào đó ở giữa. @cmaster, bảng có thể được khởi tạo tĩnh, do đó, chi phí gia tăng để thêm một cặp vị ngữ / hành động là một dòng chỉ đặt tên cho chúng: khó có thể làm tốt hơn.
Stephen C. Steel

2
Khả năng đọc là KHÔNG cá nhân. Đó là một nghĩa vụ đối với công chúng lập trình. Đó là chủ quan. Đó chính xác là lý do tại sao điều quan trọng là đến những nơi như thế này và lắng nghe những gì công chúng lập trình nói về nó. Cá nhân tôi thấy ví dụ này không đầy đủ. Chỉ cho tôi cách conditionsxây dựng ... ARG! Không phải chú thích-thuộc tính! Tai sao vậy trời? Mắt tôi!
candied_orange

7

Cân nhắc sử dụng return;sau khi một điều kiện đã thành công, nó giúp bạn tiết kiệm tất cả các elses. Bạn thậm chí có thể return addAlert(1)trực tiếp nếu phương thức đó có giá trị trả về.


3
Tất nhiên, điều này giả định rằng không có gì khác xảy ra sau chuỗi ifs ... Đó có thể là một giả định hợp lý, và một lần nữa nó có thể không xảy ra.
một CVn

5

Đôi khi tôi đã thấy các công trình như thế này được coi là sạch hơn:

switch(true) {
    case cond1(): 
        statement1; break;
    case cond2():
        statement2; break;
    case cond3():
        statement3; break;
    // .. etc
}

Ternary với khoảng cách đúng cũng có thể là một thay thế gọn gàng:

cond1() ? statement1 :
cond2() ? statement2 :
cond3() ? statement3 : (null);

Tôi đoán bạn cũng có thể thử tạo một mảng với cặp chứa điều kiện và hàm và lặp lại cho đến khi điều kiện đầu tiên được đáp ứng - điều mà tôi thấy sẽ bằng với câu trả lời đầu tiên của Ewan.


1
ternary gọn gàng
Ewan

6
@Ewan gỡ lỗi một bản nhạc bị hỏng đệ quy sâu có thể là một nỗi đau không cần thiết.
dfri

5
trông gọn gàng trên màn hình mặc dù.
Ewan

Uhm, ngôn ngữ nào cho phép sử dụng các chức năng với casenhãn?
undercat

1
@undercat đó là một ECMAScript / JavaScript afaik hợp lệ
zworek

1

Là một biến thể của câu trả lời của @ Ewan, bạn có thể tạo một chuỗi (thay vì "danh sách phẳng") các điều kiện như sau:

abstract class Condition {
  private static final  Condition LAST = new Condition(){
     public void alertOrPropagate(DisplayInterface display){
        // do nothing;
     }
  }
  private Condition next = Last;

  public Condition setNext(Condition next){
    this.next = next;
    return this; // fluent API
  }

  public void alertOrPropagate(DisplayInterface display){
     if(isConditionMeet()){
         display.alert(getMessage());
     } else {
       next.alertOrPropagate(display);
     }
  }
  protected abstract boolean isConditionMeet();
  protected abstract String getMessage();  
}

Bằng cách này, bạn có thể áp dụng các điều kiện của mình theo thứ tự xác định và cơ sở hạ tầng (lớp trừu tượng được hiển thị) bỏ qua các kiểm tra còn lại sau khi lần đầu tiên được đáp ứng.

Đây là nơi vượt trội so với phương pháp "danh sách phẳng" nơi bạn phải thực hiện "bỏ qua" trong vòng lặp áp dụng các điều kiện.

Bạn chỉ cần thiết lập chuỗi điều kiện:

Condition c1 = new Condition1().setNext(
  new Condition2().setNext(
   new Condition3()
 )
);

Và bắt đầu đánh giá bằng một cuộc gọi đơn giản:

c1.alertOrPropagate(display);

Có, đó được gọi là Mô hình chuỗi trách nhiệm
Tối đa

4
Tôi sẽ không giả vờ nói cho bất kỳ ai khác, nhưng trong khi mã trong câu hỏi ngay lập tức có thể đọc được và rõ ràng trong hành vi của nó, tôi sẽ không coi điều này là rõ ràng ngay lập tức như những gì nó làm.
một CVn

0

Trước hết, mã gốc không phải là IMO khủng khiếp. Điều đó khá dễ hiểu và vốn dĩ không có gì xấu cả.

Sau đó, nếu bạn không thích nó, hãy xây dựng ý tưởng của @ Ewan để sử dụng một danh sách nhưng loại bỏ foreach breakmô hình hơi không tự nhiên của anh ấy :

public class conditions
{
    private List<Condition> cList;
    private int position;

    public Condition Head
    {
        get { return cList[position];}
    }

    public bool Next()
    {
        return (position++ < cList.Count);
    }
}


while not conditions.head.check() {
  conditions.next()
}
conditions.head.alert()

Bây giờ hãy điều chỉnh điều này bằng ngôn ngữ bạn chọn, biến mỗi thành phần của danh sách thành một đối tượng, một tuple, bất cứ điều gì và bạn đều tốt.

EDIT: có vẻ như nó không rõ ràng như tôi nghĩ, vì vậy hãy để tôi giải thích thêm. conditionslà một danh sách sắp xếp của một số loại; headlà yếu tố hiện tại đang được điều tra - lúc đầu, nó là yếu tố đầu tiên của danh sách và mỗi lần next()được gọi nó sẽ trở thành yếu tố sau; check()alert()checkConditionX()addAlert(X)từ OP.


1
(Không downvote nhưng) Tôi không thể làm theo điều này. Đầu là gì?
Belle-Sophie

@Belle Tôi đã chỉnh sửa câu trả lời để giải thích thêm. Đó là ý tưởng tương tự như của Ewan nhưng while notthay vì foreach break.
Nico

Một sự phát triển tuyệt vời của một ý tưởng tuyệt vời
Ewan

0

Câu hỏi thiếu một số chi tiết cụ thể. Nếu các điều kiện là:

  • có thể thay đổi hoặc
  • lặp đi lặp lại trong các phần khác của ứng dụng hoặc hệ thống hoặc
  • sửa đổi trong một số trường hợp nhất định (như các bản dựng khác nhau, thử nghiệm, triển khai)

hoặc nếu nội dung trong addAlertphức tạp hơn, thì một giải pháp có thể tốt hơn có thể nói là c # sẽ là:

//in some central spot
IEnumerable<Tuple<Func<bool>, int>> Conditions = new ... {
  Tuple.Create(CheckCondition1, 1),
  Tuple.Create(CheckCondition2, 2),
  ...
}

//at the original place
var matchingCondition = Conditions.Where(c=>c.Item1()).FirstOrDefault();
if(matchingCondition != null) 
  addAlert(matchingCondition.Item2)

Tuples không quá đẹp trong c # <8, nhưng được chọn để kết hợp.

Ưu điểm của phương pháp này, ngay cả khi không có tùy chọn nào ở trên áp dụng, đó là cấu trúc được gõ tĩnh. Bạn không thể vô tình làm hỏng, nói, thiếu một else.


0

Cách tốt nhất để giảm độ phức tạp Cyclomatic trong trường hợp bạn có nhiều if->then statementslà sử dụng từ điển hoặc danh sách (phụ thuộc ngôn ngữ) để lưu trữ giá trị khóa (nếu giá trị câu lệnh hoặc một số giá trị) và sau đó là kết quả giá trị / hàm.

Ví dụ: thay vì (C #):

if (i > 10) { return "Two"; }
else if (i > 8) { return "Four" }
else if (i > 4) { return "Eight" }
return "Ten";  //etc etc say anything after 3 or 4 values

Tôi có thể đơn giản

var results = new Dictionary<int, string>
{
  { 10, "Two" },
  { 8, "Four"},
  { 4, "Eight"},
  { 0, "Ten"},
}

foreach(var key in results.Keys)
{
  if (i > results[key]) return results.Values[key];
}

Nếu bạn đang sử dụng các ngôn ngữ hiện đại hơn, bạn có thể lưu trữ nhiều logic hơn thì cũng chỉ đơn giản là các giá trị (c #). Đây thực sự chỉ là các hàm nội tuyến, nhưng bạn cũng có thể chỉ đến các chức năng khác nếu logic là để đặt thẳng hàng.

var results = new Dictionary<Func<int, bool>, Func<int, string>>
{
  { (i) => return i > 10; ,
    (i) => return i.ToString() },
  // etc
};

foreach(var key in results.Keys)
{ 
  if (key(i)) return results.Values[key](i);
}

0

Tôi đang cố gắng làm theo các đề xuất mã sạch của chú Bob và đặc biệt để giữ cho các phương thức ngắn.

Tôi thấy mình không thể rút ngắn logic này mặc dù:

if (checkCondition()) {addAlert(1);}
else if (checkCondition2()) {addAlert(2);}
else if (checkCondition3()) {addAlert(3);}
else if (checkCondition4()) {addAlert(4);}

Mã của bạn đã quá ngắn, nhưng bản thân logic không nên thay đổi. Thoạt nhìn, có vẻ như bạn đang lặp lại chính mình với bốn cuộc gọi đến checkCondition()và chỉ rõ ràng là mỗi cuộc gọi sẽ khác nhau sau khi đọc lại mã cẩn thận. Bạn nên thêm định dạng và tên hàm thích hợp, ví dụ:

if (is_an_apple()) {
  addAlert(1);
}
else if (is_a_banana()) {
  addAlert(2);
}
else if (is_a_cat()) {
  addAlert(3);
}
else if (is_a_dog()) {
  addAlert(4);
}

Mã của bạn nên được đọc trên tất cả những thứ khác. Đã đọc một vài cuốn sách của chú Bob, tôi tin rằng đó là thông điệp mà anh ấy luôn cố gắng vượt qua.


0

Giả sử tất cả các hàm được thực hiện trong cùng một thành phần, bạn có thể làm cho các hàm giữ một số trạng thái để thoát khỏi nhiều nhánh trong luồng.

EG: checkCondition1()sẽ trở thành evaluateCondition1(), trên đó nó sẽ kiểm tra nếu điều kiện trước được đáp ứng; nếu vậy, thì nó lưu trữ một số giá trị được lấy bởi getConditionNumber().

checkCondition2()sẽ trở thành evaluateCondition2(), trên đó nó sẽ kiểm tra nếu các điều kiện trước được đáp ứng. Nếu điều kiện trước không được đáp ứng, thì nó sẽ kiểm tra kịch bản điều kiện 2, lưu vào bộ đệm một giá trị cần lấy getConditionNumber(). Và như vậy.

clearConditions();
evaluateCondition1();
evaluateCondition2();
evaluateCondition3();
evaluateCondition4();
if (anyCondition()) { addAlert(getConditionNumber()); }

BIÊN TẬP:

Đây là cách kiểm tra các điều kiện đắt tiền sẽ cần phải được thực hiện để phương pháp này hoạt động.

bool evaluateCondition34() {
    if (!anyCondition() && A && B && C) {
        conditionNumber = 5693;
        return true;
    }
    return false;
}

...

bool evaluateCondition76() {
    if (!anyCondition() && !B && C && D) {
        conditionNumber = 7658;
        return true;
    }
    return false;
}

Do đó, nếu bạn có quá nhiều séc đắt tiền được thực hiện và những thứ trong mã này vẫn ở chế độ riêng tư, phương pháp này giúp duy trì nó, cho phép thay đổi thứ tự của séc nếu cần thiết.

clearConditions();
evaluateCondition10();
evaluateCondition9();
evaluateCondition8();
evaluateCondition7();
...
evaluateCondition34();
...
evaluateCondition76();

if (anyCondition()) { addAlert(getConditionNumber()); }

Câu trả lời này chỉ cung cấp một số gợi ý thay thế từ các câu trả lời khác và có thể sẽ không tốt hơn mã gốc nếu chúng tôi chỉ xem xét 4 dòng mã. Mặc dù, đây không phải là một phương pháp khủng khiếp (và không làm cho bảo trì khó khăn hơn như những người khác đã nói) cho kịch bản tôi đã đề cập (quá nhiều kiểm tra, chỉ có chức năng chính như tiếp xúc với công chúng, tất cả các chức năng chi tiết thi hành cùng lớp).


Tôi không thích đề xuất này - nó ẩn logic thử nghiệm bên trong nhiều chức năng. Điều này có thể làm cho mã khó duy trì nếu, ví dụ, bạn cần thay đổi thứ tự và thực hiện # 3 trước # 2.
Lawrence

Không. Bạn có thể kiểm tra nếu một số điều kiện trước đó được đánh giá nếu anyCondition() != false.
Emerson Cardoso

1
Ok, tôi thấy những gì bạn đang nhận được. Tuy nhiên, nếu (nói) điều kiện 2 và 3 đều đánh giá true, OP không muốn điều kiện 3 được đánh giá.
Lawrence

Ý tôi là bạn có thể kiểm tra anyCondition() != falsetrong các chức năng evaluateConditionXX(). Điều này là có thể thực hiện. Nếu tôi không hiểu cách tiếp cận sử dụng trạng thái nội bộ, nhưng lập luận rằng điều này không hoạt động là không hợp lệ.
Emerson Cardoso

1
Vâng, sự phản đối của tôi là nó vô tình che giấu logic kiểm tra, không phải nó không hoạt động. Trong câu trả lời của bạn (đoạn 3), kiểm tra điều kiện cuộc họp 1 được đặt bên trong eval ... 2 (). Nhưng nếu anh ta chuyển điều kiện 1 và 2 ở cấp cao nhất (do thay đổi yêu cầu của khách hàng, v.v.), bạn sẽ phải chuyển sang ... 2 () để xóa kiểm tra cho điều kiện 1, cộng với đi vào eval. ..1 () để thêm kiểm tra cho điều kiện 2. Điều này có thể được thực hiện để làm việc, nhưng nó có thể dễ dàng dẫn đến các vấn đề với bảo trì.
Lawrence

0

Bất kỳ nhiều hơn hai mệnh đề "khác" buộc người đọc mã phải đi qua toàn bộ chuỗi để tìm ra một điều quan tâm. Sử dụng một phương thức như: void AlertUponCondition (Điều kiện điều kiện) {switch (condition) {case condition.Con1: ... break; trường hợp Điều kiện.Con2: ... nghỉ; vv ...} Trong đó "Điều kiện" là một enum thích hợp. Nếu cần, trả về một bool hoặc giá trị. Gọi nó như thế này: AlertOnCondition (GetCondition ());

Nó thực sự không thể đơn giản hơn, VÀ nó nhanh hơn chuỗi if-if một khi bạn vượt quá một vài trường hợp.


0

Tôi không thể nói cho tình huống cụ thể của bạn vì mã không cụ thể, nhưng ...

mã như thế thường là một mùi cho một mô hình OO thiếu. Bạn thực sự có bốn loại, mỗi loại được liên kết với loại thay thế riêng của nó, nhưng thay vì nhận ra các thực thể này và tạo một thể hiện lớp cho mỗi loại, bạn coi chúng là một thứ và cố gắng bù đắp cho nó sau, tại một thời điểm bạn thực sự cần phải biết những gì bạn đang giải quyết để tiến hành.

Đa hình có thể phù hợp với bạn tốt hơn.

Hãy nghi ngờ về mã với các phương thức dài chứa các cấu trúc if-then dài hoặc phức tạp. Bạn thường muốn một cây lớp ở đó với một số phương thức ảo.

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.