SOLID #5 – DIP

Dotarliśmy do ostatniego artykułu opisującego zasady SOLID. Dzisiaj przyszła kolej na zasadę odwracania zależności (ang. Dependency Inversion Principle). Co kryje się pod tym tajemniczo brzmiącym zwrotem?

 

Już wyjaśniam!

 

 

Dependency Inversion Principle

Co mówi nam ta zasada?



Okej, ale o co w tym chodzi? Głównym założeniem tej zasady jest to, aby posługiwać się interfejsami zamiast konkretnymi implementacjami. Dosadnie można rozumieć to następująco:

  • Żadna zmienna nie powinna zawierać wskaźnika lub referencji do konkretnej klasy,
  • Żadna klasa nie powinna być klasą pochodną konkretnej klasy,
  • Żadna metoda nie powinna przesłaniać zaimplementowanej metody żadnej ze swoich klas bazowych

Oczywiście nie da się spełnić w pełni tych warunków. Z resztą jest to niemożliwe – gdzieś przecież musimy stworzyć instancję konkretnej klasy. Należy tu jasno zaznaczyć, że zasady SOLID wskazówkami do tego, w jaki sposób mamy pisać kod, aby posiadał jak najmniej zależności oraz powiązań pomiędzy tworzonymi strukturami. Powinniśmy na ile jest to możliwe korzystać z  tych wskazówek, a nie bezwzględnie korzystać z tych zasad – nie zawsze są najlepszymi rozwiązaniami w każdej sytuacji.

Wróćmy jednak do DIPu. Przedstawię teraz kod wyciągnięty z projektu, który rozwijam w ramach DSP. Zastosowałem w nim DIP oraz wzorzec projektowy repozytorium.

Główny interfejs repozytorium.

public interface IRepository<TEntity> where TEntity : class
{
    TEntity GetById(int id);
    IEnumerable<TEntity> GetEntities();
    void Add(TEntity entity);
    void Remove(TEntity entity);
    void Update(TEntity entity);
    void Save();
}

 

Tutaj implementacja interfejsu repozytorium dla artykułów:

public interface IArticleRepository : IRepository<Article>
{}

 

Teraz konkretna klasa repozytorium artykułów. Modelem jest tutaj kontekst bazy danych, który także jest wstrzykiwany poprzez interfejs:

internal class ArticleRepository : IArticleRepository, IDisposable
{
    private readonly IErpDatabaseContext _dbContext;

    public ArticleRepository(IErpDatabaseContext erpDatabaseContext)
    {
        _dbContext = erpDatabaseContext;
    }

    public Article GetById(int id)
    {
        return _dbContext.Article.Find(id);
    }

    public IEnumerable<Article> GetEntities()
    {
        return _dbContext.Article.ToList();
    }

    public void Add(Article entity)
    {
        _dbContext.Article.Add(entity);
    }

    public void Remove(Article entity)
    {
        _dbContext.Article.Remove(entity);
    }

    public void Update(Article entity)
    {
        var a = _dbContext.Entry(entity).State = EntityState.Modified;
    }

    public void Save()
    {
        _dbContext.SaveChanges();
    }
}

 

Tutaj konkretne użycie repozytorium w klasie modelu widoku:

public class ArticleTableViewModel : ViewModelBase
{
    private readonly IArticleRepository _articleRepository;

    public ArticleTableViewModel(IArticleRepository articleRepository)
    {
        _articleRepository = articleRepository;
    }
    
    /*
       Inne działania na repozytorium
    */
}

 

Spójrz teraz na konstruktor w klasie ArticleTableViewModel. Jego parametrem jest obiekt implementujący interfejs IArticleRepository.

 

Co zyskujemy?

Przede wszystkim nie działamy na konkretnych klasach – zmniejszamy zależności. Dzięki temu, że parametrem tym jest interfejs, możemy dostarczać różne implementacje repozytorium dla naszego modelu widoku i wcale nie musimy zmieniać jego kodu gdy takie implementacje się pojawią! Coś wspaniałego! Jest to rozwiązanie, które umożliwi testowanie kodu poprzez dostarczanie różnych dostawców danych dla tworzonego repozytorium. Warto zauważyć, że w klasie ArticleRepository możemy dostarczyć dowolną implementację IErpDatabaseContext.

 

 

Podsumowanie

W wyżej przedstawionej implementacja ArticleTableViewModel nie tworzymy żadnych konkretnych obiektów – nie korzystamy z klas konkretnych. Klasa ArticleTableViewModel korzysta z utworzonej gdzieś “wyżej” implementacji. Zwróć uwagę, że przy tak utworzonej klasie, możemy z niej korzystać w innych projektach, ponieważ korzystamy z kontraktów jakimi są interfejsy.

 

Tu kończy się seria artykułów o SOLID. Mam szczerą nadzieje, że seria ta niosła ze sobą wartość merytoryczną, która jesteś lub będziesz w stanie kiedyś wykorzystać!

Mam coś dla Ciebie

Zapisz się do mojego newslettera, a ja prześlę Ci zbiór kilkunastu praktycznych wskazówek dla programisty aplikacji mobilnych.

Menu