This blog posting piggybacks onto Of Interfaces (1of3) which is another blog posting. Please read it first. This blog posting shows a refactoring to the code detailed in the prior blog posting. I have added a new project called InterfaceExample.FileIO and have moreover added a few files to the two existing projects. We will no longer need the PersonRepository in InterfaceExample.Core so I have just renamed it to be DepreciatedPersonRepository.
I made a .csv file called IBMCEOs.csv which has this in it:
Thomas J. Watson Jr.,T. Vincent Learson,Frank Cary,John Opel,John Akers,Louis V. Gerstner Jr.,Samuel J. Palmisano
...and looks like this when one opens it in Excel:
I'm going to read from this file the list of names solving the problem eluded to in the prior blog posting (a need for abstracting data out of code). For simplicity-sake I am just putting IBMCEOs.csv in the root of the InterfaceExample project, but this is only done to allow the file to live within our web site. It is NOT done to associate IBMCEOs.csv with the web forms project out of a strict need. The .csv file could just as easily be hosted at a different web site or in a folder on a server somewhere having nothing to do with the public-facing content of any web site. I have created the InterfaceExample.FileIO project to manage the procurement of data from the file. This is a wise distinction from the other two projects as FileIO matters are an external dependency which is to say they are neither of the user interface nor the core logic. InterfaceExample.FileIO has a new version of PersonRepository in it that looks like this:
using System.Collections.Generic;
using System.IO;
using System.Linq;
using InterfaceExample.Core;
namespace InterfaceExample.FileIO
{
public class PersonRepository : IPersonRepository
{
public List<Person> GetAllPeople()
{
return People();
}
public List<Person> GetPeopleNotNamedJohn()
{
return People().Where(p => !p.Name.Contains("John")).ToList();
}
private static List<Person> People()
{
string path = "C:\\inetpub\\wwwroot\\app\\IBMCEOs.csv";
TextReader textReader = new StreamReader(path);
string scrapping = textReader.ReadToEnd();
textReader.Close();
string comma = ",";
string[] contents = scrapping.Split(comma.ToCharArray());
return contents.Select(name => new Person {Name = name}).ToList();
}
}
}
Great! So what does this have to do with interfaces? Well, I'm getting around to that. Without interfaces we would need to implement a three-layered approach to architecture in which the Core would inherit from the FileIO project (allowing it to create collections of Person objects from data that it got from InterfaceExample.FileIO) and the UI would inherit from the Core (allowing it to get the collections from the Core). This is a bad approach. Why so? I will explain in the next blog posting in this series. Let's think about that later. Trust me when I say it is bad.
A better approach is to decouple the project that feeds in data from the core logic. Moreover, the FileIO project needs to know about the core logic so that it may create collections of Person which is a class in InterfaceExample.Core. Hence, I have InterfaceExample.FileIO inheriting from InterfaceExample.Core and not the other way around. I have the UI inheriting from both projects as it will also serve as our bootstrapper for an IoC container.
Alright, instead of having PersonRepository in the core I simply have IPersonRepository in the core in its place. IPersonRepository is an interface. The code behind of our web form in the UI will look to IPersonRepository at the core to populate its repeater. IPersonRepository will then be associated, against the flow of inheritance, with PersonRepository in InterfaceExample.FileIO. The two methods below will thus be wired up to correspond to methods in a different file in a different project.
using System.Collections.Generic;
namespace InterfaceExample.Core
{
public interface IPersonRepository
{
List<Person> GetAllPeople();
List<Person> GetPeopleNotNamedJohn();
}
}
An interface sort of empowers a blind assumption that one may call the methods upon it and that something else will fill-in-the-blanks for what those methods should be. Obviously, as the core does not inherit from the FileIO project, the core is not going to be able to go fishing for the methods itself, so how is the association between IPersonRepository and PersonRepository bound? I am using in IoC (Inversion of Control) container named StructureMap for this. StructureMap.config (in the UI project which is the "bootstrapper" that wires this up) looks like this.
<StructureMap>
<PluginFamily Type="InterfaceExample.Core.IPersonRepository"
Assembly="InterfaceExample.Core" DefaultKey="Default">
<Plugin Assembly="InterfaceExample.FileIO"
Type="InterfaceExample.FileIO.PersonRepository" ConcreteKey="Default" />
</PluginFamily>
</StructureMap>
Finally, our code behind changes somewhat to accommodate calling an interface for a repository instead of a class.
using System;
using InterfaceExample.Core;
using InterfaceExample.FileIO;
using StructureMap;
namespace InterfaceExample
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ObjectFactory.Initialize(x =>
{
x.ForRequestedType<IPersonRepository>()
.TheDefaultIsConcreteType<PersonRepository>();
});
IPersonRepository personRepository =
ObjectFactory.GetInstance<IPersonRepository>();
Repeater1.DataSource = personRepository.GetPeopleNotNamedJohn();
Repeater1.DataBind();
}
}
}
No comments:
Post a Comment