Adapter pattern is often mentioned as wrapper in normal convention. It is a pattern which introduces loose coupling when creating a middle interface between two unmatched types. The name itself, self describes itself.
Think we have a class which renders a dataset on the screen. So we’ve a working class like this.
class Renderer { private readonly IDbDataAdapter _adapter; public Renderer(IDbDataAdapter dataAdaper) { _adapter = dataAdaper; } public void Render() { Console.WriteLine("Writing data...."); DataSet dataset = new DataSet(); _adapter.Fill(dataset); DataTable table = dataset.Tables.OfType<DataTable>().First<DataTable>(); foreach (DataRow row in table.Rows) { foreach (DataColumn column in table.Columns) { Console.Write(row[column].ToString()); Console.Write(" "); } Console.WriteLine(); } Console.WriteLine("/n Render complete"); } }
Severe bug is, that it renders only the first table of the DataSet, but forget about the bug as of now and let’s focus on the design.
Simply Renderer does these things.
- It has a constructor which takes an IDbDataAdapter and set it to its readonly property.
- The Render() calls the Fill method of the IDbDataAdapter by passing a DataSet
- Takes the first table of the DataSet
- Displays the data
So it’s obvious any class that implements IDbDataAdapter would be a perfect candidate for the Renderer class.
Now move on to the scenario.
Think we have a data object class Game.
public class Game { public string Id { get; set; } public string Name { get; set; } public string Description { get; set; } }
And we have a class for rendering the game objects on the screen.
public class GameRenderer { public string ListGames(IEnumerable<Game> games) { // do the rendering return game.ToString(); } }
We have the Render class to do the rendering work for us. So we do not need to write the same code again. Only thing we’ve to do is connecting GameRenderer to our Renderer.
But we’ve got problems.
- We need an IDbDataAdapter implementation to use the Renderer.
- Render() is a parameter less method with no return types, which has to be mapped with the ListGames(IEnumerable<Game> games) which returns a string.
We need to have a class which works between GameRenderer and Renderer. That’s the adapter class we’re going to write. (GameCollectionDBAdapter)
So our GameCollectionDBAdapter should to be an IDbDataAdapter to work with the Renderer. In the other end it should be some other type to conform with the GameRenderer.
Create a new interface called IGameColllectionRenderer. This is the interface which conforms our adapter class with the GameRenderer.
The diagram explains the things clearly.
Not neat indeed.
So now you’ve got the idea.
The rest of the code goes here.
Code for IGameColllectionRenderer
public interface IGameColllectionRenderer { string ListGames(IEnumerable<Game> games); }
Code for the GameCollectionDBAdapter which is a IDbDataAdapter and IGameColllectionRenderer.
public class GameCollectionDBAdapter : IDbDataAdapter, IGameColllectionRenderer { private IEnumerable<Game> _games; public string ListGames(IEnumerable<Game> games) { _games = games; Renderer renderer = new Renderer(this); renderer.Render(); return _games.Count().ToString(); } public int Fill(DataSet dataSet) { DataTable table = new DataTable(); table.Columns.Add(new DataColumn() { ColumnName = "Id" }); table.Columns.Add(new DataColumn() { ColumnName = "Name" }); table.Columns.Add(new DataColumn() { ColumnName = "Description" }); foreach (Game g in _games) { DataRow row = table.NewRow(); row.ItemArray = new object[] { g.Id, g.Name, g.Description }; table.Rows.Add(row); } dataSet.Tables.Add(table); dataSet.AcceptChanges(); return _games.Count(); } }
Here IDbDataAdapter is not fully implemented, Fill method alone enough to run the code. But you have to have blank implementations of the other methods with throw NotImplementedException.
A slight change in your GameRenderer
public class GameRenderer { private readonly IGameColllectionRenderer _gameControllerRenderer; public GameRenderer(IGameColllectionRenderer gameCollectionRenderer) { _gameControllerRenderer = gameCollectionRenderer; } public string ListGames(IEnumerable<Game> games) { return _gameControllerRenderer.ListGames(games); } }
Finally the Main method
class Program { static void Main(string[] args) { List<Game> games = new List<Game>() { new Game() { Id = "2323", Name = "Need for Sleep", Description = "A game for sleepers" }, new Game() { Id = "w4334", Name = "MK4", Description = "Ever green fighter game" } }; GameRenderer gr = new GameRenderer(new GameCollectionDBAdapter()); gr.ListGames(games); Console.ReadKey(); } }