Dependency Injection a hermetyzacja

0

Cześć

Chciałbym napisać testy instrumentacyjne dla swojej aplikacji, jednak utknąłem na etapie wstrzykiwania zależności do mojego Activity bo mam pewien dylemat.

Jako że nie ma możliwości wstrzykiwania zależności poprzez konstruktor, to z tego co widzę w dokumentacji Androida, zależności można wstrzyknąć bezpośrednio do pola używając Hilta albo Daggera, w taki sposób:

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var analytics: AnalyticsAdapter
  ...
}

Tylko że aby to było możliwe, to pole nie może być prywatne. W tym momencie zapala mi się czerwona lampka, no bo przecież nie chcemy aby jakiś inny obiekt miał dostęp do naszego pola analytics, szczególnie że jest to var, to pole powinno być prywatne.

Więc chciałbym was zapytać - co robić? Jak żyć? Czy w tym wypadku należy nie przejmować się hermetyzacją, albo może istnieje jakiś inny sposób na wstrzykiwanie zależności aby umożliwić sobie testowanie?

0
atmal napisał(a):

Jako że nie ma możliwości wstrzykiwania zależności poprzez konstruktor

A dlaczego nie ma możliwości?
Wstrzykiwać można przez konstruktor (prawilnie) albo źle (przez pole lub setter). Wybór należy do Ciebie

3

Wstrzykiwać jak najmniej do komponentów z Androida. Idealny przypadek to 1 rzecz. Wtedy znika ten problem. Co do hermetyzacji to w Kotlinie należy użyć internal i wydzielić moduł a w Javie może być package private.

0

Opiszę może co dokładnie potrzebuję zrobić.
Mam Activity które wygląda tak:

class MainActivity : AppCompatActivity() {
    private lateinit var timetableHandler: TimetableHandler  // To musiałbym zamienić na potrzeby testów

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        timetableHandler = TimetableFileHandler(this.filesDir)
        // ....
        listTimetables()
    }

    private fun listTimetables() {
        infoList.addAll(timetableHandler.getStoredTimetables())
        recycler_view.adapter = InfoListAdapter(infoList)
    }

Metoda getStoredTimetables z klasy TimetableFileHandler zwraca wszystkie rozkłady zajęć które są zapisane na urządzeniu. Tą listę wyświetlam za pomocą mojego adaptera InfoListAdapter.

Na razie chciałem przetestować jak zachowuje się MainActivity w różnych przypadkach (np. gdy getStoredTimetables zwraca pustą listę). Później będę musiał przetestować co się dzieje po kliknięciu w element z listy itd.

0

Ale masz problem, tak jak napisałeś w temacie, z hermetyzacją czy ogólnie z testami? Bo ten przykład jest oderwany od głównego wątku. Tutaj wystarczy albo wstrzyknąć od razu TimetableHandler z innym, tymczasowym katalogiem źródłowym, albo wrzucać testowe dane do filesDir i czyścić przed testami.

0

Z hermetyzacją. Pytam jak należałoby to zrobić, tak aby trzymając się zasad programowania obiektowego (hermetyzacja) móc przetestować ten kod.

1

W takim razie internal i osobny moduł dla tej aktywności.

0

Zastanawiam się w takim razie czy nie jest to overkill dla mojej aplikacji, bo każda aktywność do której chciałbym wstrzykiwać zależności musiałaby być w osobnym module.

Niemniej jednak dzięki za pomoc ;) Tak coś czułem po intensywnym googlowaniu że private będzie musiało odpaść :D

0

Do tego typu projektów DI to jest generalnie overkill, CHYBA że się uczysz, ale wtedy znajdź sobie jakąkolwiek inną dużą apkę z dostępnym kodem i zobacz jak to się robi prawilnie. Przykładowo zobacz to https://m.facebook.com/groups/203076974338769?view=permalink&id=266218194691313 lub jeśli potrzebujesz innego przykładu to napisz.

Natomiast ja widzę że ty masz raczej pytanie natury filozoficznej "hermetyzacja a DI", mam rację?

Pisząc klasę hermetyzujesz zachowanie natomiast pozwalasz na dostarczenie konkretnej implementacji danego interfejsu (kontraktu) z zewnatrz.

2

DI w aplikacji to nigdy nie jest overkill tylko zwykła praktyka programistyczna. Dagger, Koin czy jeszcze jakiś inny kontener DI to może być przesada, bo będzie więcej strat niż zysków z tego.

0

Z tego co pamiętam, to w Activity nie powinno być nic związanego z logiką aplikacji.
Activity ma jedynie powiązać kod obsługujący logikę z UI i w zasadzie wtedy nie ma sensu go testować.

0
Roman Mokrzan napisał(a):

Do tego typu projektów DI to jest generalnie overkill, CHYBA że się uczysz, ale wtedy znajdź sobie jakąkolwiek inną dużą apkę z dostępnym kodem i zobacz jak to się robi prawilnie. Przykładowo zobacz to https://m.facebook.com/groups/203076974338769?view=permalink&id=266218194691313 lub jeśli potrzebujesz innego przykładu to napisz.

Nie zgodzę się. Tak jak napisał @Michał Sikora, DI można (albo powiedziałbym nawet że należy) używać w każdej aplikacji, bo znacząco ułatwia testowanie. Chyba że mówimy o projektach pokroju Hello World… :)

Roman Mokrzan napisał(a):

Natomiast ja widzę że ty masz raczej pytanie natury filozoficznej "hermetyzacja a DI", mam rację?

Tak, zgadza się ;) Pytanie wzięło się stąd, że brakuje mi możliwości wstrzykiwania zależności poprzez konstruktor, czyli tak jak należałoby to zrobić.

Michał Sikora napisał(a):

Dagger, Koin czy jeszcze jakiś inny kontener DI to może być przesada, bo będzie więcej strat niż zysków z tego.

Aplikacja którą piszę nie jest bardzo mała, ale na pewno nie jest też duża.
Tak jak napisałem wcześniej, skończyło się na tym że używam Hilt'a i wstrzykuję zależności poprzez Field-Injection. Działa sprawnie i udało mi się dokonać tego co chciałem - czyli poprzez wstrzykiwanie zależności testować zachowanie moich Activities symulując różne sytuacje. O ile nie jestem w stanie powiedzieć czy używanie Hilta do tej aplikacji to przesada, bo najzwyczajniej brakuje mi doświadczenia, to wydaję mi się że bez frameworków miałbym dużo boilerplate'u i w rezultacie czytelność kodu by się pogorszyła.

2
atmal napisał(a):

wydaję mi się że bez frameworków miałbym dużo boilerplate'u i w rezultacie czytelność kodu by się pogorszyła.

To raczej nie jest problemem. Polecam np. tę prezentację i projekt do niej (https://github.com/handstandsam/ShoppingApp). Można by było oczywiście lepiej to probić, bo tam nie ma z tego co pamiętam żadnego wzorca architektonicznego, ale aplikacja jest zrobiona tylko w celach demonstracyjnych.

Moje zdanie natomiast jest takie, że w zasadzie projektów nie zaczynam bez Daggera. Ale zdaję sobię, że nie trzeba z niego korzystać. Po prostu jest mi łatwiej i szybciej rozwijać tak aplikacje nawet jak mają skalę dwóch ekranów.

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