ISP Interface Segregation Principle (Princípio de segregação de interface)

Sumário

Veja o índice completo do tópico “S.O.L.I.D”

Olá pessoas …

Continuando a nossa saga de artigos, vamos agora ver a letra do acróstico que define a sigla ISP Interface Segregation Principle (Princípio de Segregação de Interface).

Definição

Este princípio é o mais simples de entender, ele quer dizer que:

note-taking

Classes clientes não devem ser forçadas a depender de métodos que elas não usam

Mas eu prefiro dizer que:

As abstrações não dependem de detalhes, mas os detalhes dependem de abstrações.
Evite o uso de interfaces gordas, assim como devemos evitar o uso de classes ou objetos deuses.

A segregação de interfaces assume que suas interfaces devem ser coesas, quando uma interface não é coesa, ele contêm métodos que não dizem respeito ao grupo de comportamento que as definem.

Coesão está diretamente ligada ao Princípio da Responsabilidade Única (SRP).

certo-ou-errado

Certo ou errado?

Vamos agora aos nossos exemplos entre certo e errado.

Ferindo o princípio

errado

Isto está errado.

Perceba que a interface “ISaldo” define o método “Atualizar()” e o método “BaixarEstoque()“. A primeira vista está certo, pois podemos assumir que é uma interface para cálculo de saldo de estoques.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

namespace Solid.Errado.Contract
{
    public interface ISaldo
    {
        #region Public Methods

        void Atualizar();

        void BaixarEstoque();

        #endregion Public Methods
    }
}

A classe abaixo, de forma errada e ferindo o princípio da SRP, implementa a interface “ISaldo“, perceba que a classe de saldo é obrigada a atualizar os saldos e realizar a baixa em estoque de um produto qualquer.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Errado.Contract;
using System;

namespace Solid.Errado
{
    public class AtualizarSaldoDiario: ISaldo
    {
        #region Public Methods

        public void Atualizar()
        {
            Console.WriteLine("O saldo DIARIO foi atualizado.");
        }

        public void BaixarEstoque()
        {
            Console.WriteLine("O estoque foi baixado em 10 itens");
            Atualizar();
        }

        #endregion Public Methods
    }
}

Classe para cálculo de saldo financeiro.

/*
 * Classe apenas para fins de exemplo e aprendizado não considera nenhum tipo de validação ou regra ou se utiliza de algum framework.
 */

using Solid.Errado.Contract;
using System;

namespace Solid.Errado
{
    public class SaldoFinanceiro: ISaldo
    {
        #region Public Methods

        public void Atualizar()
        {
            Console.WriteLine("O saldo financeiro foi atualizado.");
        }

        public void BaixarEstoque()
        {
            throw new Exception("OPS! Eu não sei o que fazer aqui. Não sou uma classe de saldo de estoques");
        }

        #endregion Public Methods
    }
}

Como podemos perceber, à primeira vista nossas definições de interfaces parecem corretas, pois se estamos falando de saldos, podemos baixar o estoque para calcular os saldos. Neste momento já podemos perceber que ferimos o princípio do SRP e do ISP.  Apesar de eu estar falando de saldos, eu posso estar calculando saldos financeiros, que nada têm haver com saldos de estoque, e se por alguma razão eu implementar esta definição de saldos, serei obrigado a baixar estoque em uma movimentação financeira.

Então, vamos corrigir o princípio…

Corrigindo o princípio

certo

Isto está correto.

Definição de contratos (Interfaces)

namespace Solid.Certo.ISP.Contract
{
    public interface ISaldo
    {
        #region Public Methods

        #region Public Methods

        void Atualizar();

        #endregion Public Methods
    }
}
namespace Solid.Certo.ISP.Contract
{
    public interface ISaldoEstoque: ISaldo
    {
    }
}
namespace Solid.Certo.ISP.Contract
{
    public interface ISaldoFinanceiro: ISaldo
    {
    }
}

Definição de abstrações

using Solid.Certo.ISP.Contract;

namespace Solid.Certo.ISP.Abstract
{
    public abstract class SaldoBase: ISaldo
    {
        #region Public Methods

        public abstract void Atualizar();

        #endregion Public Methods
    }
}
using System;
using Solid.Certo.ISP.Contract;

namespace Solid.Certo.ISP.Abstract
{
    public abstract class SaldoEstoqueBase: SaldoBase, ISaldoEstoque
    {
        public override void Atualizar()
        {
            Console.WriteLine("Saldo de estoque atualizado.");
        }
    }
}
using System;
using Solid.Certo.ISP.Contract;

namespace Solid.Certo.ISP.Abstract
{
    public abstract class SaldoFinanceiroBase: SaldoBase, ISaldoFinanceiro
    {
        #region Public Methods

        public override void Atualizar()
        {
            Console.WriteLine("O saldo financeiro foi atualizado.");
        }

        #endregion Public Methods
    }
}

Classes concretas

using Solid.Certo.ISP.Abstract;

namespace Solid.Certo.ISP
{
    public class SaldoEstoque: SaldoEstoqueBase
    {
    }
}
using Solid.Certo.ISP.Abstract;

namespace Solid.Certo.ISP
{
    public class SaldoFinanceiro: SaldoFinanceiroBase
    {
    }
}

Como podemos perceber, ao extrair as interfaces, abstrações e definindo as classes concretas, utilizamos os 4 princípios que aprendemos até agora:

  •  Cada uma tem sua responsabilidade, (SRP);
    • As classes de saldo, só atualizam saldos. Removemos o a baixa de estoque da interface mais genérica.
  • Podem sofrer heranças e ter seus comportamentos modificados, (OCP);
    • Eu posso herdar a classe de saldos de base e modificar o comportamento do método “Atualizar()“.
  • Podem ser convertidas entre suas generalizações e especializações (LSP);
    • Eu posso passar, como parâmetro por exemplo, qualquer classe de saldo e chamar um método que realiza os cálculos de saldos da aplicação.
  • E por fim, interfaces e abstrações (Este princípio – ISP);
    • Abstraímos todas as interfaces e abstrações das classes que necessitamos para que nossas classes de cálculo de saldos façam apenas cálculos de saldos.

O fonte utilizado aqui pode ser baixado pelo GITHub em https://github.com/desenvolvedores-net/ArtigoSOLID


É isso ai pessoal 🙂
Até o próximo
♦ Marcelo

Marcelo

Nascido em Juruaia/MG em uma fazenda de criação de búfalos, e residindo na região Sul do Brasil.
Trabalha com desenvolvimento de aplicações desde os 17 anos. Atualmente é Arquiteto Organizacional na Unimake Software.
Para saber mais ... http://desenvolvedores.net/marcelo
[]'s

Você vai gostar de...

Postagens populares.

Deixe um comentário

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.