Tool methods: static vs DI

Tool methods: static vs DI
0

Witam,
Zastanawia mnie czy używanie metod statycznych dla drobnych metod toolowych jak np. specjalistyczne Spring Utils (generalnie pisanie własnego String Utils jest bez sensu, bo to już zostało napisane, jednak chodzi tylko o sam przykład).

Zastanawiam się nad dwoma podejściami:
a) dependency injection

Kopiuj
@Inject
private SpringUtils sprUtils;

void metoda(String a) {
    String result = sprUtils.toolMethod(a);
    // pozostalt kod
}

b) metoda statyczna

Kopiuj
void metoda(String a) {
    String result = SpringUtils .toolMethod(a);
    // pozostalt kod
}

Które podejście jest lepsze i dlaczego? Jedno i drugie mogę łatwo testować w przypadku prostych toolkitów. Jeżeli nie mam rzeczywistego obiektu, który przechowuje stan (tylko np. zwracam przerobiony String nie mam raczej też ryzyka wyścigów w przypadku dostępu przez wiele wątków, bo nie mam stanu).

Pozdrawiam,

Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
0

A jak będziesz ten swój toolkit testował? Są co prawda cuda takie jak PowerMock który potrafi mockować statici, ale generalnie jest to trudniejsze niż testowanie zwykłych klas i obiektów.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
0

Dopiero wchodzę w temat testów. Generalnie z samym toolkitem problemu nie ma, zwykłe asserty jak najabardziej działają. A dane w metodzie statycznej nie działają na obiekcie.

Wspominasz o mockowaniu metody statycznej. Zastanawia mnie czy to ma jakikolwiek sens w przypadki mojego toolkitu, ponieważ metody w nim zawarte są bardzo proste. I nie odwołują się do żadnych danych kontekstowych lub bazy danych: czysta logika, niezwiązana z obiektem.

Wydaje mi się, że prawidłowa odpowiedź to: metody statyczne są w porządku póki ich testowanie jest łatwe.

airborn
  • Rejestracja:ponad 15 lat
  • Ostatnio:prawie 7 lat
  • Postów:274
1

Czysta logika to jest właśnie to co najczęściej powoduje jakieś błędy ;) Nawet w prostej sytuacji, można zrobić jakiś trywialny błąd typu przesunięcie o jeden albo jakaś nadmiarowa spacja czy cokolwiek innego. Przetestowanie statycznego toolkitu nie stanowi problemu, ale problem stanowi przygotowanie testu tego obiektu w którym tego statica wykorzystasz. Testy powinny być odseparowane, a u Ciebie błąd w tym statycznym toolkicie spowoduje propagację błędu do wszystkich miejsc gdzie jest on wykorzystywany.

0

Dziękuję za wytłumaczenie. Po wstrzyknięciu obiektu np. przez CDI równie łatwo go używać jak statica. A problem nie występuje. Będę więc testował z użyciem Dependency Injection.

airborn
  • Rejestracja:ponad 15 lat
  • Ostatnio:prawie 7 lat
  • Postów:274
0

To nawet nie jest kwestia wstrzykiwania przez CDI czy jakikolwiek inny magiczny mechanizm. Nawet gdybyś musiał ręcznie stworzyć obiekt i przekazać go do konstruktora to warto było by zastosować takie podejście. Uzyskujesz dzięki temu odseparowane komponenty które w razie czego można np. podmienić. Jeżeli masz statyczną metodę, to Twoje komponenty są silnie sprzężone ze sobą i nie jesteś w stanie nic z tym zrobić bez refactoringu.

0

Dzięki.

0

Oj, widzę, że się wytworzyło środowisko antystaticowe ;)

Generalnie statyczne utilities wszelkiego rodzaju są OK, pod warunkiem że to rzeczywiście utilities.

Oto dlaczego:
Charakterystyczne cechy metod utilsowych:
a) niewielka złożoność
b) bardzo rzadko się zmieniają
c) pisane są "na bieżąco", wraz z implementacją bardziej skomplikowanej logiki

Jeśli w projekcie jest jakiś mechanizm DI to można się jeszcze kłócić - kwestia gustu, czy ktoś woli coś takiego:

Kopiuj
public class BusinessServiceImpl implements BusinessService {
	
	@Inject
	private UtilsA utilsA;

	@Inject
	private UtilsB utilsB;

	@Inject
	private UtilsC utilsC;

	@Inject
	private UtilsD utilsD;

	@Override
	public BusinessResult get(BusinessData businessData){
		A a = utilsA.createA(businessData);
		B b = utilsB.createB(businessData);
		C c = utilsC.createC(businessData);
		D d = utilsD.createD(businessData);
		// do something
	}
} 

od:

Kopiuj

public class BusinessServiceImpl implements BusinessService {

	@Override
	public BusinessResult get(BusinessData businessData){
		A a = UtilsA.createA(businessData);
		B b = UtilsB.createB(businessData);
		C c = UtilsC.createC(businessData);
		D d = UtilsD.createD(businessData);
		// do something
	}
} 

Moim zdaniem to drugie jest czytelniejsze i dlatego wolę to stosować. Jeśli tylko ludzie trzymają się tego, żeby tylko metody utilsowe nie były złożone oraz żeby zmiana biznesowa nie powodowała zmiany w takiej metodzie to nic złego się nie stanie.
Jak to testować? Jednostkowo. Za tym idzie oczywiście założenie, że w statycznym kontekście nie będzie niczego do mockowania (dane z zewnątrz - pliki, baza danych itp.). Proste wejście-wyjście, bez żadnych udziwnień.

Co do tworzenia instancji klasy takiego utils'a - dziwny pomysł. Niepotrzebnie nadmuchuje to metodę (jeśli utils'y są tworzone wewnątrz metody) lub obiekt (jeśli utils'y są tworzone wewnątrz klasy).

Koziołek
Moderator
  • Rejestracja:prawie 18 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Stacktrace
  • Postów:6821
1

@wartek_no_log, jeżeli ktoś tworzy kod wymagający użycia kilku klas helperowych jak w twoim przykładzie to ja bym raczej się zastanowił czy aby na pewno nie można takiej klasy podzielić na kilka mniejszych.

Statyczne helpery są OK, ale pod warunkiem, że wzbogacają API biblioteki standardowej o metody, które tam powinny po prostu być. Przykładowo klasa Objects wprowadzona w Javie 7 powinna się tam znajdować "od zawsze" i dlatego powstały biblioteki typu commons-object, które łatały tą dziurę. Jeżeli twój helper rzeczywiście dostarcza tego typu funkcjonalności to niech będzie statyczny. Będzie można wykorzystać siłę import static by wprowadzić do kodu pewne nowe "metody kluczowe" (chodzi o metody spełniające podobną rolę jak metody z API biblioteki standardowej).

Jeżeli jednak w helperze zamykasz logikę powiązaną z domeną to warto używać ich jako niestatycznych. Z kilku powodów. Po pierwsze spada ich reużywalność, paradoksalnie jest to dobre ponieważ i tak zazwyczaj tego typu klas nie można użyć poza domeną. Przykładem niech będzie walidacja numeru identyfikującego klienta (jeżeli jest tworzony w jakiś specyficzny sposób). Po drugie łatwiej to przetestować. Helper taki dostarcza pewien element logiki biznesowej, a jej testy powinny być niezależne. Po trzecie taki helper zazwyczaj jest źle nazwaną klasą :) Serio. W trakcie rozwoju aplikacji na pewno trafisz na lepszą nazwę dla tej klasy niż coś w stylu FakturaHelper.


Sięgam tam, gdzie wzrok nie sięga… a tam NullPointerException
0

Panowie, mam następującą sytuację.

Mam klasę (JavaBean), w którym pojawiło się wstrzyknięcie utilka.

Kopiuj
public class MojaKlasa {
 private int a;
 @Inject
 private Util util;

 // metody biznesowe

 // getters and setters 
}

Generalnie wcześniej był test, który robił to samo ale staticiem. W tej chwili test się wywala ze względu na NullPointerException (Weld nie może wstrzyknąć obiektu util), co jest zupełnie ok.

Pytania:

  1. Czy musze jakos niezaleznie dostarczyc ten obiekt w moim tescie (w rzeczywistosci jest to byly static, nie dziala na zadnych danych)? Jak najlepiej to zrobic (new i wstrzykniecie przez setter, ktory musialbym utworzyc: brzydko chyba)?

Znalazlem takie cos:
http://jglue.org/
Wydaje sie obiecujace w kontekscie testowania CDI. Warto?

  1. Czy musze przetestowac jego funkcjonalnosc (to co robi static) niezaleznie od pierwotnego testu? Jest to chyba niezbedne, jesli chce aby testy byly niezalezne, chociaz nie do konca podoba mi sie to.
Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
0

"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
Koziołek
Moderator
  • Rejestracja:prawie 18 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Stacktrace
  • Postów:6821
0

Użyj Mockito, które wstrzyknie odpowiednio obiekt. Kod z wykorzystaniem JUnit4 (pisane z palaca więc pewno literówki w nazwach klas):

Kopiuj
@RunWith(MockitoJunit4Runner.class)
public class MojaKlasaTest{
    @Mock
    private Util utilMock;

    @InjectMocks
    private MojaKlasa underTest;

   //....
}

Widzę, że mnie @Shalom wyprzedził z linkiem do dokumentacji to już nie będę tu tego powtarzać.


Sięgam tam, gdzie wzrok nie sięga… a tam NullPointerException
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)