OOP i FP, przyjaciele czy wrogowie?
Mateusz Pokora
Mateusz
Pokora
software developer @
https://twitter.com/pokorson
https://github.com/pokorson
Programowanie obiektowe
- Modelowanie obiektów posiadających dane oraz funkcje operujące na nich
Programowanie obiektowe
class Car
attr_accessor :name, :speed, :color
def accelerate
@speed += 10
end
def break
@speed = 0
end
end
myCar = Car.new "Toyota" 50 "black"
myCar.accelerate
Programowanie funkcyjne
- Rozdzielenie na dane i funkcje operujące na danych
- Immutable data
- Izolacja side effect
- Currying / Partial application
- Kompozycja funkcji
- Bardziej czytelny kod
- Bardziej reużywalny kod
- Łatwiejsze testowanie
Języki obiektowe a funkcyjne
- Paradygmaty programowania określają sposób rozwiązywania pewnych problemów
- Język programowania może wspierać pewne założenia lub nie, nie ogranicza nas to jednak we wdrażaniu tych założeń
Immutable data
- Nie możemy zmienić raz zadeklarowanych struktur (obiekty, listy)
- Nie możemy zmienić przypisania zmiennej
- Tworzymy nowe obiekty ze zmodyfikowanymi polami
Immutable data
class Car
attr_accessor :name, :speed, :color
def accelerate
@speed += 10
end
def break
@speed = 0
end
end
1 myCar = Car.new "Toyota" 50 "black"
...
50 myCar.accelerate
Immutable data
class Car
attr_reader :name, :speed, :color
def self.accelerateCar(car)
self.new car.name, car.speed + 10, car.color
end
end
1 myCar = Car.new "Toyota" 50 "black"
...
50 acceleratedCar = Car.accelerateCar myCar
Pure functions
- Otrzymując te same argumenty zawsze zwracamy ten sam wynik
# pure
def add(a, b)
a + b
end
# not pure
def persistCar(car)
DB.save(car)
end
Izolacja side effect
Przykładowe side effecty
- Zapis do bazy danych
- Zapisanie obrazka w S3
- Wysłanie zapytania HTTP
Izolacja side effect
Brak mechanizmów w samym języku w Ruby
class Car
attr_reader :name, :speed, :color, :owner
def self.accelerateCar(car)
Notifications.sendSpeedAlert car
self.new car.name, car.speed + 10, car.color
end
end
myCar = Car.new "Toyota" 50 "black" current_user
Car.accelerateCar myCar
Currying
- Pojedyncze aplikowanie argumentów do funkcji
add = -> (x, y) { x + y }
add.call(5, 10) => 15
-------------------------
# Currying
add = -> (x, y) { x + y }
createrAdder = add.curry
add5 = createAdder.call(5)
add5.call(10) => 15
Kompozycja funkcji
class Car
attr_accessor :name, :speed, :color
def accelerate
@speed += 10
end
def slow_down
@speed -= 10
end
def stop
@speed = 0
end
end
myCar = Car.new "Toyota" 50 "black"
myCar.accelerate
.accelerate
.slowDown
.accelerate
.stop
Kompozycja funkcji
- Łączenie funkcji przekazując wynik wykonania jednej jako argument kolejnej
- Skomplikowane rzeczy tworzymy dzieląc je na mniejsze problemy
def add5(x)
x + 5
end
def multiplyBy10(x)
x * 10
end
multiplyBy10( add5 3 ) === compose(multiplyBy10, add5).call(3)
Kompozycja funkcji
# String -> List[String]
getWords = -> (str) { str.split(' ') }
# List[String] -> Int
listLength = -> (list) { list.length }
# String -> Int
getWordsCount = compose(
listLength,
getWords
)
getWordsCount.call("hello LRUG") => 2
- Brak funkcji compose w Ruby, jest ona jednak łatwa do zaimplementowania samemu ( 1 linijka)
Wady FP
- Początkowo większa czasochłonność w wykonywaniu zadań
- Wydajność (jeśli język nie jest funkcyjny)
- Duża alokacja pamięci
- Zbyt restrykcyjne trzymanie się zasad może w efekcie skomplikować kod
- Odrzucenie reguł powszechnie przyjętych przez community może utrudnić wejście w projekt nowym osobom
Dziękuję za uwagę
OOP i FP w ruby
By vrael560
OOP i FP w ruby
- 481