Çok karmaşık yöntemlerden kaçının - Siklomatik Karmaşıklık


23

Siklomatik Karmaşıklığı azaltmak için bu yöntemin nasıl uygulanacağından emin değilim. Sonar 13 rapor ederken, 10 olması bekleniyor. Bu yöntemi olduğu gibi bırakmanın hiçbir zararı olmadığından eminim, ancak Sonar'ın kurallarına uyma konusunda bana meydan okuyor. Herhangi bir düşünce çok takdir edilecektir.

 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üm ExtractXXX yöntemleri staticiç sınıflar olarak tanımlanır . Örneğin, aşağıdakilerden biri gibi -

    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;
     }
 }

GÜNCELLEME 1

Sonar'ı tatmin etmek için burada bir dizi öneri ile anlaşacağım. İyileştirmeler ve basitleştirme için kesinlikle yer var.

Guava Functionburada sadece istenmeyen bir törendir. Mevcut durumla ilgili soruyu güncellemek istedi. Burada hiçbir şey kesin değil. Düşüncelerinizi dökün lütfen ..

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;
    }
}

}


GÜNCELLEME 2

Güncellememi eklememe rağmen, bu sadece zamana değiyor. Sonar şimdi şikayet etmediğinden devam edeceğim. Endişelenmeyin ve ben de bu sorunun üstesinden gelenler için kötü bir örnek teşkil etmek istemediğim ve gideceğimiz yol olarak mattnz cevabını kabul ediyorum. Alt satır - Sonar (ya da Yarı Pişmiş Proje Yöneticisi) adına uğruna mühendisliği yapma, CC hakkında şikayetçi. Sadece proje için bir kuruşa değer ne yaparsan yap. Herkese teşekkürler.


4
En kolay OO cevapsız cevap private static Dictionary<string,Func<string,long>> _mappingStringToParser;veriyor : Gerisini sizin için bir egzersiz olarak bırakacağım (veya şu anda benden daha fazla boş zamanı olan biri). .. Orada monadic ayrıştırıcıları ile yakınlıkları varsa bulunacak bir temizleyici API var ama şu anda oraya gitmeyecek
Jimmy Hoffa

Bir zamanlar "monadik ayrıştırıcılar" a bir zaman ayırabilirseniz ve bunun gibi oldukça küçük bir fonksiyona nasıl uygulanabileceğini çok isterim. Ve bu kod parçası Java’dan.
asyncwait

ExtractBlahsınıflar nasıl tanımlanır? Bunlar bazı kütüphanelerden mi yoksa homebrew'den mi?
tatarcık

4
Eklenen kod beni daha basit bir uygulamaya doğru biraz daha ileri götürür: Gerçek farkınız çarpandır. Bunların bir haritasını oluşturun: sValue'nuzun sonundan alfa karakterleri çekin, bunları anahtarınız olarak kullanın ve sonra eşlenen çarpanı ile çoğalttığınız değer için alfalar önden sonuna kadar çekin.
Jimmy Hoffa

2
Re Update: Kafamda "Over Engineered" çalan tek kişi ben miyim?
mattnz

Yanıtlar:


46

Yazılım Mühendisliği Cevap:

Bu, basitçe sayılması kolay olan fasulyeleri saymanın yanlış şeyi yapmanıza neden olacağı birçok durumdan sadece bir tanesidir. Bu karmaşık bir fonksiyon değildir, değiştirmeyin. Siklomatik Karmaşıklık yalnızca karmaşıklığa bir rehberdir ve bu işlevi temel alarak değiştirirseniz zayıf kullanırsınız. Basit, okunabilir, bakımı kolay (şimdilik), gelecekte daha da büyürse, CC katlanarak fırlayacak ve daha önce ihtiyaç duymadan ihtiyaç duyduğu zaman ihtiyaç duyduğu dikkati alacaktır.

Büyük Çokuluslu Bir Şirket Cevabı için çalışan Minion:

Organizasyonlar fazladan ödemeli, verimsiz fasulye sayacı ekipleriyle doludur. Fasulye sayaçlarını mutlu etmek, doğru olanı yapmaktan daha kolay ve kesinlikle daha akıllıcadır. CC'yi 10'a düşürmek için rutini değiştirmeniz gerekir, ancak bunu neden yaptığınız konusunda dürüst olun; fasulye sayaçlarını sırtınızdan uzak tutun. Yorumlarda önerildiği gibi - "monadik ayrıştırıcılar" yardımcı olabilir


14
Kuralın nedenine saygı
duyduğum

5
İyi dedi! Bununla birlikte, CC = 13'ün birkaç yuva seviyesine ve karmaşık (dallanma) mantığa sahip olduğu diğer durumlarda, en azından basitleştirmeyi denemek tercih edilir.
grizwako

16

@JimmyHoffa, @MichaelT ve @ GlenH7'ye yardımları için teşekkürler!

piton

İlk önce, gerçekten sadece bilinen önekleri kabul etmelisiniz, yani 'H' veya 'h'. Eğer varsa sahip hem kabul etmek, kendi haritasında oda kaydetmek uyumlu olmasını sağlamak için bazı işlemi gerçekleştirmek gerekir.

Python'da bir sözlük oluşturabilirsiniz.

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

Sonra bu yöntemi kullanmak istiyoruz:

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

Daha iyi bir döngüsel karmaşıklığa sahip olmalı.


Java

Her çarpandan sadece 1 tanesine ihtiyacımız var. Diğer bazı cevapların önerdiği gibi onları bir haritaya koyalım.

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));

O zaman haritayı doğru dönüştürücüyü almak için kullanabiliriz.

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);
}

Evet, dizeleri bir dönüşüm faktörü ile eşleştirmek çok daha basit bir çözüm olacaktır. Nesnelere ihtiyaçları yoksa , o zaman onlardan kurtulmaları gerekir, ancak programın geri kalanını göremiyorum, bu yüzden belki de nesneler başka bir yerde nesneler olarak kullanılıyor ...?
SinirliFormsDesigner ile

@FrustratedWithFormsDesigner onlar yapabilir, ancak bu yöntem kapsamında, sadece uzun bir dönüş ve somutlaştırılmış nesne kapsam dışına düşer. Bunun yanı sıra, bu kodun daha sık çağrılması durumunda, sık kullanılan, kısa ömürlü ve durumsuz nesnelerin sayısının azaltılması gibi yan etkisi de vardır.

Bu cevapların hiçbirinin, geçerli olmayan varsayımlara dayanan Orijinal program ile aynı çözümü sağladığı düşünülemez. Java Kodu: Yöntemlerin tek çarpanı uygulayacağından nasıl emin olabilirsiniz? Python Kodu: Dizginin rakamlardan başka baş harfleri (veya orta nokta) içermesine izin verilmediğinden nasıl eminsiniz (Örn. "123.456s").
mattnz

@ mattnz - sağlanan örnekler içindeki değişken isimlerine başka bir göz atın. OP'nin bir zaman birimi dizge olarak aldığı ve daha sonra onu başka bir zaman birimine dönüştürmesi gerektiği açıktır. Dolayısıyla bu cevapta verilen örnekler doğrudan OP'nin alanına aittir. Bu yönü gözardı ederek, cevap hala farklı bir alan için kullanılabilecek genel bir yaklaşım sunar. Bu çözüm değil problem sunuldu sorunu çözer olabilir sunulmuştur.

5
@ mattnz - 1) OP hiçbir zaman kendi örneğinde bunu belirtmez ve umursamayabilir. Varsayımların geçersiz olduğunu nereden biliyorsunuz? 2) Genel yöntem hala işe yarayacak ve potansiyel olarak daha karmaşık bir regex gerektirecektir. 3) Cevabın amacı, konjonktürel karmaşıklığı çözmek için kavramsal bir yol sağlamaktır, illa ki kesin ve kesin bir cevap değildir. 4) bu cevap, "karmaşıklık önemli değil" nin geniş yönünü görmezden gelse de, konuyu dolaylı olarak kod için alternatif bir form göstererek yanıtlar.

3

Eğer bu yana return milliso korkunç sonunda ifelseifelse akla gelen her neyse, ilk iş eğer-blok içinde hemen değerini döndürmektir. Bu yaklaşım, yeniden yerleştirme kalıpları kataloğunda, İç İçe Koşullu Koşulları Koruma Cümleleriyle Değiştir olarak listelenen birini izlemektedir .

Bir yöntem, normal yürütme yolunun ne olduğunu açıkça belirtmeyen koşullu davranışa sahiptir.

Tüm özel durumlar için Guard Cümleleri kullanın.

Başkalarından kurtulmanıza, kodu düzleştirmenize ve Sonar'ı mutlu etmenize yardımcı olacaktır:

    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

Göz önünde bulundurmaya değer başka bir şey, try-catch bloğunu düşürmektir. Bu aynı zamanda siklomatik karmaşıklığı da azaltacaktır, fakat tavsiye etmemin temel nedeni, bu blokta, arayan kodunun yasal olarak ayrıştırılmış 0'ı sayı format istisnasından ayırt etmesinin mümkün olmamasıdır.

Ayrıştırma hataları için 0 döndürmenin% 200 emin olmadıkça, arayan kodunun ihtiyaç duyduğu şey olduğunda, bu istisnayı daha iyi yaymanız ve arayan kodunun bununla nasıl başa çıkacağına karar vermesine izin vermeniz iyi olur. Arayanda yürütmeyi iptal edip etmemek veya tekrar giriş yapmak isteyip istemediğinize karar vermek veya 0 veya -1 veya bazı değerler gibi varsayılan değerlere geri dönülmesine karar vermek genellikle daha uygundur.


Örneğin ExtractHour kod snippet'iniz, ExtractXXX işlevselliğinin en iyi şekilde tasarlanmadığını hissettiriyor. Kalan sınıfların hepsinin düşüncesizce aynı olduğunu tekrarlıyorparseDouble ve substring60 ve 1000 gibi şeyleri tekrar tekrar tekrar ediyorum .

Bunun nedeni , neye bağlı olarak yapılması gerekenlerin özünü özlemiş olmanızdır sValue- yani, dizenin sonundan ne kadar karakter kesileceğini ve çarpan değeri ne olacağını tanımlar. "Çekirdek" nesnesini bu temel özelliklerin etrafında tasarlarsanız, aşağıdaki gibi görünür:

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();
    }
}

Ondan sonra, karşılanırsa belirli koşul başına nesnelerin üzerinde konfigüre eden bir koda ihtiyacınız var, ya da bir şekilde aksi takdirde "atlar". Bu aşağıdaki gibi yapılabilir:

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...

Yukarıdaki yapı taşlarına dayanarak, yönteminizin kodu aşağıdaki gibi görünebilir:

public long parseTimeValue(String sValue) {

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

Görüyorsunuz, geriye kalan hiçbir karmaşıklık yok, yöntemin içinde hiç küme parantezi yok (ya da düzleştirme kodundaki orijinal kaba kuvvet önerimdeki gibi birden fazla geri dönüş ). Sadece girişi kontrol edip işlemi gerektiği gibi ayarlayabilirsiniz.


1

Gerçekten canlandırmak istiyorsan, böyle bir şey yapabilirsin:

// 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;
}

Buradaki fikir, istediğiniz işlemi yapan belirli nesnelerle eşleştiren bir anahtar haritasına (her zaman "biter" ile ne kullandığınızı) sahip olmanızdır.

Burası biraz zor ama umarım yeterince açıktır. Ayrıntılarını doldurmadım, extractKeyFromSValue()çünkü bu dizgilerin düzgün şekilde yapmaları için ne olduğunu yeterince bilmiyorum. Sayısal olmayan son 1 veya 2 karakter gibi görünüyor (bir regex muhtemelen yeterince kolay çıkartabilir, belki .*([a-zA-Z]{1,2})$de işe yarar), ancak% 100 emin değilim ...


Orijinal cevap:

Değişebilirsin

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

için

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

Bu sizi biraz kurtarabilir, ama dürüst olmak gerekirse, çok fazla endişelenmem. Yöntemi olduğu gibi bırakmanın çok fazla zararı olmadığını düşündüğüme katılıyorum. "Sonar'ın kurallarına uymaya" çalışmak yerine "Sonar'ın kurallarına, makul derecede mümkün olduğunca yakın" olmaya çalışın.

Bu analiz araçlarının içerdiği her kurala uymaya çalışırken kendinizi sinirlendirebilir, ancak kuralların projeniz için ve yeniden düzenlemeye harcanan zamanın buna değmeyeceği belirli durumlar için anlamlı olup olmadığına karar vermeniz gerekir. .


3
Fazla bir kullanım değil, sonar hala şikayet ediyor. Biraz eğlenmek için deniyorum, en azından bir veya iki tane öğrenmeye izin veriyor. Kod olsa evrelemeye taşınır.
asyncwait

@ asyncwait: Ah, bu raporu sonardan daha ciddiye aldığını sanıyordum. Evet, önerdiğim değişiklik çok büyük bir fark yaratmaz - 13'ten 10'a kadar süreceğini düşünmüyorum, ancak bazı araçlarda bu tür şeylerin biraz göze çarpan bir fark yarattığını gördüm.
SinirliFormsDesigner'la

Sadece başka bir IF dalı eklemek, CC'yi + 1'e yükseltti.
asyncwait

0

Kullanılabilir tüm durumlarınızı saklamak için bir enum kullanmayı ve eşleşen değerler için öngörüleri düşünebilirsiniz. Daha önce de belirtildiği gibi, işleviniz değişmeden bırakılacak kadar okunabilir. Bu ölçütler, tam tersi olmasa da sana yardım etmek için var.

//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

Yorumunuzla ilgili:

Alt satır - Sonar (ya da Yarı Pişmiş Proje Yöneticisi) adına uğruna mühendisliği yapma, CC hakkında şikayetçi. Sadece proje için bir kuruşa değer ne yaparsan yap.

Dikkate alınması gereken bir başka seçenek de, bu gibi durumlar için ekibinizin kodlama standartlarını değiştirmektir. Belki de bir yönetişim ölçütü sağlamak ve kestirme durumlardan kaçınmak için bir çeşit takım oyu ekleyebilirsiniz.

Ancak, takımın standartlarının anlam ifade etmeyen durumlara yanıt olarak değiştirilmesi, standartlarla ilgili doğru tutuma sahip iyi bir takımın işaretidir. Standartlar, ekibe girmek için değil, takıma yardımcı olmak için var.


0

Dürüst olmak gerekirse, yukarıdaki tüm teknik tepkiler eldeki görev için oldukça karmaşık görünmektedir. Daha önce yazıldığı gibi, kodun kendisi temiz ve iyi, bu yüzden karmaşıklık sayacını karşılamak için mümkün olan en küçük değişikliği tercih ediyorum. Aşağıdaki refactor hakkında nasıl:

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);
    }
}

Doğru sayıyorsam, çıkarılan işlev 9, yine de gereksinimleri geçen bir karmaşıklığa sahip olmalıdır. Ve temelde eskisi gibi aynı , bu iyi bir şey, çünkü kod başlangıçta iyiydi.

Ayrıca, Temiz Kod okuyucuları, üst düzey yöntemin şimdi basit ve kısa olmasına rağmen elde edilen ayrıntılarla ilgileniyor olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.