Tránh phương pháp quá phức tạp - Độ phức tạp theo chu kỳ


23

Không chắc chắn làm thế nào để đi về phương pháp này để giảm độ phức tạp Cyclomatic. Sonar báo cáo 13 trong khi 10 dự kiến. Tôi chắc chắn không có gì có hại khi rời khỏi phương pháp này, tuy nhiên, chỉ thách thức tôi làm thế nào để tuân theo quy tắc của Sonar. Bất kỳ suy nghĩ sẽ được đánh giá rất cao.

 public static long parseTimeValue(String sValue) {

    if (sValue == null) {
        return 0;
    }

    try {
        long millis;
        if (sValue.endsWith("S")) {
            millis = new ExtractSecond(sValue).invoke();
        } else if (sValue.endsWith("ms")) {
            millis = new ExtractMillisecond(sValue).invoke();
        } else if (sValue.endsWith("s")) {
            millis = new ExtractInSecond(sValue).invoke();
        } else if (sValue.endsWith("m")) {
            millis = new ExtractInMinute(sValue).invoke();
        } else if (sValue.endsWith("H") || sValue.endsWith("h")) {
            millis = new ExtractHour(sValue).invoke();
        } else if (sValue.endsWith("d")) {
            millis = new ExtractDay(sValue).invoke();
        } else if (sValue.endsWith("w")) {
            millis = new ExtractWeek(sValue).invoke();
        } else {
            millis = Long.parseLong(sValue);
        }

        return millis;

    } catch (NumberFormatException e) {
        LOGGER.warn("Number format exception", e);
    }

    return 0;
}

Tất cả các phương thức ExtractXXX được định nghĩa là staticcác lớp bên trong. Ví dụ, như một bên dưới -

    private static class ExtractHour {
      private String sValue;

      public ExtractHour(String sValue) {
         this.sValue = sValue;
      }

      public long invoke() {
         long millis;
         millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 60 * 60 * 1000);
         return millis;
     }
 }

CẬP NHẬT 1

Tôi sẽ ổn định với một loạt các gợi ý ở đây để làm hài lòng anh chàng Sonar. Chắc chắn phòng để cải tiến và đơn giản hóa.

Quả ổi Functionchỉ là một buổi lễ không mong muốn ở đây. Muốn cập nhật câu hỏi về tình trạng hiện tại. Không có gì là cuối cùng ở đây. Hãy đổ suy nghĩ của bạn ..

public class DurationParse {

private static final Logger LOGGER = LoggerFactory.getLogger(DurationParse.class);
private static final Map<String, Function<String, Long>> MULTIPLIERS;
private static final Pattern STRING_REGEX = Pattern.compile("^(\\d+)\\s*(\\w+)");

static {

    MULTIPLIERS = new HashMap<>(7);

    MULTIPLIERS.put("S", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractSecond(input).invoke();
        }
    });

    MULTIPLIERS.put("s", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractInSecond(input).invoke();
        }
    });

    MULTIPLIERS.put("ms", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractMillisecond(input).invoke();
        }
    });

    MULTIPLIERS.put("m", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractInMinute(input).invoke();
        }
    });

    MULTIPLIERS.put("H", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractHour(input).invoke();
        }
    });

    MULTIPLIERS.put("d", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractDay(input).invoke();
        }
    });

    MULTIPLIERS.put("w", new Function<String, Long>() {
        @Nullable
        @Override
        public Long apply(@Nullable String input) {
            return new ExtractWeek(input).invoke();
        }
    });

}

public static long parseTimeValue(String sValue) {

    if (isNullOrEmpty(sValue)) {
        return 0;
    }

    Matcher matcher = STRING_REGEX.matcher(sValue.trim());

    if (!matcher.matches()) {
        LOGGER.warn(String.format("%s is invalid duration, assuming 0ms", sValue));
        return 0;
    }

    if (MULTIPLIERS.get(matcher.group(2)) == null) {
        LOGGER.warn(String.format("%s is invalid configuration, assuming 0ms", sValue));
        return 0;
    }

    return MULTIPLIERS.get(matcher.group(2)).apply(matcher.group(1));
}

private static class ExtractSecond {
    private String sValue;

    public ExtractSecond(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = Long.parseLong(sValue);
        return millis;
    }
}

private static class ExtractMillisecond {
    private String sValue;

    public ExtractMillisecond(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = (long) (Double.parseDouble(sValue));
        return millis;
    }
}

private static class ExtractInSecond {
    private String sValue;

    public ExtractInSecond(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = (long) (Double.parseDouble(sValue) * 1000);
        return millis;
    }
}

private static class ExtractInMinute {
    private String sValue;

    public ExtractInMinute(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = (long) (Double.parseDouble(sValue) * 60 * 1000);
        return millis;
    }
}

private static class ExtractHour {
    private String sValue;

    public ExtractHour(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = (long) (Double.parseDouble(sValue) * 60 * 60 * 1000);
        return millis;
    }
}

private static class ExtractDay {
    private String sValue;

    public ExtractDay(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = (long) (Double.parseDouble(sValue) * 24 * 60 * 60 * 1000);
        return millis;
    }
}

private static class ExtractWeek {
    private String sValue;

    public ExtractWeek(String sValue) {
        this.sValue = sValue;
    }

    public long invoke() {
        long millis;
        millis = (long) (Double.parseDouble(sValue) * 7 * 24 * 60 * 60 * 1000);
        return millis;
    }
}

}


CẬP NHẬT 2

Mặc dù tôi đã thêm bản cập nhật của mình, nhưng nó chỉ đáng để dành thời gian. Tôi sẽ tiếp tục vì Sonar bây giờ không phàn nàn. Đừng lo lắng nhiều và tôi chấp nhận câu trả lời mattnz vì đây là cách để đi và không muốn làm gương xấu cho những người va vào câu hỏi này. Điểm mấu chốt - Đừng quá kỹ sư vì lợi ích của Sonar (hoặc Quản lý dự án Half Baked) phàn nàn về CC. Chỉ cần làm những gì đáng giá một xu cho dự án. Cảm ơn tất cả.


4
Dễ dàng nhất trả lời OO: private static Dictionary<string,Func<string,long>> _mappingStringToParser;Tôi sẽ để phần còn lại làm bài tập cho bạn (hoặc một người có nhiều thời gian rảnh rỗi hơn tôi). Có một API sạch hơn được tìm thấy nếu bạn có bất kỳ sự quen thuộc nào với các trình phân tích cú pháp đơn âm nhưng tôi sẽ không đến đó ngay bây giờ ..
Jimmy Hoffa

Sẽ thích nếu đôi khi bạn có thể sử dụng "trình phân tích cú pháp đơn âm" và cách nó có thể được áp dụng cho chức năng khá nhỏ như chức năng này. Và, đoạn mã này là từ Java.
chờ

làm thế nào ExtractBlahlớp được định nghĩa? Đây là từ một số thư viện hoặc homebrew?
gnat

4
Mã được nối thêm dẫn tôi đi xa hơn một chút để thực hiện đơn giản hơn: Phương sai thực tế của bạn là hệ số nhân. Tạo bản đồ cho các bản đồ này: Kéo các ký tự alpha từ cuối sValue của bạn, sử dụng các ký tự đó làm khóa của bạn và sau đó kéo tất cả cho đến khi các chữ cái từ phía trước cho giá trị mà bạn nhân với bội số được ánh xạ.
Jimmy Hoffa

2
Cập nhật lại: Tôi có phải là người duy nhất có tiếng chuông "Over Engineered" trong đầu không?
mattnz

Câu trả lời:


46

Kỹ thuật phần mềm Trả lời:

Đây chỉ là một trong nhiều trường hợp chỉ đơn giản là đếm những hạt đậu đơn giản để đếm sẽ khiến bạn làm sai. Nó không phải là một chức năng phức tạp, đừng thay đổi nó. Độ phức tạp theo chu kỳ chỉ là một hướng dẫn cho độ phức tạp và bạn đang sử dụng nó kém nếu bạn thay đổi chức năng này dựa trên nó. Nó đơn giản, dễ đọc, có thể bảo trì (hiện tại), nếu nó trở nên lớn hơn trong tương lai, CC sẽ tăng vọt theo cấp số nhân và nó sẽ nhận được sự chú ý khi cần, chứ không phải trước đó.

Minion làm việc cho một công ty đa quốc gia lớn Trả lời:

Các tổ chức có đầy đủ các đội đậu trả tiền, không hiệu quả. Giữ cho quầy đậu hạnh phúc dễ dàng hơn, và chắc chắn khôn ngoan hơn là làm đúng. Bạn cần thay đổi thói quen để giảm CC xuống 10, nhưng hãy thành thật về lý do tại sao bạn làm điều đó - để giữ cho quầy đậu khỏi lưng bạn. Như được đề xuất trong các nhận xét - "trình phân tích cú pháp đơn âm" có thể giúp


14
+1 vì tôn trọng lý do của quy tắc, chứ không phải chính quy tắc
Radu Murzea

5
Nói hay lắm! Tuy nhiên, trong các trường hợp khác khi bạn có CC = 13 với một số mức lồng nhau và logic (phân nhánh) phức tạp, ít nhất nên cố gắng đơn giản hóa nó.
grizwako

16

Cảm ơn @JimmyHoffa, @MichaelT và @ GlenH7 vì sự giúp đỡ của họ!

Con trăn

Trước tiên, bạn thực sự chỉ nên chấp nhận các tiền tố đã biết, đó là 'H' hoặc 'h'. Nếu bạn phải chấp nhận cả hai, bạn nên thực hiện một số thao tác để làm cho nó phù hợp để tiết kiệm phòng trong bản đồ của bạn.

Trong python bạn có thể tạo một từ điển.

EXTRACTION_MAP = {
    'S': ExtractSecond,
    'ms': ExtractMillisecond,
    'm': ExtractMinute,
    'H': ExtractHour,
    'd': ExtractDay,
    'w': ExtractWeek
}

Sau đó, chúng tôi muốn phương thức sử dụng này:

def parseTimeValue(sValue)
    ending = ''.join([i for i in sValue if not i.isdigit()])
    return EXTRACTION_MAP[ending](sValue).invoke()

Nên có độ phức tạp chu kỳ tốt hơn.


Java

Chúng tôi chỉ cần 1 (một) của mỗi số nhân. Hãy đặt chúng vào bản đồ như một số câu trả lời khác đã gợi ý.

Map<String, Float> multipliers = new HashMap<String, Float>();
    map.put("S", 60 * 60);
    map.put("ms", 60 * 60 * 1000);
    map.put("m", 60);
    map.put("H", 1);
    map.put("d", 1.0 / 24);
    map.put("w", 1.0 / (24 * 7));

Sau đó, chúng ta chỉ có thể sử dụng bản đồ để lấy đúng trình chuyển đổi

Pattern foo = Pattern.compile(".*(\\d+)\\s*(\\w+)");
Matcher bar = foo.matcher(sValue);
if(bar.matches()) {
    return (long) (Double.parseDouble(bar.group(1)) * multipliers.get(bar.group(2);
}

Có, ánh xạ các chuỗi thành một yếu tố chuyển đổi sẽ là một giải pháp đơn giản hơn nhiều. Nếu họ không cần các đối tượng thì họ nên loại bỏ chúng, nhưng tôi không thể thấy phần còn lại của chương trình, vì vậy có lẽ các đối tượng đó được sử dụng nhiều hơn như các đối tượng ở nơi khác ...?
Thất vọngWithFormsDesigner

@FrustratedWithFormsDesigner họ có thể, nhưng trong phạm vi của phương thức đó, nó chỉ trả về một đối tượng dài và tức thời rơi ra khỏi phạm vi. Bên cạnh đó, điều này có tác dụng phụ là nếu mã này được gọi thường xuyên hơn, số lượng đối tượng thường xuyên sử dụng, sống ngắn không có trạng thái bị cắt giảm.

Cả hai câu trả lời này đều không thể được coi là cung cấp giải pháp giống như chương trình gốc vì chúng dựa trên các giả định có thể không hợp lệ. Mã Java: Làm thế nào để bạn chắc chắn điều duy nhất mà các phương thức làm là áp dụng một số nhân? Mã Python: Làm thế nào để bạn chắc chắn chuỗi không được phép chứa các ký tự hàng đầu (hoặc điểm giữa) ngoài các chữ số (Ví dụ: "123.456s").
mattnz

@mattnz - có cái nhìn khác về tên biến trong các ví dụ được cung cấp. Rõ ràng là OP đang nhận một đơn vị thời gian dưới dạng một chuỗi và sau đó cần chuyển đổi nó thành một loại đơn vị thời gian khác. Vì vậy, các ví dụ được cung cấp trong câu trả lời này liên quan trực tiếp đến miền của OP. Bỏ qua khía cạnh đó, câu trả lời vẫn cung cấp một cách tiếp cận chung có thể được sử dụng cho một miền khác. Câu trả lời này giải quyết vấn đề được trình bày không phải là vấn đề có thể đã được trình bày.

5
@mattnz - 1) OP không bao giờ chỉ định rằng trong ví dụ của họ và có thể không quan tâm. Làm thế nào để bạn biết các giả định là không hợp lệ? 2) Phương pháp chung vẫn sẽ hoạt động, có khả năng yêu cầu một biểu thức phức tạp hơn. 3) Điểm của câu trả lời là cung cấp một lộ trình khái niệm để giải quyết sự phức tạp theo chu kỳ, và không nhất thiết phải là một câu trả lời cụ thể, có thể biên dịch được. 4) trong khi câu trả lời này bỏ qua khía cạnh rộng hơn của "vấn đề phức tạp", nó gián tiếp trả lời vấn đề bằng cách hiển thị một hình thức thay thế cho mã.

3

Vì dù sao bạn cũng return millisở cuối ifelseifelse khủng khiếp đó, điều đầu tiên bạn nghĩ đến là trả về giá trị ngay lập tức từ bên trong if-blocks. Cách tiếp cận này tuân theo một phương pháp được liệt kê trong danh mục các mẫu tái cấu trúc như Thay thế điều kiện lồng nhau bằng các khoản bảo vệ .

Một phương thức có hành vi có điều kiện không làm rõ đường dẫn thực thi thông thường là gì

Sử dụng các điều khoản bảo vệ cho tất cả các trường hợp đặc biệt

Nó sẽ giúp bạn thoát khỏi những thứ khác, làm phẳng mã và làm cho Sonar hạnh phúc:

    if (sValue.endsWith("S")) {
        return new ExtractSecond(sValue).invoke();
    } // no need in else after return, code flattened

    if (sValue.endsWith("ms")) {
        return new ExtractMillisecond(sValue).invoke();
    }

    // and so on...
    return Long.parseLong(sValue); // forget millis, these aren't needed anymore

Một điều đáng xem xét là bỏ khối thử bắt. Điều này cũng sẽ làm giảm độ phức tạp theo chu kỳ, nhưng lý do chính tại sao tôi khuyên bạn là vì với khối này, không có cách nào để mã người gọi phân biệt được phân tách hợp pháp 0 với ngoại lệ định dạng số.

Trừ khi bạn chắc chắn 200% rằng trả về 0 cho lỗi phân tích cú pháp là những gì mã người gọi cần, tốt hơn hết bạn nên tuyên truyền ngoại lệ đó và để mã người gọi quyết định cách xử lý. Thông thường sẽ thuận tiện hơn khi quyết định người gọi có nên hủy bỏ việc thực hiện hoặc thử lại nhận đầu vào hay quay lại một số giá trị mặc định như 0 hoặc -1 hoặc bất cứ điều gì.


Đoạn mã của bạn cho một ví dụ ExtractHour khiến tôi cảm thấy rằng chức năng ExtractXXX được thiết kế theo cách không tối ưu. Tôi đặt cược mỗi của các lớp còn lại không suy nghĩ lặp đi lặp lại cùng parseDoublesubstring, và nhân thứ như 60 và 1000 hơn và hơn và hơn nữa.

Điều này là do bạn đã bỏ lỡ bản chất của những gì cần phải thực hiện tùy thuộc vào sValue- cụ thể là, nó xác định số lượng ký tự cần cắt từ cuối chuỗi và giá trị nhân sẽ là bao nhiêu. Nếu bạn thiết kế đối tượng "cốt lõi" của mình xung quanh các tính năng cần thiết này, nó sẽ trông như sau:

private static class ParseHelper {
    // three things you need to know to parse:
    final String source;
    final int charsToCutAtEnd;
    final long multiplier;

    ParseHelper(String source, int charsToCutAtEnd, long multiplier) {
        this.source = source == null ? "0" : source; // let's handle null here
        this.charsToCutAtEnd = charsToCutAtEnd;
        this.multiplier = multiplier;
    }

    long invoke() {
        // NOTE consider Long.parseLong instead of Double.parseDouble here
        return (long) (Double.parseDouble(cutAtEnd()) * multiplier);
    }

    private String cutAtEnd() {
        if (charsToCutAtEnd == 0) {
            return source;
        }
        // write code that cuts 'charsToCutAtEnd' from the end of the 'source'
        throw new UnsupportedOperationException();
    }
}

Sau đó, bạn cần một mã cấu hình các đối tượng trên cho mỗi điều kiện cụ thể nếu nó được đáp ứng hoặc bằng cách nào đó "bỏ qua" nếu không. Điều này có thể được thực hiện như sau:

private ParseHelper setupIfInSecond(ParseHelper original) {
    final String sValue = original.source;
    return sValue.endsWith("s") && !sValue.endsWith("ms")
            ? new ParseHelper(sValue, 1, 1000)
            :  original; // bypass
}

private ParseHelper setupIfMillisecond(ParseHelper original) {
    final String sValue = original.source;
    return sValue.endsWith("ms")
            ? new ParseHelper(sValue, 2, 1)
            : original; // bypass
}

// and so on...

Dựa trên các khối xây dựng ở trên , mã phương thức của bạn có thể trông như sau:

public long parseTimeValue(String sValue) {

   return setupIfSecond(
           setupIfMillisecond(
           setupIfInSecond(
           setupIfInMinute(
           setupIfHour(
           setupIfDay(
           setupIfWeek(
           new ParseHelper(sValue, 0, 1))))))))
           .invoke();
}

Bạn thấy đấy, không có sự phức tạp nào còn sót lại, không có dấu ngoặc nhọn nào trong phương thức cả (cũng không có nhiều trả về như trong đề xuất lực lượng vũ phu ban đầu của tôi về mã làm phẳng). Bạn chỉ cần kiểm tra tuần tự đầu vào và điều chỉnh xử lý khi cần thiết.


1

Nếu bạn thực sự muốn cấu trúc lại nó, bạn có thể làm một cái gì đó như thế này:

// All of your Extract... classes will have to implement this interface!
public Interface TimeExtractor
{
    public long invoke();
}

private static class ExtractHour implements TimeExtractor
{
  private String sValue;


  /*Not sure what this was for - might not be necessary now
  public ExtractHour(String sValue)
  {
     this.sValue = sValue;
  }*/

  public long invoke(String s)
  {
     this.sValue = s;
     long millis;
     millis = (long) (Double.parseDouble(sValue.substring(0, sValue.length() - 1)) * 60 * 60 * 1000);
     return millis;
 }
}

private static HashMap<String, TimeExtractor> extractorMap= new HashMap<String, TimeExtractor>();

private void someInitMethod()
{
   ExtractHour eh = new ExtractorHour;
   extractorMap.add("H",eh);
   /*repeat for all extractors */
}

public static long parseTimeValue(String sValue)
{
    if (sValue == null)
    {
        return 0;
    }
    String key = extractKeyFromSValue(sValue);
    long millis;
    TimeExtractor extractor = extractorMap.get(key);
    if (extractor!=null)
    {
      try
      {
         millis= extractor.invoke(sValue);
      }
        catch (NumberFormatException e)
      {
          LOGGER.warn("Number format exception", e);
      }
    }
    else
       LOGGER.error("NO EXTRACTOR FOUND FOR "+key+", with sValue: "+sValue);

    return millis;
}

Ý tưởng là bạn có một bản đồ các khóa (những gì bạn sử dụng trong "endWith" mọi lúc) ánh xạ tới các đối tượng cụ thể thực hiện việc xử lý mà bạn muốn.

Ở đây hơi khó khăn một chút nhưng tôi hy vọng nó đủ rõ ràng. Tôi đã không điền vào các chi tiết extractKeyFromSValue()vì tôi chỉ không biết đủ các chuỗi này là gì để làm điều đó đúng. Có vẻ như đó là 1 hoặc 2 ký tự không phải là số cuối cùng (một regex có thể trích xuất nó đủ dễ dàng, có thể .*([a-zA-Z]{1,2})$sẽ hoạt động), nhưng tôi không chắc chắn 100% ...


Câu trả lời gốc:

Bạn có thể thay đổi

else if (sValue.endsWith("H") || sValue.endsWith("h")) {

đến

else if (sValue.toUpper().endsWith("H")) {

Điều đó có thể giúp bạn tiết kiệm một chút, nhưng thành thật mà nói, tôi sẽ không lo lắng về điều đó quá nhiều. Tôi đồng ý với bạn rằng tôi không nghĩ rằng có nhiều tác hại khi rời khỏi phương pháp như vậy. Thay vì cố gắng "tuân theo các quy tắc của Sonar", hãy cố gắng "theo sát các hướng dẫn của Sonar, càng nhiều càng tốt".

Bạn có thể khiến mình phát điên khi cố gắng tuân theo mọi quy tắc duy nhất mà các công cụ phân tích này sẽ có trong đó, nhưng bạn cũng phải quyết định xem các quy tắc đó có hợp lý với dự án của bạn hay không và đối với các trường hợp cụ thể trong đó thời gian tái cấu trúc có thể không xứng đáng .


3
Không sử dụng nhiều, sonar vẫn phàn nàn. Tôi là loại thử nó cho vui, ít nhất là cho phép học một hoặc hai. Mã được chuyển sang dàn mặc dù.
async chờ

@asyncwait: Ah, tôi nghĩ rằng bạn đang xem báo cáo này từ sonar nghiêm trọng hơn thế. Vâng, sự thay đổi mà tôi đề xuất sẽ không tạo ra sự khác biệt lớn - tôi không nghĩ rằng nó sẽ đưa bạn từ 13 đến 10, nhưng trong một số công cụ tôi đã thấy loại điều này tạo ra sự khác biệt đáng chú ý.
Thất vọngWithFormsDesigner

Chỉ cần thêm một nhánh IF khác đã tăng CC lên +1.
chờ

0

Bạn có thể cân nhắc sử dụng một enum để lưu trữ tất cả các trường hợp và vị từ có sẵn cho các giá trị khớp. Như đã đề cập trước khi chức năng của bạn đủ dễ đọc chỉ để nó không thay đổi. Những số liệu đó là có để giúp bạn không phải cách khác.

//utility class for matching values
private static class ValueMatchingPredicate implements Predicate<String>{
    private final String[] suffixes;

    public ValueMatchingPredicate(String[] suffixes) {      
        this.suffixes = suffixes;
    }

    public boolean apply(String sValue) {
        if(sValue == null) return false;

        for (String suffix : suffixes) {
            if(sValue.endsWith(suffix)) return true;
        }

        return false;
    }

    public static Predicate<String> withSuffix(String... suffixes){         
        return new ValueMatchingPredicate(suffixes);
    }       
}

//enum containing all possible options
private static enum TimeValueExtractor {                
    SECOND(
        ValueMatchingPredicate.withSuffix("S"), 
        new Function<String, Long>(){ 
            public Long apply(String sValue) {  return new ExtractSecond(sValue).invoke(); }
        }),

    MILISECOND(
        ValueMatchingPredicate.withSuffix("ms"), 
        new Function<String, Long>(){
            public Long apply(String sValue) { return new ExtractMillisecond(sValue).invoke(); }
        }),

    IN_SECOND(
        ValueMatchingPredicate.withSuffix("s"),
        new Function<String, Long>(){
            public Long apply(String sValue) { return new ExtractInSecond(sValue).invoke(); }
        }),

    IN_MINUTE(
        ValueMatchingPredicate.withSuffix("m"),
        new Function<String, Long>(){
            public Long apply(String sValue) {  return new ExtractInMinute(sValue).invoke(); }
        }),

    HOUR(
        ValueMatchingPredicate.withSuffix("H", "h"),
        new Function<String, Long>(){
            public Long apply(String sValue) {  return new ExtractHour(sValue).invoke(); }
        }),

    DAY(
        ValueMatchingPredicate.withSuffix("d"),
        new Function<String, Long>(){
            public Long apply(String sValue) {  return new ExtractDay(sValue).invoke(); }
        }),

    WEEK(
        ValueMatchingPredicate.withSuffix("w"),
        new Function<String, Long>(){
            public Long apply(String sValue) {  return new ExtractWeek(sValue).invoke(); }
        });

    private final Predicate<String>      valueMatchingRule;
    private final Function<String, Long> extractorFunction;

    public static Long DEFAULT_VALUE = 0L;

    private TimeValueExtractor(Predicate<String> valueMatchingRule, Function<String, Long> extractorFunction) {
        this.valueMatchingRule = valueMatchingRule;
        this.extractorFunction = extractorFunction;
    }

    public boolean matchesValueSuffix(String sValue){
        return this.valueMatchingRule.apply(sValue);
    }

    public Long extractTimeValue(String sValue){
        return this.extractorFunction.apply(sValue);
    }

    public static Long extract(String sValue) throws NumberFormatException{
        TimeValueExtractor[] extractors = TimeValueExtractor.values();

        for (TimeValueExtractor timeValueExtractor : extractors) {
            if(timeValueExtractor.matchesValueSuffix(sValue)){
                return timeValueExtractor.extractTimeValue(sValue);
            }
        }

        return DEFAULT_VALUE;
    }
}

//your function
public static long parseTimeValue(String sValue){
    try{
        return TimeValueExtractor.extract(sValue);
    } catch (NumberFormatException e) {
        //LOGGER.warn("Number format exception", e);
        return TimeValueExtractor.DEFAULT_VALUE;
    }
}

0

Liên quan đến bình luận của bạn về:

Điểm mấu chốt - Đừng quá kỹ sư vì lợi ích của Sonar (hoặc Quản lý dự án Half Baked) phàn nàn về CC. Chỉ cần làm những gì đáng giá một xu cho dự án.

Một lựa chọn khác để xem xét là thay đổi tiêu chuẩn mã hóa của nhóm của bạn cho các tình huống như thế này. Có lẽ bạn có thể thêm vào một số loại bỏ phiếu nhóm để cung cấp một biện pháp quản trị và tránh các tình huống cắt ngắn.

Nhưng thay đổi tiêu chuẩn của đội để đáp ứng với các tình huống không có ý nghĩa là dấu hiệu của một đội tốt với thái độ đúng đắn về tiêu chuẩn. Các tiêu chuẩn là có để giúp nhóm, không có cách viết mã.


0

Thành thật mà nói, tất cả các phản ứng kỹ thuật ở trên có vẻ phức tạp khủng khiếp cho nhiệm vụ trong tay. Như đã được viết, bản thân mã sạch và tốt, vì vậy tôi sẽ chọn thay đổi nhỏ nhất có thể để đáp ứng bộ đếm phức tạp. Làm thế nào về các refactor sau:

public static long parseTimeValue(String sValue) {

    if (sValue == null) {
        return 0;
    }

    try {
        return getMillis(sValue);
    } catch (NumberFormatException e) {
        LOGGER.warn("Number format exception", e);
    }

    return 0;
}

private static long getMillis(String sValue) {
    if (sValue.endsWith("S")) {
        return new ExtractSecond(sValue).invoke();
    } else if (sValue.endsWith("ms")) {
        return new ExtractMillisecond(sValue).invoke();
    } else if (sValue.endsWith("s")) {
        return new ExtractInSecond(sValue).invoke();
    } else if (sValue.endsWith("m")) {
        return new ExtractInMinute(sValue).invoke();
    } else if (sValue.endsWith("H") || sValue.endsWith("h")) {
        return new ExtractHour(sValue).invoke();
    } else if (sValue.endsWith("d")) {
        return new ExtractDay(sValue).invoke();
    } else if (sValue.endsWith("w")) {
        return new ExtractWeek(sValue).invoke();
    } else {
        return Long.parseLong(sValue);
    }
}

Nếu tôi đếm chính xác, hàm trích xuất sẽ có độ phức tạp là 9, vẫn vượt qua các yêu cầu. Và về cơ bản nó giống như mã trước đây , đó là một điều tốt, vì mã này rất tốt để bắt đầu.

Thêm vào đó, độc giả của Clean Code có thể thích thú với thực tế là phương pháp cấp cao nhất hiện nay đơn giản và ngắn gọn, trong khi phương thức được trích xuất liên quan đến các chi tiế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.