SÓLIDO == OOP?

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 object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.

, , "" . , , .



"", , "" . , ! , ( ), :



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 ( classhaskell ):



// 
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 , . , !




All Articles