Acho que não me enganarei se disser que na maioria das vezes as pessoas perguntam sobre os princípios do SOLID durante as entrevistas. Tecnologias, linguagens e estruturas são diferentes, mas os princípios de codificação são geralmente semelhantes: SÓLIDO, BEIJO, SECO, YAGNI, GRASP e similares valem a pena conhecer para todos.
Na indústria moderna, o paradigma OOP domina há muitas décadas, e muitos desenvolvedores têm a impressão de que é o melhor ou até pior - o único. Há um ótimo vídeo sobre este tópico Por que a programação funcional não é a norma? sobre o desenvolvimento de linguagens / paradigmas e as raízes de sua popularidade.
O SOLID foi originalmente descrito por Robert Martin para OOP e é percebido por muitos como se referindo apenas ao OOP, mesmo que a wikipedia nos fale sobre isso, vamos ver se esses princípios estão tão ligados ao OOP?
Responsabilidade Única
Vamos aproveitar a visão do tio Bob sobre o SOLID :
Este princípio foi descrito no trabalho de Tom DeMarco e Meilir Page-Jones. Eles chamavam de coesão. Eles definiram coesão como a relação funcional dos elementos de um módulo. Neste capítulo, mudaremos um pouco esse significado e relacionaremos a coesão às forças que causam a mudança de um módulo ou classe.
Cada módulo deve ter um motivo para alterações (e não fazer nada, pois muitas respostas) e, como o próprio autor explicou em um dos vídeos, isso significa que as mudanças devem vir de um grupo / função de pessoas, por exemplo, o módulo deve mudar apenas de acordo com solicitações de analista de negócios, designer, especialista em DBA, contador ou advogado.
Observe que esse princípio se aplica a um módulo, que em OOP é uma classe. Como existem módulos em quase todos os idiomas, esse princípio não se limita ao POO.
Aberto fechado
SOFTWARE ENTITIES (CLASSES, MODULES, FUNCTIONS, ETC.) SHOULD BE OPEN FOR EXTENSION, BUT CLOSED FOR MODIFICATION
Bertrand Meyer
- , — , ( ) .
( , ). , . map
, filter
, reduce
, . , foldLeft
!
def map(xs: Seq[Int], f: Int => Int) =
xs.foldLeft(Seq.empty) { (acc, x) => acc :+ f(x) }
def filter(xs: Seq[Int], f: Int => Boolean) =
xs.foldLeft(Seq.empty) { (acc, x) => if (f(x)) acc :+ x else acc }
def reduce(xs: Seq[Int], init: Int, f: (Int, Int) => Int) =
xs.foldLeft(init) { (acc, x) => f(acc, x) }
, , — .
Liskov Substitution
If for each objecto1
of typeS
there is an objecto2
of typeT
such that for all programsP
defined in terms ofT
, the behavior ofP
is unchanged wheno1
is substituted foro2
thenS
is a subtype ofT
.
, , "" . , , .
"", , "" . , ! , ( ), :
static <T> T increment(T number) {
if (number instanceof Integer) return (T) (Object) (((Integer) number) + 1);
if (number instanceof Double) return (T) (Object) (((Double) number) + 1);
throw new IllegalArgumentException("Unexpected value "+ number);
}
, T
, , "" (.. ), , — .
, , "" , , . , , ( ), , (ad hoc) . .
Interface Segregation
, , , .
, , "" ! , (type classes), .
Comparable
Java
type class Ord
haskell
( class
— haskell
):
//
class Ord a where
compare :: a -> a -> Ordering
"", , , compare
( Comparable
). .
Dependency Inversion
Depend on abstractions, not on concretions.
Dependency Injection, — , :
int first(ArrayList<Integer> xs) // ArrayList ->
int first(Collection<Integer> xs) // Collection ->
<T> T first(Collection<T> xs) //
: ( ):
def sum[F[_]: Monad](xs: Seq[F[Int]]): F[Int] =
if (xs.isEmpty) 0.pure
else for (head <- xs.head; tail <- all(xs.tail)) yield head + tail
sum[Id](Seq(1, 2, 3)) -> 6
sum[Future](Seq(queryService1(), queryService2())) -> Future(6)
, , .
SOLID , . , SOLID , . , !