C# · Programowanie · Wzorce projektowe

Wzorzec adapter – cz. 2 adapter obiektu

We wcześniejszym wpisie opisałam działanie wzorca adapter klasy. Dla przypomnienia: jest on wykorzystywany na przykład wtedy, gdy klient zdefiniuje swoje wymagania na podstawie jakiegoś interfejsu. Wystarczy, że dostarczy klasę, która implementuje ten interfejs i dziedziczy po istniejącej klasie. I tyle.

Co jednak, gdy klient nie przygotuje takiego interfejsu? Tylko dostarczy jakąś klasę spełniającą wymagania? Wtedy należy użyć wzorca adapter obiektu.

Skopiujmy przykładową klasę serwisu BoardGameService (jest ona taka sama, jak w poprzednim poście)

public class BoardGameService
{
	public static List<BoardGame> boardGameList = new List<BoardGame>
	{
		new BoardGame
		{
			Id = 1, Name = "Catan", Active = true
		},
		new BoardGame
		{
			Id = 2, Name = "Gaia", Active = true
		},
	};
	
	public BoardGame Get(int boardGameId)
	{
		return boardGameList.FirstOrDefault(x => x.Id == boardGameId);
	}
	
	public void Add(BoardGame boardGame)
	{
		boardGameList.Add(boardGame);
	}
	
	public void Delete(int boardGameId)
	{
		var boardGame = boardGameList.FirstOrDefault(x => x.Id == boardGameId);
		if (boardGame != null)
		{
			boardGame.Active = false;
		}
	}
}

wykorzystującą obiekt modelu BoardGame

public class BoardGame
{
	public int Id { get; set; }
	public string Name { get; set; }
	public bool Active { get; set; }
}

Załóżmy, że klient zdefiniował własną, zewnętrzną klasę ExternalBoardGameService, spełniającą swoje wymagania. Wygląda ona następująco:

public class ExternalBoardGameService
{
    public virtual string GetBoardGameName(int boardGameId)
    {
        //some logic
        return null;
    }
	public virtual ExternalBoardGame GetNewBoardGame(int boardGameId)
    {
        //some logic
        return null;
    }
	public virtual void AddBoardGame(ExternalBoardGame newBoardGame)
    {
        //some logic
    }
	public virtual void DeleteBoardGame(int boardGameId)
    {
        //some logic
    }
}

W tym momencie implementacja metod nie jest ważna, dlatego zostawiłam je puste (lub zwracam pusty obiekt). Ważne są metody. Żeby istniała możliwość nadpisania tych metod (podmienienia ich logiki), dodałam słowo kluczowe virtual.

Zerknijmy teraz, jak będzie wyglądała klasa adaptera. Po pierwsze, będzie dziedziczyć po obiekcie klienta ExternalBoardGameService. Dzięki temu będzie mogła skorzystać z wszystkich metod występujących w tej klasie.

public class BoardGameServiceAdapter : ExternalBoardGameService
{
    public override void AddBoardGame(ExternalBoardGame newBoardGame)
    {
        base.AddBoardGame(newBoardGame);
    }
    public override string GetBoardGameName(int boardGameId)
    {
        return base.GetBoardGameName(boardGameId);
    }
    public override ExternalBoardGame GetNewBoardGame(int boardGameId)
    {
        return base.GetNewBoardGame(boardGameId);
    }
    public override void DeleteBoardGame(int boardGameId)
    {
        base.DeleteBoardGame(boardGameId);
    }
}

W powyższym przykładzie wypisanie wszystkich metod klasy ExternalBoardGameService wraz z ich bazową implementacją nie jest potrzebne – służy tylko pokazaniu,  że zostaną wywołane metody z klasy bazowej.

Załóżmy, że zamiast metod bazowych, pochodzących z klasy ExternalBoardGameService  chcielibyśmy wywołać odpowiadajace im metody z klasy BoardGameService. Potrzebna jest nam do tego instacja klasy serwisu, którą możemy przekazać jako parametr w kontruktorze.

public class BoardGameServiceAdapter : ExternalBoardGameService
{
   private readonly BoardGameService _boardGameService;
   public BoardGameServiceAdapter(BoardGameService boardGameService)
   {
       _boardGameService = boardGameService;
   }
}

Dzięki temu możemy teraz nadpisać odpowiednie metody wykorzystując na przykład metody z serwisu BoardGameService:

public class BoardGameServiceAdapter : ExternalBoardGameService
{
    private readonly BoardGameService _boardGameService;
    public BoardGameServiceAdapter(BoardGameService boardGameService)
    {
        _boardGameService = boardGameService;
    }
    public override void AddBoardGame(ExternalBoardGame newBoardGame)
    {
        var boardGame = new BoardGame
        {
            Id = newBoardGame.Id,
            Name = newBoardGame.Name,
            Active = !newBoardGame.IsDeleted
        };
        _boardGameService.Add(boardGame);
    }
    public override ExternalBoardGame GetNewBoardGame(int boardGameId)
    {
        var boardGame = _boardGameService.Get(boardGameId);
        if (boardGame != null)
		{
            return new ExternalBoardGame
            {
                Id = boardGame.Id,
                Name = boardGame.Name,
                IsDeleted = !boardGame.Active
            };
		}
        return null;
    }
    public override void DeleteBoardGame(int boardGameId)
    {
        _boardGameService.Delete(boardGameId);
    }
}

To, co warto zauważyć, to to, że nigdzie nie nadpisujemy metody GetBoardGameName(). W powyższym rozwiązaniu możemy sobie na to pozwolić – zostanie zawsze wywołana logika zdefiniowana przez serwis zewnętrzny ExternalBoardGame.

PS – przykładowy kod można pobrać pod linkiem.

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Google+

Komentujesz korzystając z konta Google+. Wyloguj /  Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Connecting to %s