Interfejsy markerowe - czy warto stosować

0

Używacie interfejsów markerowych? Czyli takich, które nie mają żadnych metod tylko służą wspólnemu nazewnictwu.
https://en.wikipedia.org/wiki/Marker_interface_pattern

5

Jak trzeba, to tak.

2

Od metadanych są annotacje tak więc @Tak a nie implements Tak.

5

Jako counterpoint wrzucę oficjalny gajdlajny dotyczące projektowania międzymordzi

screenshot-20220101160048.png

Interface Design

Chociaż średnio mi się to podoba. wydaje mi się że nie jest to tak handy jak interface i is

0

A jak bez tych marker interfaces ogarnąć DI? Jak serwisy implementują jakieś IDependency, to wystarczy kilka linijek. Teoretycznie można rejestrować po suffixie (DAO, UseCase, Factory), ale jak dojdzie nam nowy serwis o jakimś mniej popularnym suffixie, to trzeba będzie zmienić konfigurację IoC, czyli trochę słabo imo.

No chyba, że to nie pytanie, czy warto używać, tylko raczej marker interface vs attribute.

1

Markery są szczególnie przydatne jeśli trzeba zakombinować coś z refleksją kiedy mamy do czynienia z interfejsami generycznymi. Wtedy proste użycie IsAssignableFrom lub is pozwala na łatwe sprawdzenie czy dany typ należy do jakiejś tam rodziny typów. Dużo lepiej się to spina niż dokładanie do tego atrybutów.

1

Przykład z jednego z moich pet projektów:

            for (Field field : getClass().getFields()) {
                // ...
                // Field must have @ElfApi annotation
                ElfApi elfApi = field.getAnnotation(ElfApi.class);
                if (elfApi == null) continue;


               // Masz typowaną adnotacje, można wyciągnąć dowolne info

To jedno wywołanie getAnnotation(type) nie wydaje mi się jakieś super szalone.

Zalety adnotacji:

  • Mogą dodatkowo przekazywać dodatkowe dane
  • Klasa może mieć na sobie wiele adnotacji tego samego typu
  • Nie ma problemów przy dziedziczeniu (można wybrać czy sie dziedziczą)
  • Można przyczepiać nie tylko na typy ale na pola itp.

Pusty interface to jakaś dziwność, założę się że w 90% przypadków jak adnotacje nie pasują to taki interface dało by się jednak przerobić na taki z metodami...

PS. Jak MS robił lepszą Java aka C# to Serializable jest już atrybutem (== adnotacją). Także zdania są podzielone. Dla mnie Serializable w Java i to jak zostało zrobione (transient keyword) to prawdziwe wypaczenie z programistycznego średniowiecza. A ile przy tym było dziur security to już nie wspomnę. Także gratuluje architektom tego rozwiązania...

0
Sampeteq napisał(a):

Używacie interfejsów markerowych? Czyli takich, które nie mają żadnych metod tylko służą wspólnemu nazewnictwu.

https://en.wikipedia.org/wiki/Marker_interface_pattern

Staram się unikać, ale jak każą, to trzeba.

nobody01 napisał(a):

A jak bez tych marker interfaces ogarnąć DI? Jak serwisy implementują jakieś IDependency, to wystarczy kilka linijek. Teoretycznie można rejestrować po suffixie (DAO, UseCase, Factory), ale jak dojdzie nam nowy serwis o jakimś mniej popularnym suffixie, to trzeba będzie zmienić konfigurację IoC, czyli trochę słabo imo.

No różne zastosowania pustych interfejsów spotkałem, ale konfigurowania kontenera jeszcze nie widziałem. W C# po prostu bierzesz wszystkie publiczne typy z danego assembly i je rejestrujesz, tu nie ma co filozofować.

0
somekind napisał(a):

No różne zastosowania pustych interfejsów spotkałem, ale konfigurowania kontenera jeszcze nie widziałem.

Hmm, myślałem, że to dość popularne, pierwszy lepszy przykład.

Bardzo często w aplikacji grupujemy typy w „rodziny” poprzez implementacje jakiegoś bazowego interfejsu. Często nawet ten interfejs sam nic nie zawiera i służy tylko do oznaczenia nim właśnie grupy typów – tak zwany marker interfejs. Można go wykorzystać w automatycznej rejestracji, czy w ograniczeniach w typach generycznych.

W C# po prostu bierzesz wszystkie publiczne typy z danego assembly i je rejestrujesz, tu nie ma co filozofować.

Wszystkie? DTO też? Ogólnie których klas nie rejestrować w kontenerze? I to chyba nie takie proste, bo możemy chcieć zarejestrować różne typy z różnymi scope, albo podpiąc niektóre typy pod interfejs, który implementują, a niektóre nie.

1
nobody01 napisał(a):

Wszystkie? DTO też?

A co to przeszkadza, jak DTO zarejestrujesz?

nobody01 napisał(a):

Ogólnie których klas nie rejestrować w kontenerze?

A po co się przejmować nierejestrowaniem jakiejś klasy, której nie używasz w kontenerze? Bo jak używasz, to musisz ją zarejestrować i tak.

nobody01 napisał(a):

I to chyba nie takie proste, bo możemy chcieć zarejestrować różne typy z różnymi scope

To wtedy rejestrujesz tylko te typy inaczej, a resztę z automatu. No i javowcy zazwyczaj bawią się w te scopy, połowa rzeczy jest singleton, druga połowa transient, trzecia połowa w ogóle wstrzykuje transienty w singletony i robi się kosmos. W świecie dotneta zdecydowana większość leci jako transient i problem z głowy.

nobody01 napisał(a):

albo podpiąc niektóre typy pod interfejs, który implementują, a niektóre nie.

A kiedy tego potrzebujesz? I kiedy zarejestrowanie typu pod interfejsem spowoduje problem, że chciałbyś tego nie robić, a tylko zarejestrować typ bezpośrednio?

3
nobody01 napisał(a):

Hmm, myślałem, że to dość popularne, pierwszy lepszy przykład.

Popularne nie znaczy dobre.
Jaką zaletę ma zrobienie pustego IRepository zamiast po prostu rejestracja wszystkiego, co implementuje jakiś faktyczny interfejs repozytorium?

Wszystkie? DTO też?

Po pierwsze, to jeśli masz dobrze podzielony kod na assembly, to DTO nie będziesz miał tam, gdzie masz zależności.
Po drugie odsianie DTO na etapie rejestracji jest banalne, wystarczy pominąć typy bez publicznych metod.

I to chyba nie takie proste, bo możemy chcieć zarejestrować różne typy z różnymi scope

No i jeśli masz dobrze podzielony kod na assembly, to zależności wymagające różnych lifetimów też będą razem.
A jeśli nie, to tych klika wyjątków trzeba zarejestrować ręcznie, a resztą można już generycznie.

albo podpiąc niektóre typy pod interfejs, który implementują, a niektóre nie.

Ale dlaczego nie? Skoro już interfejs jest, i jest używany, to nie widzę powodu, aby rejestrować z jego pominięciem.

Afish napisał(a):

No i javowcy zazwyczaj bawią się w te scopy, połowa rzeczy jest singleton, druga połowa transient, trzecia połowa w ogóle wstrzykuje transienty w singletony i robi się kosmos. W świecie dotneta zdecydowana większość leci jako transient i problem z głowy.

No właśnie nie z głowy, bo tworzenie jakichś ciężkich klas albo cache per request zamiast jako singletony, to może być dopiero początek problemów.
I w ogóle, to ciekawe, co napisałeś, bo gadałem kiedyś ze znajomym Javowcem, to twierdził, że u nich 95% klas to singeltony.

2

bo gadałem kiedyś ze znajomym Javowcem, to twierdził, że u nich 95% klas to singeltony.

To wynika ze specyfiki jednego kontenera - Spring IoC, gdzie komponenty per session są wrapowane w proxy. Czyli przykładowo niech Foo będzie per session, wtedy możemy mieć FooContoller jako singleton a w runtime'ie Spring wygeneruje taką strukturę:

Contoller -> FooProxy -> Foo

W danej sesji Spring zwróci ten sam obiekt Contoller z tym samym obiektem FooProxy (oba singletony). Ale FooProxy przekieruje żądanie do komponentu sesyjnego Foo. Tak to jest trochę pjbn ale tak sobie wymyślili w Springu.

Szczegóły: https://www.monirthought.com/2017/12/spring-boot-rest-api-spring-bean-scope.html

1

Czasami. Np. zamiast adnotacji pól klas dla jakiś mapperów wole napisać swoje mapery, żeby nie skalać biblioteki modelowej zależnościami do konkretnej bazy danych etc. ale np. przy testowaniu przydaje się.

0
somekind napisał(a):

No właśnie nie z głowy, bo tworzenie jakichś ciężkich klas albo cache per request zamiast jako singletony, to może być dopiero początek problemów.

Wtedy te ciężkie rzeczy rejestrujesz jako singleton i po sprawie, zamiast jednej linijki rejestracji do DI będziesz miał dwie.

somekind napisał(a):

I w ogóle, to ciekawe, co napisałeś, bo gadałem kiedyś ze znajomym Javowcem, to twierdził, że u nich 95% klas to singeltony.

I zapewne tak było, tylko potem do tych singletonów wstrzykiwał też transienty. W dotnecie kontroler jest transient (czy tam per web request), w javie zazwyczaj jest singletonem, a rzeczy per request załatwia się właśnie przez takie wstrzykiwanie transienta doń. Oczywiście to taka reguła kciuka, nie aksjomat.

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