Design Patternsfor Loosely Coupled ApplicationsBarry S. StahlMicrosoft .NET MVPPhysics & Math Nerd@bsstahl@cognitiveinheritance.comhttps://CognitiveInheritance.com |
|
Favorite Physicists
Other notables: Stephen Hawking, Edwin Hubble, Leonard Susskind, Christiaan Huygens |
Favorite Mathematicians
Other notables: Daphne Koller, Grady Booch, Leonardo Fibonacci, Evelyn Berezin, Benoit Mandelbrot |
|
|
|
|
|
|
Design Principles
A Sample Tightly-Coupled Application
Design Patterns
A class should have only one reason to change
Also applies to modules and methods
Entities should depend on abstractions, not on concrete implementations
Our App Violates SRP
Our App Violates DIP
The 3 primary aspects of this application, the input, output & business-rules are tightly coupled
An abstract solution to a well known problem
Facade - A simplified view of an API that fronts a more complicated interface
Repository - A somewhat standardized facade that fronts a data-store
Goals of a Repository
Differences from other Facades
public interface IMeetingReadRepository { IEnumerable<Meeting> GetAllMeetings(); }
public class FileMeetingReadRepository : IMeetingReadRepository
{
private Path _sourceFilePath;
public FileMeetingReader(Path sourcePath)
{
_sourceFilePath = sourceFilePath;
}
public IEnumerable<Meeting> GetMeetings()
{
// Load the meetings from the file
}
}
Our App Violates SRP
Our App Still Violates DIP
An implementation of the Dependency Inversion Principle that provides the concrete implementations of abstract dependencies to client objects
Tools for implementing Dependency Injection - Configured to hold the concrete implementations of dependencies
Goals of DI Frameworks
Usage
A DI Framework can be queried for a dependency if the container is passed to the client which requests the dependency by its interface.
IServiceProvider _container;
public Engine(IServiceProvider container)
{
_container = container;
}
public void CreateData()
{
IMeetingRepository repo = _container.GetService<IMeetingRepository>();
// Additional functionality here
}
Using DI, dependencies can be substituted with Fakes or Mocks for testing.
public void ShouldReturnFalseIfMeetingIsOverBeforeNoon()
{
// Arrange - Add mock repo to container
IMeetingRepository repo = Mock.Of<IMeetingRepository>();
container.AddSingleton<IMeetingRepository>(repo);
// Act - Run the test in isolation
var target = new Engine(container);
target.Process(myMeeting);
// Assert - Add asserts here
}
Our App Violates SRP
Our App Violates DIP
We now have a new set of mixed concerns (SRP violation)
An implementation of a facade that fronts an algorithm or other logic
Goals of a Strategy
Differences from other Facades
Using the Strategy's abstract interface any number of different strategies can be used to act on the data and can be easily swapped.
public interface ICateringStrategy { bool ShouldMeetingBeCatered(start, len) }
public class CateringStrategyEngine: ICateringStrategy
{
public bool ShouldMeetingBeCatered(start,len)
{
// Logic here
return result;
}
}
|
|
|
|
Demos & Articles
Patterns
Tooling