PRINCIPLES OF OBJECT ORIENTED DESIGN S.O.L.I.D
S.O.L.I.D Principles What is SOLID? Acrostic of 5 Principles: The Single Responsibility Principle The Open Closed Principle The Liskov Substitution Principle The Interface Segregation Principle The Dependency Inversion Principle
S.O.L.I.D Principles The Open Closed Principle The Liskov Substitution Principle The Single Responsibility Principle The Dependency Inversion Principle The Interface Segregation Principle Bringing it all together
S.O.L.I.D Principles How can SOLID help? Drives good design Maintenance Refactorability Clarity Coupling and Cohesion We want LOW coupling And HIGH cohesion
S.O.L.I.D Principles The Open/Closed Principle and The Liskov Substitution Principle
OCP and LSP OCP “Classes should be open for extension but closed for modification” Polymorphism / abstraction LSP “Derived classes must be substitutable for their base class” Basic polymorphism/inheritance Has implications with covariance/contravariance
S.O.L.I.D Principles The Single Responsibility Principle
“Every object should have a single responsibility, and that all its services should be narrowly aligned with that responsibility” Achieved with Dependency Inversion Interface Segregation
The Single Responsibility Principle public class User { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string GenerateUpdate() { return String.Format( "UPDATE Users SET FirstName='{0}', LastName='{1}' WHERE Id={2}", FirstName, LastName, Id); } public string GenerateDelete() { return String.Format( "DELETE FROM Users WHERE Id={0}", Id); } public string GenrateInsert() { if (Id != 0) throw new InvalidOperationException( String.Format( "This user already exists with an ID of {0}", Id)); return String.Format( "INSERT INTO Users VALUES ({0},{1})", FirstName, LastName); } public bool IsValid() { return !String.IsNullOrEmpty(FirstName) && !String.IsNullOrEmpty(LastName); }
S.O.L.I.D Principles The Dependency Inversion Principle
“Depend on abstractions and not concretions” “High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.”
The Dependency Inversion Principle Heavily uses Interface based programming Achieved with Dependency Injection
The Dependency Inversion Principle Interface based programming public string Concatenate(List parts, string delimeter) { string result = ""; foreach (var part in parts) result += part + delimeter; return result; }
The Dependency Inversion Principle Interface based programming public string Concatenate(List parts, string delimeter) { string result = ""; foreach (var part in parts) result += part + delimeter; return result; } public string Concatenate(string[] parts, string delimeter) { string result = ""; foreach (var part in parts) result += part + delimeter; return result; }
The Dependency Inversion Principle Interface based programming public string Concatenate(IList parts, string delimeter) { string result = ""; foreach (var part in parts) result += part + delimeter; return result; } public string Concatenate(string[] parts, string delimeter) { string result = ""; foreach (var part in parts) result += part + delimeter; return result; }
The Dependency Inversion Principle Interface based programming public interface IList : ICollection, IEnumerable, IEnumerable { // Methods int IndexOf(T item); void Insert(int index, T item); void RemoveAt(int index); // Properties T this[int index] { get; set; } }
The Dependency Inversion Principle Interface based programming public interface ICollection : IEnumerable, IEnumerable { // Methods void Add(T item); void Clear(); bool Contains(T item); void CopyTo(T[] array, int arrayIndex); bool Remove(T item); // Properties int Count { get; } bool IsReadOnly { get; } }
The Dependency Inversion Principle Interface based programming public string Concatenate(IEnumerable parts, string delimeter) { string result = ""; foreach (var part in parts) result += part + delimeter; return result; }
The Dependency Inversion Principle DRY Don’t Repeat Yourself!
The Dependency Inversion Principle Dependency Injection “Depend on abstractions and not concretions” Never call “new” to obtain a dependency Inject dependencies instead
The Dependency Inversion Principle Dependency Injection public interface IRepository { IQueryable Query(); void Insert(TEntity entity); void Update(TEntity entity); void Delete(TEntity entity); } public interface IValidator { IEnumerable Validate(TEntity entity); }
The Dependency Inversion Principle Dependency Injection public class Repository : IRepository { private readonly IValidator validator; public Repository(IValidator validator) { this.validator = validator; } public void Insert(TEntity entity) { var violations = this.validator.Validate(entity); if (violations.Count() != 0) throw new ValidationException(violations); // Insert if validation passed Session.Save(entity); } // other methods }
S.O.L.I.D Principles The Interface Segregation Principle
“Clients should not be forced to depend on interfaces that they do not use” Fat vs Thin Interfaces Drives low coupling Helps create self-documenting code
The Interface Segregation Principle Example: public interface ICRUDService { TEntity Find(int id); void Insert(TEntity entity); void Update(TEntity entity) void Delete(TEntity entity); }
The Interface Segregation Principle Example: public interface ICRUDService { TEntity Find(int id); void Insert(TEntity entity); void Update(TEntity entity) void Delete(TEntity entity); }
The Interface Segregation Principle Example public class CountryService : ICRUDService { Country Find(int id) { // bla } void Insert(Country entity) { throw new NotImplementedException(); } void Update(Country entity) { // bla } void Delete(Country entity) { throw new NotImplementedException(); }
The Interface Segregation Principle Example: public interface IQueryService { TEntity Find(int id); } public interface IInsertService { void Insert(TEntity entity); }
The Interface Segregation Principle Example: public class CountryService : IQueryService, IUpdateService { Customer Get(int id) { // bla } void Update(Customer customer) { // bla }
Implementing S.R.P. To achieve SRP Inject Dependencies by Inverting Control Deal with abstractions not concretions by using interface based programming Segregate interfaces into groups of concerns
Moving Forwards Using IoC Containers to help with Dependency Injection Using N-Tier architecture to create layers of concerns to aid with SRP