Ocena projektu i umiejętności - Java

0

Cześć, jakiś czas temu napisałem aplikację umożliwiająca identyfikacje inteligentnych kontraktów tworzonych na platformę Ethereum. Aplikacja polega na tym, ze po wrzuceniu kodu bajtowego, użytkownik otrzyma listę najbardziej prawdopodobnych implementacji na podstawie zgromadzonej bazie danych implementacji. Wspomniany kod bajtowy jest uruchamiany przez Ethereum Virtual Machine.

Prosiłbym o ocenę moich umiejętności Javy, Clean Codu itd. w skali 1-6. Bardzo liczę na wszelkie uwagi dotyczące kodu.
https://github.com/peterjot/EthereumSmartContractIdentifier

1

Nie znam się na Springu ani na Ethereum, ale z punktu widzenia Clean Code razi mnie choroba niepotrzebnych else'ów :)

if (functions.size() > 1) {
    throw new IllegalStateException("Expected only one function, but found :" + functions.size());
} else if (functions.size() == 1) {
    return functions.listIterator().next();
}

Czy rzucenie wyjątku nie wyrzuci nas z metody? Ostrożny else sugeruje, że autor woli się zabezpieczyć przed takim egzotycznym wypadkiem, że po throw kod wykonałby się dzielnie dalej.

Podobnie tutaj:

if (nonNull(canonicalType)) {
    return canonicalType;
} else {
    return s;
}

Czy możliwe jest, że wykonałyby się OBIE klauzule return? Jeżeli nie, to pisałbym:

if (nonNull(canonicalType)) {
    return canonicalType;
} 
return s;

Mniej linii i mniej zagnieżdżeń, które niczemu nie służą.

Oczywiście żadnej praktycznej szkody w tym nie ma. Tak samo jak można np. wszędzie stawiać dwa średniki zamiast jednego, i niczego to w funkcjonowaniu kodu nie zmieni. Będzie tylko niepotrzebne :)

2
if (nonNull(canonicalType)) {
    return canonicalType;
} else {
    return s;
}

To raczej się dziś robi jakimś Optional.ofNullable(canonicalType).orElse(s); albo w ogóle nie dopuszcza się że przyjdzie skądś null, tylko wrzuca Optionale.

  1. Test disasma jest mocno słaby, bo w sumie nie mówi "co nie działa". To trzeba by testować jakioś "losowo" albo każdą instrukcje z osobna.
  2. https://github.com/peterjot/EthereumSmartContractIdentifier/blob/master/src/main/java/com/smartcontract/solidity/SolidityPattern.java nie pasuje się języków programowania regexpami. Teraz masz tam takie cuda "write-only" bo jak coś się zacznie źle matchować to szansa że poprawisz wynosi 0%
  3. Tutaj https://github.com/peterjot/EthereumSmartContractIdentifier/blob/master/src/main/java/com/smartcontract/solidity/SolidityParser.java chyba trochę zby złożone metody.

Ale tak ogólnie to na pierwszy rzut oka wygląda spoko.

1

W Pair pola mogłyby być final.

Wątpliwości budzi też u mnie nazewnictwo klas: SolidityController, SolidityFile, SolidityParser, SolidityPattern, SolidityService etc. ad mortem usrandum.

Jeśli praktycznie wszystkie klasy w danym package'u mają jeden i ten sam prefiks, warto siebie zapytać, czy tego wspólnego mianownika nie powinien załatwiać sam package.

Ten często spotykany antywzorzec (modny w środowisku "Java enterprise") jest szerzej znany jako "smurf naming convention" - vide https://devcards.io/smurf-naming-convention

Ogólnie są to sprawy kosmetyczne, bo kod jest dobrej jakości, a na tle arcydzieł przeważnie wrzucanych tu do "Oceny i recenzji", w zasadzie wzorowy.

1

Dziękuję za uwagi ^_^

@V-2:

  1. Faktycznie, jeśli używam throw, czy return, wtedy else jest zbędne i zaciemnia kod.

  2. Co do smurf-nameing-convention. Jeśli pozmieniałbym klasy typu BytecodeService, SolidityService na Service ( ale w jakimś pakiecie ) to muszę potem podawać za każdym razem com.smartcontract.solidity.Service.
    Np. public Service(com.smartcontract.solidity.Service service, Disassembler disassembler) {
    Co więcej, możliwe, że musiałbym tez w ten sposób używać adnotacji ze względu na konflikty nazw np. @org.springframework.stereotype.Service. Myślę, że stąd taka konwencja.

@Shalom

  1. Powodem braków testów dla disasma jest to, że porostu nie wiedziałem jak napisać tutaj sensowne jednostkowe testy. Disassembler wyrzuca błąd tylko jeśli długość jest zła lub wprowadzono znaki inne niż szesnastkowe i tyle.

W ramach testów przepuściłem kilka tysięcy kodów bajtowych w nadziei, że będzie się wywalać i obserwowałem czy nie wykryło zbyt dużej ilości nieznanych opcodów. Jak coś się popsuło to poprawiałem i sprawdzałem ponownie. Jeśli masz wiedzę w jaki sposób mógłbym zrobić testy do tego to chętnie przeczytam.

  1. Patrzyłem jak robione są kompilatory i tam też używano regexów. W jaki sposób mógłbym zastąpić te regexy, np. pisząc zwykły kod z ifami? Myślałem ze to zajmie niepotrzebnie dużo linii kodu.

  2. Zastanawiałem się nad tym, że są zbyt złożone, ale nie bylem przekonany. W takim razie postaram się nad nimi popracować, dzięki za informacje :P

1
szopn napisał(a):

Co do smurf-nameing-convention. Jeśli pozmieniałbym klasy typu BytecodeService, SolidityService na Service ( ale w jakimś pakiecie ) to muszę potem podawać za każdym razem com.smartcontract.solidity.Service.

Np. public Service(com.smartcontract.solidity.Service service, Disassembler disassembler) {
Co więcej, możliwe, że musiałbym tez w ten sposób używać adnotacji ze względu na konflikty nazw np. @org.springframework.stereotype.Service. Myślę, że stąd taka konwencja.

Jeśli zachodzą konflikty nazw, to jasne. Ale pewnie nie w każdym wypadku zachodzą. Zresztą czasem można uniknąć ich nie tyle usuwając generyczny prefiks bez śladu, co zastępując go prefiksem więcej mówiącym o rzeczywistej odpowiedzialności danej klasy. Czyli np. SolidityParser to w takim ujęciu nie Parser - ale SourceCodeParser. (W pakiecie solidity, który definiuje szerszy kontekst całej "rodziny").

Standardowe biblioteki Javy też kierują się taką ekonomiczną zasadą. Np. istnieje java.util.Formatter, ale jest i java.util.logging.Formatter. A nie LoggingFormatter.

Był po angielsku taki dowcip z brodą o sekretarce (albo o sekretarce z brodą, by iść zgodnie z duchem stulecia), co wszystkie dokumenty wpinała do folderu "T". No, bo "the invoices", "the reports" itd. ;) Podobnie wykoncypowali twórcy Windowsa z tą swoją konwencją "My Documents", "My Music", "My Pictures"...

1

Patrzyłem jak robione są kompilatory i tam też używano regexów. W jaki sposób mógłbym zastąpić te regexy, np. pisząc zwykły kod z ifami? Myślałem ze to zajmie niepotrzebnie dużo linii kodu.

To chyba też takie amatorskie "kompilatory" jak twój ;) Generalnie raczej korzysta się z jakiegoś generatora parserów, pisze sie sensowną gramatykę i na tym polega. Bo te twoje regexy już teraz są nie do przeczytania. Wyobraź sobie że składnia trochę się rozszerzy i co wtedy? ;]
Ja bym w ramach testów np. generował zestaw losowych poprawnych opkodów i sprawdził czy się dobrze disasmują a potem zrobił jeszcze testy z wrzucaniem losowych błędów i sprawdzaniem że się wywali.

1 użytkowników online, w tym zalogowanych: 0, gości: 1