interface Numeral {
Numeral plus(Numeral other);
Numeral minus(Numeral other);
Numeral multiply(Numeral other);
Numeral divide(Numeral other);
}
Este príncipio versa sobre o design de interfaces. Interfaces são como classes abstratas, mas que não implementam comportamento. São apenas o conjunto das assinaturas dos métodos que alguma outra classe irá implementar. Por exemplo, a classe abaixo define as quatro operações aritmeticas:
interface Numeral {
Numeral plus(Numeral other);
Numeral minus(Numeral other);
Numeral multiply(Numeral other);
Numeral divide(Numeral other);
}
Não definimos como iremos implementar estas operações, apenas definimos as operações em si.
O que o Princípio de Segregação de Interfaces dita é que:
Uma classe não deve ser obrigada a implementar um contrato que não pode cumprir.
No nosso caso acima, se pensarmos em números racioanais todas aquelas operações são possiveis, mas para números inteiros não pois a divisão de um inteiro por outro inteiro nem sempre é um inteiro.
Poderiamos pensar que podemos reutilizar a operação de divide
e realizar a divisão inteira, só que isso seria uma violação do contrato original de Numeral
. Para dois Numeral
a
e b
o contrato implica que
var q = a.divide(b);
assert a == q.multiply(b);
Mas para inteiros precisamos duas operações: a divisão inteira e o resto (remainder).
var q = a.integerDivide(b);
var r = a.reminder(b);
assert a == q.multiply(b).plus(r);
Então uma classe Integer
não poderia implementar Numeral
, equanto que uma classe Rational
poderia. O que o Princípio de Segregação de Interfaces indica é que a solução está em segregar as interfaces, ou seja, fazer interfaces diferentes.
Nesse caso fariamos um interface apenas para as operações de soma, uma apenas para as operações de multiplicação e outra ainda para as de divisão:
interface Summable {
Summable plus(Summable other);
}
interface Multipliable {
Multipliable plus(Multipliable other);
}
interface Divisable {
Divisable divide(Divisable other);
}
interface IntegerDivisable {
IntegerDivisable integerDivide(IntegerDivisable other);
IntegerDivisable remainder(IntegerDivisable other);
}
Assim, poderiamos construir as classes Rational
e Integer
:
interface Rational extends Summable, Multipliable, Divisable {
}
interface Integer extends Summable, Multipliable, IntegerDivisable {
}
Então, para operações de soma e multiplicação, podemos usar qualquer uma das duas, mas para divisões depende de que divisão queremos.
A API de coleções do Java não segue este principio completamente. Uma interface List
, por exemplo, sempre tem métodos para adicionar, substituir e remover elementos, independentente se a implementação é imutável ou não. Para os casos em que a operação não é desejável ou não ha implementação, uma exceção é lançada.
A não segregação de interfaces nestes caso dificulta o uso, pois podemos tentar invocar um método que não tem implementação e obter uma exceção, violando o Principio da Mínima Surpresa.
Esta falta de segregação também obriga a uma falha em seguir o Príncípio de Substituição de Liskov
O Príncipio de Segregação de Interfaces foi proposto por Robert C. Martin num artigo, em 1996.
Também pela mão de Robert C. Martin este princípio entrou para o top 5 dos Principios de Orientação de Objetos, juntos conhecidos como os "Principios SOLID" (SOLID Principles) num trocadilho de palavras sugerindo que seguir os princípios SOLID, tornaria o código sólido, por oposição a frágil. O Princípio de Segregação de Interfaces corresponde ao I pelo seu nome em inglês: Interface Segregation Principle.