DI w bibliotece .dll

0

Hej, piszę dla siebie małą bibliotekę w .Net.

W zasadzie to nigdy tego wcześniej nie robiłem. Chcę mieć po prostu kawałek kodu który będę sobie wykorzystywał co jakiś czas w różnych projektach.

Biblioteka ta mi trochę już urosła. Korzysta z różnych domen, i siłą rzeczy chciałem je porozdzielać po różnych klasach i komunikować się między nimi przez interfejsy, co by mieć je ogarnięte oraz móc sobie też przetestować lub mockować łatwiej.

I jakoś niespecjalnie wydaje się to praktyczne, bo później jest zabawa z IoC projektu który z biblioteki korzysta. Zastanawiam się czy:

  • biblioteki się testuje?
  • korzysta się w nich z IoC oraz DI? tzn wewnątrz dll-a.
  • czy zamyka się kilka domen w jednej bibliotece? np. operacje na stringu + IO + WebBrowser.

Pozdrawiam,
b.

1
bakunet napisał(a):
  • czy zamyka się kilka domen w jednej bibliotece? np. operacje na stringu + IO + WebBrowser.

Nie wiem o jakim języku piszesz.

Ale na pewno "okienka" (w adekwatnym środowisku GUI) oddzielnie od "algorytmów" *)
IO - zależy. Jeśli to nadbudowa nad biblioteką standardową to może razem. Jeśli coś specyficznego (mikrokontrolery, specyficzne urządzenia) to oddzielnie

*) zarówno w świecie Javy, jak i dotnet są sytuacje, że cały projekt jest odrzucany, bo ISTNIEJE coś zakazanego. Np Java / Google App Engine ma na blackliscie całe GUI, łącznie z zupełnie niewinną klasą Color.
.NET web odrzuca(ł ?) referencje do WinForms

0
AnyKtokolwiek napisał(a):

Nie wiem o jakim języku piszesz.

Ale na pewno "okienka" (w adekwatnym środowisku GUI) oddzielnie od "algorytmów" *)
IO - zależy. Jeśli to nadbudowa nad biblioteką standardową to może razem. Jeśli coś specyficznego (mikrokontrolery, specyficzne urządzenia) to oddzielnie

*) zarówno w świecie Javy, jak i dotnet są sytuacje, że cały projekt jest odrzucany, bo ISTNIEJE coś zakazanego. Np Java / Google App Engine ma na blackliscie całe GUI, łącznie z zupełnie niewinną klasą Color.
.NET web odrzuca(ł ?) referencje do WinForms

Chodzi mi o C#. Właściwie to chcę żeby to była jak najbardziej autonomiczna biblioteka, więc chyba będzie z IO, właśnie wyczytałem, że lepiej nie wołać UI z biblioteki, w sumie to ma sens. Myślę też wrzucić jednak wszystko do jednego worka i nie tworzyć interfejsów/serwisów, za dużo później zabawy z IoC w aplikacji, coś wymyślę z testowaniem. Dzięki za komentarz.

0

UPDATE

Ok, uparłem się, trochę pogrzebałem, i udało mi się znaleźć jak to zrobić. W zasadzie to nie znalazłem bezpośredniego rozwiązania, ale zainspirował mnie ten artykuł oraz ta odpowiedź na SO.

Generalnie rzecz biorąc to chodzi tu o to, żeby ukryć zależności biblioteki przed projektem aplikacji. W przeciwnym wypadku projekt aplikacji będzie płakał wyjątkami, że zależności biblioteki nie zostały zarejestrowane w kontenerze aplikacji. A tak naprawdę zostały już zarejestrowane w kontenerze biblioteki, choć są wstrzyknięte do konstruktora biblioteki dostępnego publicznie, przez to widzi je też aplikacja.

Jak ktoś poradził na SO, z pomocą tu przychodzi wzorzec fasady. Należy w nim schować zależności i rozwiązać Resolve fasadę w klasie biblioteki dostępnej z projektu aplikacji. Prosty przykład poniżej, w którym warstwę serwisową zarejestrowałem jako singletony:

public class Booter
    {
        public static IContainer BootStrap()
        {
            var builder = new ContainerBuilder();

            builder.RegisterType<FilesService>()
              .As<IFilesService>().SingleInstance();

            builder.RegisterType<ControlsService>()
              .As<IControlsService>().SingleInstance();

            builder.RegisterType<Facade>()
              .AsSelf().SingleInstance();

            return builder.Build();
        }
    }

Do fasady wstrzykujemy wszystkie zależności biblioteki:

public class Facade
    {
        IFilesService _fileService;
        IControlsService _controlsService;
        public Facade(IFilesService filesService, IControlsService controlsService)
        {
            _fileService = filesService;
            _controlsService = controlsService;

            FilesService = _fileService;
            ControlsService = _controlsService;
        }

        public IFilesService FilesService { get; }

        public IControlsService ControlsService { get; }
    }

Następnie w naszej bibliotece najpierw wołamy metodę Initialize, w której rozwiązujemy fasadę, a następnie do interfejsów serwisów przypisuję własności fasady wstrzykniętych już serwisów:

public class MainService : IMainService
    {
        IFilesService _fileService;
        IControlsService _controlsService;
        IContainer _container;
        public MainService()
        {
            Initialize();
        }

        public void Initialize()
        {
            _container = Booter.BootStrap();

            var aggregator = _container.Resolve<Facade>();

            _fileService = aggregator.FilesService;
            _controlsService = aggregator.ControlsService;
        }

        public UiControlsModel SwitchOff()
        {
            return _controlsService.SwitchOff();
        }

        public UiControlsModel SwitchOn()
        {
            return _controlsService.SwitchOn();
        }
    }

Mam nadzieję że nie jest to żaden antywzorzec.

2

Szczerze, to w ogóle nie rozumiem, czemu biblioteka w ogóle ma potrzebę używania wewnętrznie kontenera. Jak dla mnie biblioteka to zestaw klas, których dopiero używa się w swojej właściwej aplikacji, i tam trzeba tworzyć ich obiekty, zgodnie z regułami przyjętymi w aplikacji. Chyba w życiu nie widziałem biblioteki, która by wewnętrznie używała kontenera.

1

Po co w ogóle używasz kontenera w tej bibliotece? Co on Ci daje? Czy rozumiesz jak robić dependency injection bez kontenera?

2

Kontener mam po to, by rejestrować interfejsy które chcę wstrzyknąć do innej klasy . Kiedyś robiłem proste przykłady mockowania bez DI za pomocą fabryk i własnych namiastek/makiet.

Stop, ja jeszcze o żadnym mockowaniu nie mówię. Po co Ci kontener? Ponownie pytam, czy rozumiesz jak robić dependency injection bez kontenera?

Na oko obstawiam, że nie rozumiesz czym jest DI i po co właściwie używa się kontenera. DI != kontener DI. Może się mylę, jeżeli tak, to wyjaśnij, co ten kontener daje Ci w tym przypadku.

0

W kontenerze rejestruję interfejsy razem z klasami oraz ewentualnymi parametrami klasy które są rozwiązywane wraz z instancją kontenera. Następnie, dzięki zarejestrowanemu interfejsowi mogę wstrzyknąć interfejs do swojej klasy, co jest tzw luźnym połączeniem (loose coupling) i w razie potrzeby zamiast klasy sparowanej z interfejsem w kontenerze mogę wykorzystać makietę, np do testów jednostkowych.

Klawo. Której z tych rzeczy nie możesz zrobić bez kontenera?

1

Jak dla mnie potwierdziłeś, że nie rozumiesz DI. Dodatkowo Twój kod wyżej pokazuje, jak tworzysz kontener tylko po to, żeby wyciągnąć z niego fasadę, jeżeli takie coś chcesz robić w bibliotece, to kontener jest moim zdaniem całkowicie zbędny i tylko komplikuje sprawę.

0

@bakunet:

  1. nie pisz odpowiedzi na posty w komentarzach!
  2. Wrzucanie ciężkiej zależności w stylu kontenera do biblioteki to zły pomysł. Bo tego się potem nie da używać.

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