180 likes | 194 Views
Learn how to implement the Abstract Factory design pattern to manage interchangeable mapper classes efficiently in your architecture. Ensure consistency and flexibility for accessing data mappers.
E N D
Architectures 2 Architecture Patterns Design Patterns
Layer Supertype • Some functionality is frequently common for all classes • In a module or layer • Separated out in a ”layer supertype” Domain layer Data mapper ”layer”
Testing and Speed Considerations • Not all tests need real DB • Some test domain • Some test mapper • Some test DB access • When focus is not db • A fake db will do • Replace data mapper with one using in-memory data • Mappers need same interface • Apply DIP Request Data Mapper Presentation Logic Use Create/Read Object-oriented Domain Model Access Data Source Logic
What We Have • We have two setups with mapper classes • Each with its own layer supertype
What we Need • The mappers should be interchangeable, so we need something like this • The DB client should not bother whether it gets a Linq, SQL or Dummy • The RoomMapper interface needs to be the same • The client depends on an interface, not on a particular mapper • DIP with dynamic replacement of the actual mapper possible
Arrange Mappers According to Domain Classes • What the client is concerned about • To get a room mapper, a person mapper, a booking mapper • Not interested in whether it is linq, sql or dummy • Need an orthogonal structure • One mapper from each technology need to implement RoomMapper, PersonMapper etc • ISP: Treat classes according to their roles (interfaces)
Where the Mapper Comes From • The client can get its mapper in two different ways • Have it given to itself from the outside: Dependency Injection • Ask someone for it • We may let it ask for it • Depending on setups, we may want to have different setups • LinqBookingMapper, LinqRoomMapper, LinqPersonMapper • DummyBookingMapper, DummyRoomMapper, DummyPersonMapper • But not • LinqBookingMapper, DummyRoomMapper, SqlPersonMapper • The setup needs to be consistent
Solution: Abstract Factory • Use design pattern Abstract Factory • One factory delivers a consistent set of mappers
Applying Patterns • Refactoring • Architecture Patterns • Design Patterns • Singleton • Abstract Factory
Where The Factory Comes From: Singleton • With different factories, one is the current one • Assign that one to a singleton • Similar to a global variable • MapperFactory.Instance returns the current factory • Every time you need a new room mapper • roomMapper = MapperFactory.Instance.MakeRoomMapper(); • The factory singleton manages a current instance in terms of MapperFactory • That is an abstract class • We need to inject an instance of a concrete class into the singleton • MapperFactory.SetFactory(new LinqMapperFactory());
The Singleton namespace DataMappers { abstract public class MapperFactory { private static MapperFactory instance; public static void SetFactory(MapperFactory factory) { instance = factory; } public static MapperFactory Instance { get { return instance; } } abstract public RoomMapper MakeRoomMapper(); abstract public PersonMapper MakePersonMapper(); abstract public BookingMapper MakeBookingMapper(); } }
Subclasses Implement the Factory Methods using DataMappers; namespace LinqDataMappers { public class LinqMapperFactory : MapperFactory { public override RoomMapper MakeRoomMapper() { return new LinqRoomMapper(); } public override PersonMapper MakePersonMapper() { return new LinqPersonMapper(); } public override BookingMapper MakeBookingMapper() { return new LinqBookingMapper(); } } }
The Factory Hierarchy • The Instance is typed MapperFactory • So it may return any of the subclasses • It needs to be instantiated • See demo • Principle: LSP • The clients will not notice any difference
Using the Mappers • Now we know how to create and manage mappers in a consistent way • Through an Abstract Factory • The application still needs to get hold of a mapper to read and save data • The abstract superclass Mapper may act as a mapper holder • A Singleton-like property for each mapper • Mapper.BookingMapper.GetAll() • A method to clear the current mapper of each kind • Called at the end of a user transaction • Needed in order to reset the Identity Map • Which is needed to avoid duplicate in-memory objects • Mapper.ClearBookingMapper() • A method to clear all the current mappers • Mapper.ClearMappers()
Implementing the Mapper Access Mechanisms • A singleton-like static accessor property in abstract class Mapper • private static BookingMapper bookingMapper; • public static BookingMapper BookingMapper{get{if (bookingMapper == null)bookingMapper = MapperFactory.Instance.MakeBookingMapper();return bookingMapper;}} • Resetting a mapper • public static void ClearBookingMapper() {bookingMapper = null;} • Resetting all the mappers • public static void ClearMappers(){ClearRoomMapper();ClearPersonMapper();ClearBookingMapper();}
Consequences of Mapper and Identity Map • Clients may use the BookingMapper etc properties directly • Mapper.BookingMapper always gives a (new or old) booking mapper • Call ClearMappers() when done with a user transaction • That is after the last update of the user transaction • Or when starting a new one with fresh data • When you know that you are done with your domain objects • Resets the identity map in the mapper • Assures that reading the same record several times does not result in several copies of the corresponding domain object in memory • Triggers fresh reading from the database on the next read operation • In case some other user has made any changes
Complete Example: Menu Selection ”Display All” • Reloading all the bookings • privatevoid allToolStripMenuItem_Click(object sender, EventArgs e){LoadData();} • privatevoid LoadData(){Mapper.ClearBookingMapper();Display(Mapper.BookingMapper.GetAll());} • privatevoid Display(IList<Booking> list){bookingBindingSource.DataSource = from item in listorderby item.Venue.Name ascending, item.FromTime ascendingselect item;}