Monday 23 May 2016

Liskov Substituton Principle (LSP)


=> Child class should never break the parent class type definition.
=> An object should be substitutable by its base class (or interface).

LSP violation code :
public interface IPersistedResource{
 void Load();
 void Persist();
}

public class ApplicationSettings : IPersistedResource{
 public void Load(){
  // Function definition for ApplicationSettings
 }

 public void Persist(){
  // Function definition for ApplicationSettings
 }
}

public class UserSettings : IPersistedResource{
 public void Load(){
  // Function definition for UserSettings
 }

 public void Persist(){
  // Function definition for UserSettings
 }
}
Use :
static IEnumerable<IPersistedResource> LoadAll(){

 var allResources = new List<IPersistedResource>
  {
   new UserSettings(),
   new ApplicationSettings()
  };

 allResources.ForEach(r => r.Load());

 return allResources;
}

static void SaveAll(IEnumerable<IPersistedResource> resources){
 resources.ForEach(r => r.Persist());
}

IEnumerable<IPersistedResource> resources = LoadAll(); // Should be a happy user with loaded resources

SaveAll(resources); // Should be a happy user with saved persist

Everything works great, until a new class is added to the system inorder to handle, let's add another class "SpecialSettings"
public class SpecialSettings : IPersistedResource{
 public void Load(){
  // Function definition for SpecialSettings
 }

 public void Persist(){
  through new NotImplementedException();
 }
}

static IEnumerable<IPersistedResource> LoadAll(){

 var allResources = new List<IPersistedResource>
  {
   new UserSettings(),
   new ApplicationSettings(),
   new SpecialSettings()
  };

 allResources.ForEach(r => r.Load());

 return allResources;
}

static void SaveAll(IEnumerable<IPersistedResource> resources){
 resources.ForEach(r => {
  if(r is SpecialSettings)
   return;
  r.Persist();
  });
}
Here, marked code is the violated for SpecialSettings(). Because we know An object should be substitute by its base class or interface".

Now it is time to refactor the violation. Let's refactor this violation :
public interface ILoadResource{
 void Load();
}

public interface IPersistedResource{
 void Persist();
}

public class ApplicationSettings : ILoadResource, IPersistedResource{
 public void Load(){
  // Function definition for ApplicationSettings
 }

 public void Persist(){
  // Function definition for ApplicationSettings
 }
}

public class UserSettings : ILoadResource, IPersistedResource{
 public void Load(){
  // Function definition for UserSettings
 }

 public void Persist(){
  // Function definition for UserSettings
 }
}

public class SpecialSettings : ILoadResource{
 public void Load(){
  // Function definition for SpecialSettings
 }
}
Use :
static IEnumerable<ILoadResource> LoadAll(){

 var allResources = new List<ILoadResource>
  {
   new UserSettings(),
   new ApplicationSettings(),
   new SpecialSettings()
  };

 allResources.ForEach(r => r.Load());

 return allResources;
}

static void SaveAll(IEnumerable<IPersistedResource> resources){
 resources.ForEach(r => r.Persist());
}

No comments:

Post a Comment