SOLID == OOP?

I guess I won't be mistaken if I say that most often people ask about SOLID principles during interviews. Technologies, languages ​​and frameworks are different, but the principles of coding are generally similar: SOLID, KISS, DRY, YAGNI, GRASP and the like are worth knowing for everyone.



In the modern industry, the OOP paradigm has dominated for many decades, and many developers have the impression that it is the best or even worse - the only one. There is a great video on this topic Why Isn't Functional Programming the Norm? about the development of languages ​​/ paradigms and the roots of their popularity.



SOLID was originally described by Robert Martin for OOP and is perceived by many as referring only to OOP, even wikipedia tells us about it, let's look at whether these principles are so tied to OOP?



Single Responsibility



Let's enjoy Uncle Bob 's insight into SOLID :



This principle was described in the work of Tom DeMarco and Meilir Page-Jones. They called it cohesion. They defined cohesion as the functional relatedness of the elements of a module. In this chapter we'll shift that meaning a bit, and relate cohesion to the forces that cause a module, or a class, to change.

Each module should have one reason for changes (and not at all do one thing, as many answer) and as the author himself explained in one of the videos, this means that changes should come from one group / role of people, for example, the module should only change according to business analyst, designer, DBA specialist, accountant or lawyer requests.



Please note that this principle applies to a module, which in OOP is a class. There are modules in almost all languages, so this principle is not limited to OOP.



Open Closed



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




All Articles