The Liskov Substitution Principle
If it looks like a Duck, Quacks like a Duck,
But Needs Batteries,
You Might Need a Better Abstraction!
The Liskov Substitution Principle (LSP) in the Modern Developer’s Design Series supports the Modern Developers Behavior Versus State Principle (BVSP).
Compliance with LSP guarantees Class Inheritance compliance with BVSP.
The Liskov Substitution Principle States:
Methods that Use References to Base Classes
… Must be Able to Use Objects
…… Of Derived Classes without Knowing It
There are two types of Inheritance in modern Object Oriented Programming: Class and Inheritance.
Class Inheritance generally assumes that you want to create reusable code blocks by compiling with the DRY Principle: Don’t Repeat Yourself.
The Properties, Methods, Events, Delegates and Indexers
… Defined within a Class should not be Duplicated in other Classes
…… That require the Same Object Behavior
Class Inheritance assumes that the Derived Class is a representation; with enhance functionality, of its Base or Parent Class.
This creates an “Is A” relationship.
In Nature, Children are ALWAYS Variations of the Parents
… But Not In Software Development
The Liskov Substitution Principle states that a Derived Class must be able to carry out the original functionality of the Parent Class behavior if called upon to do so.
Everything that the Parent can do must be a viable part of the Child’s “DNA”
If any Inherited Functionality is Altered
…In such a way that it can no longer be substituted when called
…… it has Violated the Liskov Substitution Principle
If the Intent of the Inheritance Model
is to Change the Behavior
but Create a Reusable Pattern for other Types to Inherit
then Interface Inheritance should be used:
not Class Inheritance
If you need to “Hide” an element of a base class because its functionality is not required in the derived class then you have violated two principles:
-
Liskov Substitution – The Child class can no longer fulfill the Parents functions when called to do so
-
Interface Segregation Principle – You have inherited a base class contract that you can not use
The Liskov Substitution Principle Deep Dive
A rectangle is a square and a square is a rectangle.
They both have height and width properties that define their size. A square’s height and width values are equal where as a rectangle’s are different values.
This fact creates as “Is A” relationship in geometry.
In Object Oriented Programming (OOP) one of the three pillars is Inheritance.
Base Classes create common State and Behavior functionality that can be inherited, and sometimes overridden, by derived or child classes.
Class inheritance creates an “Is A” relationship.
A derived class is a version of the base class and inherits common functionality because it “Is A” base class type.
It is the expectation of the inheritance hierarchy that this relationship is pure.
An Apple is a Fruit
and a Fruit is an Apple
Let us now take a look at the square and the rectangle relationship in OOP.
If we assume that the Rectangle is the base class and a Square is a rectangle and we can then create a derived child class Square.
We inherit the State Properties of Height, Width, Area, Color and Name.
We also inherit the behavior actions of SetHeight, SetWidth, SetColor, SetName and GetArea.
Our Square can now create a unique object and not have to create that common functionality as it is available to it through inheritance.
With close examination we can see that there is a conflict in the inheritance schema
A Rectangle needs a Height and a Width but a Square only needs one or the other
If we try to use either Height or Width as the single value required by the Square we run the risk of the square not being created as we have intended.
By setting the Height to the Width we can get unexpected results when typed to the base type.
A Rectangle Type should be interchangeable with the Square Type without unwanted artifacts
This is not possible because a Square Is Not a Rectangle in OOP.
This violates the Liskov Substitution Principle
We can comply with LSP if we change the inheritance model to the base class is a Shape and we derive a Rectangle and a Square from the Shape base class.
The Shape base class inherits State Properties of Color and Name and the Behavior actions of SetColor and SetName but creates the Height and Width with the Area method that uses only the values that the derived type requires.
With this inheritance structure we are free to create other more complex shapes that hold an “Is a Shape” relationship such as Triangles and Hexagons.
Class inheritance can introduce subtle bugs that are really hard to find as they tend to mask themselves in different manifestations within the application during run-time.
These manifestations are referred to as “Inheritance Artifacts”
Class inheritance can be easily abused by not conforming to the “Is A” Relationship rule. This generally happens when a developer’s intent is good but implementation is incorrect.
State Objects, such as DTOs, should never be Base Classes for Behavior Method Child Classes.
This inheritance model is being called upon to “Behave Like” the DTO in the derived Class not be a spawned related Type.
Compliance with the Liskov Substitution Principle
… Gives us a Validation Methodology
…… That Supports Proper Inheritance Model Implementation
Violation of the Liskov Substitution Principle can be difficult to detect until the base type is used in a Polymorphism.
Key Code Smells are
- DTOs, as Base Classes, to inherit the State Objects for a Behavior Class’s Method’s Dependency
- The Requirement to hide an Element of the Base Class to Function Correctly
- Clear evidence that the Derived Class is NOT an Enhanced Variation of the Base Class
- Properties that have to be renamed or created in the Derived Class that have no logical place in the Base Class
In Object Oriented Programming Polymorphism is one of the Pillars of OOP. This pillar is the basis for the Liskov Substitution Principle.
We “Type” a Concrete Class to a Base Class abstraction so we can “Cast” it to an enhanced version of the Base Class when requiring additional functionality.
If a Derived Class Cannot Be Substituted for Its Base Type
… Then Polymorphism has Been Compromised
- The Base Object and Derived Objects are of Different Responsibility Types– One Type is a State Object while the Base or Derived Type is a Behavior Object
- The Child Does not Resemble the Parent – It is clear that the derived type is not a variation of its parent type. It has been inherited for its behavior requirements, not as an extension of the parent’s functional type.
- Derived Child Methods must be Hidden to Function – Inherited Methods from the Base Class must hide the Parent Method in order to function. This is due to behavior that does not comply with the intent of the Parent Class Method.
- Properties Take on Different Meanings and Functions within the Derived Child Class – Properties that can not be used as Inherited Object due to the fact that they are not required within the Inherited Class Object.
- When Used in Polymorphism Functionality Creates Artifacts – The expected results are not correct when types to the derived classes base class
This violation of the Liskov Substitution Principle uses the classic example above with Squares and Rectangles.
In Mathematics a Square can be derived from a Rectangle
… And the sides of the Square can use just one of the Rectangle’s parameters
In OOP this violates LSP and ISP as it forces a square to understand two side parameters when it only needs one.
This inheritance model tightly couples Size dimensions to the Base Class creating an even greater violation for other types such as Triangles and multi-sided geometrical objects such as an Octagon.
The Console Application using the Rectangle Class as a Base Class and Deriving a Square
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
using System; namespace LSP.Rectangle { public class LspRectangle { public static void Main() { Console.WriteLine("nLiskov Substitution Code Violation Example"); Rectangle rectangle = new Rectangle(); rectangle.SetSize(5, 10); rectangle.GetArea(); CreateRectangleDisplay(rectangle); Square square = new Square(); square.SetSize(5, 5); square.GetArea(); CreateSquareDisplay(square); Rectangle newRectangle = square; newRectangle.SetSize(5, 5); newRectangle.GetArea(); CreateNewRectangleAsSquareDisplay(newRectangle); } #region Display Helpers private static void CreateRectangleDisplay(Rectangle rectangle) { Console.WriteLine("nCreate a Rectangle called 'My Rectangle' with a ntBorder of Blue and a Color of Red an an Area of 50n"); Console.WriteLine("t{0} with a {1} Border andnt Color of {2} and an Area of {3}nt and is of Type: '{4}'", rectangle.Name, rectangle.BorderColor, rectangle.Color, rectangle.Area, rectangle.GetType().Name); Console.ReadKey(); } private static void CreateSquareDisplay(Square square) { Console.WriteLine("nCreate a Square called 'My Square' with a ntBorder of Green and a Color of Yellow an an Area of 25n"); Console.WriteLine("t{0} with a {1} Border andnt Color of {2} and an Area of {3}nt and is of Type: '{4}'", square.Name, square.BorderColor, square.Color, square.Area, square.GetType().Name); Console.ReadKey(); } private static void CreateNewRectangleAsSquareDisplay(Rectangle square) { Console.WriteLine("nCreate a New Rectangle called 'My Square' as a Square with a ntBorder of Green and a Yellow of Red an an Area of 25n"); Console.WriteLine("tMy New Rectangle called {0} with a {1} Border andnt Color of {2} and an Area of {3}nt and is of Type: '{4}'", square.Name, square.BorderColor, square.Color, square.Area, square.GetType().Name); Console.ReadKey(); Console.WriteLine("nnLiskov Substitution Violation: "); Console.WriteLine("nA Square can't be Substitued for a Rectangle without parameter modification.nt The Rectangle reuires Height and Width parameters ntbut the Square only needs a Side parameter n"); Console.ReadKey(); } #endregion } } |
The Base Case: Rectangle
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
namespace LSP.Rectangle { public class Rectangle { public string Name { get; protected set; } public string BorderColor { get; protected set; } public string Color { get; protected set; } public int Width { get; protected set; } public int Height { get; protected set; } public int Area { get; protected set; } public Rectangle() { Name = "My Rectangle"; Color = "Red"; BorderColor = "Blue"; } public void SetName(string name) { Name = name; } public void SetBorderColor(string borderColor) { BorderColor = borderColor; } public void SetColor(string color) { Color = color; } public void SetSize(int width, int height) { Width = width; Height = height; } public int GetArea() { return Area = (Width * Height); } } } |
The Derived Class: Square
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
namespace LSP.Rectangle { public class Square : Rectangle { public Square() { Name = "My Square"; Color = "Yellow"; BorderColor = "Green"; } public void SetSize(int width) { Width = width; Height = width; } } } |
The Console Violation Display:
The display demonstrates the ability to represent the values of the Square, in Polymorphic Behavior, as a Rectangle in the final case display BUT not without the Square having to know about the Height and Width parameter rather than a single Side parameter.
This forces the Square to implement properties it does not require.
This is a violation of LSP and the Interface Segregation Principle (ISP)
The goal for compliance with LSP is to create an extensible Inheritance Model that will not only support Squares and Rectangles, with four sides, but all geometrical objects like Triangles and Octagons, with many sides.
In order to accomplish this design task we must
… Allow the derived objects to define their own area parameters
By creating a Shape object that can define the methods for common parameters, such as Name, Color and Border Color, we can inherit and reuse what is common in our Parent object within or Child derivatives.
We can then Type our Children to our Parent and use Polymorphic Behavior for the functionality defined by the Parent without violation of LSP or ISP.
The Console Application using the Shape Class as a Base Class and Deriving a Square and a Rectangle:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
using System; namespace LSP.Shape { public class LspShape { public static void Main() { Console.WriteLine("nLiskov Substitution Code Compliance Example"); Shape shape = new Shape(); CreateShapeDisplay(shape); Rectangle rectangle = new Rectangle(); rectangle.SetSize(5, 10); rectangle.GetArea(); CreateRectangleDisplay(rectangle); Square square = new Square(); square.SetSize(5); square.GetArea(); CreateSquareDisplay(square); Shape newShape = square; CreateCastingSquareToShapeDisplay(newShape); } #region Display Helpers private static void CreateShapeDisplay(Shape shape) { Console.WriteLine("nCreate a Shape called 'My Shape' with a Border of 'Blue' and a Color of 'Red'"); Console.WriteLine("t{0} has a {1} Border and a {2} Colornt and is of Type: '{3}'", shape.Name, shape.BorderColor, shape.Color, shape.GetType().Name); Console.ReadKey(); } private static void CreateRectangleDisplay(Rectangle rectangle) { Console.WriteLine("nCreate a Rectangle called 'My Rectangle' with a Border of 'Green' and a ntColor of 'Gold' and an Area equal to 50"); Console.WriteLine("t{0} is {1} and a {2} Border and an Area that is {3}nt and is of Type: '{4}'", rectangle.Name, rectangle.Color, rectangle.BorderColor, rectangle.Area, rectangle.GetType().Name); Console.ReadKey(); } private static void CreateSquareDisplay(Square square) { Console.WriteLine("nCreate a Square called 'My Square' with a Border of 'Black' and a ntColor of 'Silver' and an Area equal to 25"); Console.WriteLine("t{0} is {1} and a {2} Border and an Area that is {3}nt and is of Type: '{4}'", square.Name, square.Color, square.BorderColor, square.Area, square.GetType().Name); Console.ReadKey(); } private static void CreateCastingSquareToShapeDisplay(Shape square) { Console.WriteLine("nCreate a New Shape from a Square called 'My Square' with a ntBorder of 'Black' and a Color of 'Silver'"); Console.WriteLine("tMy New Shape called {0} has a {1} Border and a {2} Colornt and is of Type: '{3}'", square.Name, square.BorderColor, square.Color, square.GetType().Name); Console.WriteLine("nnLiskov Substitution Compliance: "); Console.WriteLine("nA Square can be Substitued for a Shape and perform the Shape Duties ntwith the Values from the Square.n"); Console.ReadKey(); } #endregion } } |
The Base Class: Shape
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
namespace LSP.Shape { public class Shape { public string Name { get; protected set; } public string Color { get; protected set; } public string BorderColor { get; protected set; } public Shape() { Name = "My Shape"; Color = "Red"; BorderColor = "Blue"; } public void SetName(string name) { Name = name; } public void SetBorderColor(string borderColor) { BorderColor = borderColor; } public void SetColor(string color) { Color = color; } } } |
The Derived Class: Rectangle
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
namespace LSP.Shape { public class Rectangle : Shape { public int Width { get; protected set; } public int Height { get; protected set; } public int Area { get; protected set; } public Rectangle() { Name = "My Rectangle"; Color = "Gold"; BorderColor = "Green"; } public void SetSize(int width, int height) { Width = width; Height = height; } public int GetArea() { return Area = (Width * Height); } } } |
The Derived Class: Square
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
namespace LSP.Shape { public class Square : Shape { public int Side { get; protected set; } public int Area { get; protected set; } public Square() { Name = "My Square"; Color = "Silver"; BorderColor = "Black"; } public void SetSize(int side) { Side = side; } public int GetArea() { return Area = (Side * Side); } } } |
The Console Compliance Display:
The display demonstrates the ability to represent the values of the Square and a Rectangle, in Polymorphic Behavior, as a Shape in the final case display without the Square having to know about the Height and Width parameter only a single Side parameter.
This doesn’t force the Square to implement properties it does not require.
This is a supports of LSP and the Interface Segregation Principle (ISP)
An Example of LSP Compliance using Interface Inheritance over State to Behavior Class Inheritance
This design pattern uses the Request/Response DTO Pattern to add the Error Handing feature using Interface Extensions rather than a Behavior Class Inheriting and an Error DTO which violates LSP.
The Console Display Code:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
using System; using LSP.ErrorCompliance.DTOs; using LSP.ErrorCompliance.ExceptionExtensions; namespace LSP.ErrorCompliance { public class LSPErrorCompliance { private static CustomerResponseDTO _customer {get; set;} static LSPErrorCompliance() { _customer = CreateCustomerResponseDTO(); } public static void Main() { _customer.AddError("LSP Compliance Test Error Messagent"); CreateLSPErrorComplianceDisplay(); } #region LSP Error Compliance Helpers private static CustomerResponseDTO CreateCustomerResponseDTO() { return new CustomerResponseDTO { FirstName = "James", Lastname = "Kirk", }; } private static void CreateLSPErrorComplianceDisplay() { Console.WriteLine("nLSP Error Handling Compliance Displaynt"); Console.WriteLine("The Overrided ToString() Information Display:nt{0}nt with an Error Message: {1}", _customer, _customer.ErrorList[0].Message); Console.WriteLine("nThe Inheritance Model Complies with LSP as it is State to State Inheitance ntwithout the Class to State Inheritance Violationnt"); } #endregion } } |
The Response DTOs:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using System.Collections.Generic; using LSP.ErrorCompliance.ExceptionExtensions; namespace LSP.ErrorCompliance.DTOs { public class BaseResponseDTO : IErrorCommon { public bool Success { get; set; } public List ErrorList { get; set; } public BaseResponseDTO() { ErrorList = ErrorHandling.ErrorList; } } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
namespace LSP.ErrorCompliance.DTOs { public class CustomerResponseDTO : BaseResponseDTO { public string FirstName { get; set; } public string Lastname { get; set; } public string Email { get; set; } public override string ToString() { return string.Format("{0} {1} has an Email Address of {2}", FirstName, Lastname, Email); } } } |
The Error Handling Interface:
|
1 2 3 4 5 6 7 8 9 10 |
using System.Collections.Generic; namespace LSP.ErrorCompliance.ExceptionExtensions { public interface IErrorCommon { bool Success { get; set; } List<ErrorInfoDTO> ErrorList { get; set; } } } |
The Error Handling Interface Extension Methods that add the Behavior to the Interface Inheritance Model:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
using System; using System.Collections.Generic; using System.Linq; namespace LSP.ErrorCompliance.ExceptionExtensions { public static class ErrorHandling { public static List<ErrorInfoDTO> ErrorList { get; set; } static ErrorHandling() { ErrorList = new List(); } public static List AddError(this IErrorCommon error, Exception ex) { ErrorList.Clear(); ErrorList.Add(new ErrorInfoDTO { Message = ex.Message, StackTrace = ex.StackTrace }); return ErrorList; } public static List AddError(this IErrorCommon error, string errorMessage) { ErrorList.Clear(); ErrorList.Add(new ErrorInfoDTO { Message = errorMessage }); return ErrorList; } public static List BubbleErrors(this IErrorCommon error, List errorList) { ErrorList.Clear(); if (errorList != null && errorList.Any()) { ErrorList.AddRange(errorList); return errorList; } return new List(); } } } |
And Finally the Error DTO that holds the Error Messages:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
using System; namespace LSP.ErrorCompliance.ExceptionExtensions { public class ErrorInfoDTO { public string Message { get; set; } public string StackTrace { get; set; } public ErrorInfoDTO() { Message = "No Error Message"; StackTrace = "No Stack Trace Message"; } public ErrorInfoDTO(Exception ex) { Message = ex.Message; if (!string.IsNullOrEmpty(ex.StackTrace)) { StackTrace = ex.StackTrace; } StackTrace = "No Stack Trace Message"; } public override string ToString() { return string.Format("Error Message is {0} with a Stack Stack Trace of {1}", Message, StackTrace); } } } |
Here is the Class Diagram and Console display that adds a Test message to the List and displays the ToString Override withing the CustomerResponseDTO:
The Interface Extension Methods become available when the namespace: LSP.ErrorCompliance.ExceptionExtensions is referenced in the calling Client Application: The Console Application in this case.
This is also how .NET implements LINQ when the System.LINQ namespace is referenced
Extending Interfaces adds Implementation Functionality to the Interface Abstract Contracts
An Example of LSP Violation using Class Inheritance over Interface Inheritance
This design pattern uses the Request/Response DTO Pattern to add the Error Handing feature using Behavior Class Inheriting and an Error DTO which violates LSP.
The Console Display Code:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
using System; using LSP.ErrorViolation.DTOs; namespace LSP.ErrorViolation { public class LSPErrorViolation { private static CustomerResponseDTO _customer {get; set;} static LSPErrorViolation() { _customer = CreateCustomerResponseDTO(); } public static void Main() { _customer.AddError("LSP Violation Test Error Messagent"); CreateLSPErrorViolationDisplay(); } #region LSP Error Violation Helpers private static CustomerResponseDTO CreateCustomerResponseDTO() { return new CustomerResponseDTO { FirstName = "Mr", Lastname = "Spock", }; } private static void CreateLSPErrorViolationDisplay() { Console.WriteLine("nLSP Error Handling Violation Displaynt"); Console.WriteLine("The Overrided ToString() Information Display:nt{0}nt with an Error Message: {1}", _customer, _customer.ErrorList[0].Message); Console.WriteLine("nThe Inheritance Model Violates LSP as it is nta State to Behavior Inheritance Violation. ntThere is no 'Is A' Relationshipn"); } #endregion } } |
The Response DTOs:
|
1 2 3 4 5 6 7 8 9 |
using LSP.ErrorCompliance.ExceptionExtensions; namespace LSP.ErrorCompliance.DTOs { public class BaseResponseDTO : ErrorHandling { public bool Success { get; set; } } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
using LSP.ErrorCompliance.DTOs; namespace LSP.ErrorViolation.DTOs { public class CustomerResponseDTO : BaseResponseDTO { public string FirstName { get; set; } public string Lastname { get; set; } public string Email { get; set; } public override string ToString() { return string.Format("{0} {1} has an Email Address of {2}", FirstName, Lastname, Email); } } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
using System; namespace LSP.ErrorViolation.DTOs { public class ErrorInfoDTO { public string Message { get; set; } public string StackTrace { get; set; } public ErrorInfoDTO() { Message = "No Error Message"; StackTrace = "No Stack Trace Message"; } public ErrorInfoDTO(Exception ex) { Message = ex.Message; if (!string.IsNullOrEmpty(ex.StackTrace)) { StackTrace = ex.StackTrace; } StackTrace = "No Stack Trace Message"; } public override string ToString() { return string.Format("Error Message is {0} with a Stack Stack Trace of {1}", Message, StackTrace); } } } |
The Response Error Handling Behavior Methods Class:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
using System; using System.Collections.Generic; using System.Linq; using LSP.ErrorViolation.DTOs; namespace LSP.ErrorCompliance.ExceptionExtensions { public class ErrorHandling : ErrorInfoDTO { public List ErrorList { get; set; } public ErrorHandling() { ErrorList = new List(); } public List AddError(Exception ex) { ErrorList.Clear(); ErrorList.Add(new ErrorInfoDTO { Message = ex.Message, StackTrace = ex.StackTrace }); return ErrorList; } public List AddError(string errorMessage) { ErrorList.Clear(); ErrorList.Add(new ErrorInfoDTO { Message = errorMessage }); return ErrorList; } public List BubbleErrors(List errorList) { ErrorList.Clear(); if (errorList != null && errorList.Any()) { ErrorList.AddRange(errorList); return errorList; } return new List(); } } } |
This inheritance model violates the “Is A” relationship requirement for good Class Inheritance.
It is inheriting behavior and should use Interface Inheritance to create a “Behaves Like” relationship.
This also violates the Behavior Versus State Principle as it blurs the lines between State Entities and Behavior Entities.
The Benefits of Compliance with the LSP Principle:
-
Helps support a proper and stable Inheritance model
-
Clearly defines the “Is A” relationship for Class Inheritance
-
It fosters proper use of Covariance and Contravariance within the inheritance Object model
-
Creates a process for use of Interface Inheritance as a “Behaves Like” relationship inheritance solution
-
Clearly defines the requirement for an inherited Class to be a natural extension of its Parent Class: As it is in Nature
-
Helps to mange the artifacts of Class Inheritance that occur during the Change Management process of an Object Model within the production application
Assemblies that Comply with LSP have a
Lowered Total Cost of Ownership for the Client
as they are Less Brittle and Easier to Maintain
The Next Principle Design Series Post:
The Interface Segregation Principle
… Clients of Interfaces should not be forced to Implement
…… Interface Contracts they do not require
Barbra Liskov
Barbara Liskov, born Barbara Jane Huberman on November 7, 1939 in California, is a computer scientist. She is currently the Ford Professor of Engineering in the MIT School of Engineering’s Electrical Engineering and Computer Science department and an Institute Professor at the Massachusetts Institute of Technology.
Liskov is a member of the National Academy of Engineering and a fellow of the American Academy of Arts and Sciences and of the Association for Computing Machinery (ACM). In 2002, she was recognized as one of the top women faculty members at MIT, and among the top 50 faculty members in the sciences in the U.S.A.
In 2004, Barbara Liskov won the John von Neumann Medal for “fundamental contributions to programming languages, programming methodology, and distributed systems”. On 19 November 2005, Barbara Liskov and Donald E. Knuth were awarded ETH Honorary Doctorates. Liskov and Knuth were also featured in the ETH Zürich Distinguished Colloquium Series.
Liskov received the 2008 Turing Award from the ACM, in March 2009, for her work in the design of programming languages and software methodology that led to the development of object-oriented programming. Specifically, Liskov developed two programming languages, CLU in the 1970’s and Argus in the 1980’s. The ACM cited her contributions to the practical and theoretical foundations of “programming language and system design, especially related to data abstraction, fault tolerance, and distributed computing.”
Latest posts by Brad Huett (see all)
- DevOps: A Bridge to Your DevOps Culture - March 25, 2016
- Embracing Test Driven Development (TDD) - March 25, 2016
- DevOps: Delivering Agile Projects - March 25, 2016






