O Princípio de Substituição de Liskov basicamente não permite que você use demais a herança da implementação: você nunca deve usar a herança apenas para reutilizar o código (existe composição para isso)! Ao aderir ao LSP, você pode ter certeza de que realmente existe um "é um relacionamento" entre sua superclasse e sua subclasse.
O que diz é que suas subclasses devem implementar todos os métodos da subclasse de maneira semelhante à implementação dos métodos na subclasse. Você nunca deve substituir um método pela implementação do NOP ou retornar nulo quando o supertipo lança uma exceção; declarado nos termos Design por contrato, você deve respeitar o contrato do método da superclasse ao substituir um método. Uma maneira de se defender contra a quebra desse princípio é nunca substituir um método implementado; em vez disso, extraia uma interface e implemente essa interface nas duas classes.
Princípio de Segregação de Interface , Princípio de Responsabilidade Única e Princípio de Alta Coesão do GRASP estão de alguma forma relacionados; eles se referem ao fato de que uma entidade deve ser responsável por apenas uma coisa, para que haja apenas um motivo para a mudança, para que a mudança seja feita com muita facilidade.
Na verdade, ele diz que se uma classe implementa uma interface, deve implementar e usar todos os métodos dessa interface. Se houver métodos que não são necessários nessa classe específica, a interface não será boa e deverá ser dividida em duas interfaces, uma que possua apenas os métodos necessários para a classe original. Isso pode ser considerado a partir de um ponto de vista, que se relaciona ao princípio anterior pelo fato de ele não permitir que você crie interfaces grandes para que sua implementação possa quebrar o LSP.
Você pode ver a inversão de dependência no padrão de fábrica; aqui, o componente de alto nível (o cliente) e o componente de baixo nível (instância individual a ser criada) dependem da abstração(a interface). Uma maneira de aplicá-lo em uma arquitetura em camadas: você não deve definir uma interface para uma camada na camada implementada, mas no módulo chamado. Por exemplo, a API para a camada de fonte de dados não deve ser gravada na camada de fonte de dados, mas na camada de lógica de negócios, onde é necessário chamar. Dessa maneira, a camada da fonte de dados herda / depende do comportamento definido na lógica de negócios (portanto, a inversão) e não vice-versa (como seria normalmente). Isso fornece flexibilidade no design, permitindo que a lógica de negócios funcione sem nenhuma alteração de código, com outra fonte de dados totalmente diferente.