El principio de sustitución de Liskov básicamente no le permite usar en exceso la herencia de implementación: nunca debe usar la herencia solo para reutilizar el código (hay composición para esto). Al adherirse al LSP, puede estar bastante seguro de que realmente existe una "es-una relación" entre su superclase y su subclase.
Lo que dice es que sus subclases deben implementar todos los métodos de la subclase de manera similar a la implementación de los métodos en la subclase. Nunca debe anular un método con la implementación de NOP o devolver un valor nulo cuando el supertipo arroje una excepción; En los términos de Diseño por contrato, debe respetar el contrato del método de la superclase al anular un método. Una forma de defenderse contra la ruptura de este principio es nunca anular un método implementado; en lugar de eso, extraiga una interfaz e implemente esa interfaz en ambas clases.
Principio de Segregación de Interfaz , Principio de Responsabilidad Única y Principio de Alta Cohesión de GRASP están relacionados de alguna manera; se refieren al hecho de que una entidad debe ser responsable de una sola cosa, de modo que solo hay una razón para el cambio, de modo que el cambio se realice con mucha facilidad.
En realidad dice que si una clase implementa una interfaz, debe implementar y usar todos los métodos de esa interfaz. Si hay métodos que no son necesarios en esa clase en particular, entonces la interfaz no es buena y debe dividirse en dos interfaces, una que tenga solo los métodos que necesita la clase original. Se puede considerar desde un punto de vista, que se relaciona con el principio anterior por el hecho de que no le permite crear grandes interfaces para que su implementación pueda romper el LSP.
Puede ver Inversión de dependencia en el patrón de fábrica; aquí, tanto el componente de alto nivel (el cliente) como el componente de bajo nivel (instancia individual a crear) dependen de la abstracción ( La interfaz).
Una forma de aplicarlo en una arquitectura en capas: no debe definir una interfaz para una capa en la capa que se implementa, sino en el módulo al que se llama. Por ejemplo, la API a la capa de origen de datos no debe escribirse en la capa de origen de datos sino en la capa lógica de negocios, donde se necesita llamar. De esta manera, la capa de la fuente de datos hereda / depende del comportamiento definido en la lógica de negocios (por lo tanto, la inversión) y no al revés (como sería de una manera normal). Esto proporciona flexibilidad en el diseño, permitiendo que la lógica de negocios funcione sin ningún cambio de código, con otra fuente de datos completamente diferente.