window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster już istnieje') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Programowanie obiektowe. To nie tak jak myślisz - The Codest
The Codest
  • O nas
  • Nasze Usługi
    • Software Development
      • Frontend Development
      • Backend Development
    • Zespoły IT
      • Programiści frontendowi
      • Backend Dev
      • Inżynierowie danych
      • Inżynierowie rozwiązań chmurowych
      • Inżynierowie QA
      • Inne
    • Konsultacje IT
      • Audyt i doradztwo
  • Branże
    • Fintech i bankowość
    • E-commerce
    • Adtech
    • Healthtech
    • Produkcja
    • Logistyka
    • Motoryzacja
    • IOT
  • Wartość dla
    • CEO
    • CTO
    • Delivery Managera
  • Nasz zespół
  • Case Studies
  • Nasze Know How
    • Blog
    • Meetups
    • Webinary
    • Raporty
Kariera Skontaktuj się z nami
  • O nas
  • Nasze Usługi
    • Software Development
      • Frontend Development
      • Backend Development
    • Zespoły IT
      • Programiści frontendowi
      • Backend Dev
      • Inżynierowie danych
      • Inżynierowie rozwiązań chmurowych
      • Inżynierowie QA
      • Inne
    • Konsultacje IT
      • Audyt i doradztwo
  • Wartość dla
    • CEO
    • CTO
    • Delivery Managera
  • Nasz zespół
  • Case Studies
  • Nasze Know How
    • Blog
    • Meetups
    • Webinary
    • Raporty
Kariera Skontaktuj się z nami
Strzałka w tył WSTECZ
2019-05-06
Software Development

Programowanie obiektowe. To nie jest sposób myślenia

Paweł Wal

Dzięki dostępnym obecnie darmowym zasobom, książkom, zajęciom online i bootcampom, każdy może nauczyć się kodować. Jednak nadal istnieje luka jakościowa między kodowaniem a inżynierią oprogramowania. Czy musi ona istnieć?

Swoje pierwsze "Hello World" napisałem ponad dwadzieścia lat temu - takiej odpowiedzi udzielam, gdy ktoś pyta mnie, jak długo jestem programistą. Przez ostatnie dziesięć lat cieszyłem się karierą, w której mogłem dotknąć kod Prawie codziennie - to odpowiedź, której udzielam na pytanie, jak długo jestem profesjonalnym programistą.

Jak długo jestem inżynier oprogramowania? Powiedziałbym, że około pięciu lat. Chwila, te liczby nie wydają się zgadzać! Więc co się zmieniło? Kogo mógłbym uznać za inżyniera oprogramowania, a kogo "tylko" za programistę?

Definicja inżyniera oprogramowania

Kodowanie jest stosunkowo łatwe. To już nie tylko mnemoniki asemblerowe na absurdalnie ograniczonych systemach. A jeśli używasz czegoś tak ekspresyjnego i potężnego jak Ruby, jest to jeszcze łatwiejsze.

Po prostu podnosisz bilet, znajdujesz miejsce, w którym musisz wstawić kod, wymyślasz logikę, którą musisz tam umieścić, i bum - gotowe. Jeśli jesteś nieco bardziej zaawansowany, upewnij się, że twój kod jest ładny. Logicznie podzielony na metody. Ma przyzwoite specyfikacje, które testują nie tylko szczęśliwą ścieżkę. To właśnie robi dobry programista.

Inżynier oprogramowania nie myśli już metodami i klasami, przynajmniej nie przede wszystkim. Z mojego doświadczenia wynika, że inżynierowie oprogramowania myślą przepływami. Przede wszystkim widzą grzmiącą, szalejącą rzekę danych i interakcji przepływającą przez system. Myślą o tym, co muszą zrobić, aby odwrócić lub zmienić ten przepływ. Ładny kod, logiczne metody i świetne specyfikacje przychodzą niemal po namyśle.

To żółwie przez całą drogę w dół

Ludzie generalnie myślą w określony sposób o większości interakcji z rzeczywistością. Z braku lepszego określenia nazwijmy to perspektywą "top-down". Jeśli mój mózg pracuje nad przygotowaniem sobie filiżanki herbaty, to w pierwszej kolejności określi ogólne kroki: idę do kuchni, nastawiam czajnik, przygotowuję herbatę, nalewam wodę, wracam do biurka.

Nie będzie najpierw zastanawiać się, którego kubka użyć, gdy stoję zdezorientowany przy biurku; to zdezorientowanie nastąpi później, gdy stanę przed szafką. Nie weźmie pod uwagę, że herbata może się skończyć (a przynajmniej, że może się skończyć dobry rzeczy). Jest szeroki, reaktywny i podatny na błędy. Podsumowując - bardzo człowiek w przyrodzie.

Gdy inżynier oprogramowania rozważa zmiany w nieco skomplikowanym przepływie danych, naturalnie zrobi to w podobny sposób. Rozważmy tę przykładową historię użytkownika:

Klient zamawia widżet. Przy wycenie zamówienia należy wziąć pod uwagę następujące kwestie:

  • Cena bazowa widżetu w lokalizacji użytkownika
  • Kształt widżetu (modyfikator ceny)
  • Czy jest to zamówienie pospieszne (modyfikator ceny)
  • Czy dostawa zamówienia odbywa się w dniu wolnym od pracy w lokalizacji użytkownika (modyfikator ceny)

To wszystko może wydawać się wymyślone (i oczywiście takie jest), ale nie jest to dalekie od niektórych rzeczywistych historii użytkowników, które miałem ostatnio przyjemność zmiażdżyć.

Przejdźmy teraz przez proces myślowy, który inżynier oprogramowania może zastosować, aby sobie z tym poradzić:

"Musimy poznać użytkownika i jego zamówienie. Następnie zaczniemy obliczać sumę. Zaczniemy od zera. Następnie zastosujemy modyfikator kształtu widżetu. Następnie opłata za pośpiech. Następnie sprawdzamy, czy jest święto, bum, gotowe przed lunchem!".

Ach, ten pęd, który może przynieść prosta historia użytkownika. Ale inżynier oprogramowania jest tylko człowiekiem, a nie idealną wielowątkową maszyną, a powyższy przepis jest ogólny. Inżynier musi więc myśleć głębiej:

"Modyfikator kształtu widżetu jest... och, to bardzo zależy od widżetu, prawda? I mogą się one różnić w zależności od lokalizacji, nawet jeśli nie teraz, to w przyszłości". ich zdaniem, wcześniej spalonych przez zmieniające się wymagania biznesowe, "może być również opłata za pośpiech. A święta są również bardzo specyficzne dla lokalizacji, augh, i strefy czasowe będą zaangażowane! Miałem tutaj artykuł o radzeniu sobie z czasami w różnych strefach czasowych w Railsach... ooh, zastanawiam się, czy czas zamówienia jest przechowywany ze strefą w bazie danych! Lepiej sprawdzić schemat."

W porządku, inżynierze oprogramowania. Stop. Powinieneś parzyć herbatę, ale siedzisz przed szafką i zastanawiasz się, czy kwiecista filiżanka w ogóle pasuje do twojego problemu z herbatą.

Widżet do parzenia idealnego kubka

Ale tak właśnie może się stać, gdy próbujesz zrobić coś tak nienaturalnego dla ludzkiego mózgu, jak myślenie na kilku poziomach szczegółowości jednocześnie.

Po krótkim przeszukaniu obszernego arsenału linków dotyczących obsługi strefy czasowej nasz inżynier zbiera się w sobie i zaczyna rozkładać to na rzeczywisty kod. Gdyby spróbowali naiwnego podejścia, mogłoby to wyglądać mniej więcej tak:

 def calculate_price(user, order)
   order.price = 0
   order.price = WidgetPrices.find_by(widget_type: order.widget.type).price
   order.price = WidgetShapes.find_by(widget_shape: order.widget.shape).modifier
   ...
 end

I tak dalej i dalej, w ten rozkosznie proceduralny sposób, tylko po to, by zostać mocno zablokowanym przy pierwszym przeglądzie kodu. Bo jeśli się nad tym zastanowić, to całkowicie normalne jest myślenie w ten sposób: najpierw szerokie pociągnięcia, a szczegóły znacznie później. Na początku nawet nie myślałeś, że nie masz dobrej herbaty, prawda?

Nasz inżynier jest jednak dobrze wyszkolony i nie jest mu obcy Service Object, więc oto, co zaczyna się dziać:

class BaseOrderService
def self.call(user, order)
new(user, order).call
end

def initialize(user, order)
@user = użytkownik
@order = zamówienie
end

def call
puts "[WARN] Zaimplementuj niedomyślne wywołanie dla #{self.class.name}!"
user, order
end
end

class WidgetPriceService < BaseOrderService; end
class ShapePriceModifier < BaseOrderService; end
class RushPriceModifier < BaseOrderService; end
class HolidayDeliveryPriceModifier < BaseOrderService; end

class OrderPriceCalculator < BaseOrderService
def call
user, order = WidgetPriceService.call(user, order)
user, order = ShapePriceModifier.call(user, order)
user, order = RushPriceModifier.call(user, order)
user, order = HolidayDeliveryPriceModifier.call(user, order)
user, order
end
end
```

Dobrze! Teraz możemy zastosować dobre TDD, napisać dla niego przypadek testowy i dopracować klasy, aż wszystkie elementy znajdą się na swoim miejscu. I to też będzie piękne.

Jak również całkowicie niemożliwe do uzasadnienia.

Wrogiem jest państwo

Oczywiście, są to dobrze oddzielone obiekty z pojedynczymi obowiązkami. Problem polega jednak na tym, że wciąż są to obiekty. Wzorzec obiektu usługowego z jego "wymuszonym udawaniem, że ten obiekt jest funkcją" jest tak naprawdę kulą u nogi. Nic nie stoi na przeszkodzie, by wywołać HolidayDeliveryPriceModifier.new(user, order).something_else_entirely. Nic nie stoi na przeszkodzie, aby dodać wewnętrzny stan do tych obiektów.

Nie wspominając o użytkownik i porządek są również obiektami, a manipulowanie nimi jest tak proste, jak szybkie zakradnięcie się do nich order.save gdzieś w tych skądinąd "czystych" obiektach funkcjonalnych, zmieniając podstawowe źródło prawdy, czyli bazę danych, stan. W tym wymyślonym przykładzie nie jest to wielka sprawa, ale z pewnością może wrócić, aby cię ugryźć, jeśli ten system stanie się bardziej złożony i rozszerzy się na dodatkowe, często asynchroniczne części.

Inżynier miał dobry pomysł. I użył bardzo naturalnego sposobu wyrażenia tego pomysłu. Ale wiedza o tym, jak wyrazić ten pomysł - w piękny i łatwy do zrozumienia sposób - została prawie uniemożliwiona przez podstawowy paradygmat OOP. A jeśli ktoś, kto jeszcze nie dokonał skoku w kierunku wyrażania swoich myśli jako przekierowań przepływu danych, spróbuje mniej umiejętnie zmienić podstawowy kod, wydarzy się coś złego.

Stawanie się funkcjonalnie czystym

Gdyby tylko istniał paradygmat, w którym wyrażanie swoich pomysłów w postaci przepływów danych byłoby nie tylko łatwe, ale wręcz konieczne. Gdyby rozumowanie mogło być proste, bez możliwości wprowadzenia niepożądanych efektów ubocznych. Gdyby dane mogły być niezmienne, tak jak kwiecisty kubek, w którym parzysz herbatę.

Tak, oczywiście żartuję. Taki paradygmat istnieje i nazywa się programowaniem funkcjonalnym.

Zastanówmy się, jak powyższy przykład mógłby wyglądać w ulubionym przeze mnie Elixirze.

defmodule WidgetPrices do
def priceorder([użytkownik, zamówienie]) do
[użytkownik, zamówienie]
|> widgetprice
|> shapepricemodifier
|> rushpricemodifier
|> holidaypricemodifier
koniec

defp widgetprice([user, order]) do
%{widget: widget} = zamówienie
price = WidgetRepo.getbase_price(widget)
[user, %{order | price: price }]
end

defp shapepricemodifier([user, order]) do
%{widget: widget, price: currentprice} = order
modifier = WidgetRepo.getshapeprice(widget)
[user, %{order | price: currentprice * modifier} ]
end

defp rushpricemodifier([user, order]) do
%{rush: rush, price: currentprice} = order
if rush do
[user, %{order | price: currentprice * 1.75} ]
else
[user, %{order | price: current_price} ]
end
end

defp holidaypricemodifier([user, order]) do
%{date: date, price: currentprice} = order
modifier = HolidayRepo.getholidaymodifier(user, date)
[user, %{order | price: currentprice * modifier}]
end
end
```

Można zauważyć, że jest to w pełni rozwinięty przykład tego, jak faktycznie można osiągnąć historię użytkownika. To dlatego, że jest ona mniej skomplikowana niż w Rubim. Używamy kilku kluczowych funkcji unikalnych dla Elixira (ale ogólnie dostępnych w językach funkcyjnych):

Czyste funkcje. W rzeczywistości nie zmieniamy przychodzących porządek w ogóle, po prostu tworzymy nowe kopie - nowe iteracje stanu początkowego. Nie przeskakujemy też na bok, by cokolwiek zmienić. A nawet gdybyśmy chcieli, porządek jest tylko "głupią" mapą, nie możemy wywołać order.save ponieważ po prostu nie wie, co to jest.

Dopasowywanie wzorców. Raczej podobnie do destrukturyzacji ES6, pozwala nam to na wyrwanie cena i widżet z zamówienia i przekazać je dalej, zamiast zmuszać naszych kumpli WidgetRepo i HolidayRepo wiedzieć, jak radzić sobie z pełnym porządek.

Operator rur. Widziane w price_order, pozwala nam przekazywać dane przez funkcje w swego rodzaju "potoku" - koncepcja natychmiast znana każdemu, kto kiedykolwiek uruchomił ps aux | grep postgres aby sprawdzić, czy to cholerstwo nadal działa.

Tak właśnie myślisz

Efekty uboczne nie są tak naprawdę podstawową częścią naszego procesu myślowego. Po nalaniu wody do filiżanki zazwyczaj nie martwimy się, że błąd w czajniku może spowodować jego przegrzanie i eksplozję - przynajmniej nie na tyle, by grzebać w jego wnętrzu w celu sprawdzenia, czy ktoś przypadkiem go nie zostawił. explode_after_pouring odwrócony wysoko.

Droga od programisty do inżyniera oprogramowania - wykraczająca poza martwienie się o obiekty i stany, do martwienia się o przepływy danych - może w niektórych przypadkach zająć lata. Z pewnością tak było w przypadku mojej osoby wychowanej na OOP. Dzięki językom funkcjonalnym zaczynasz myśleć o przepływach pierwszej nocy.

Zrobiliśmy inżynieria oprogramowania skomplikowane dla nas samych i dla każdego nowicjusza w tej dziedzinie. Programowanie nie musi być trudne i wymagające. Może być łatwe i naturalne.

Nie komplikujmy tego i przejdźmy już do funkcjonalności. Ponieważ tak właśnie myślimy.

Czytaj także:

  • Dlaczego technologia przyspiesza rozwój handlu elektronicznego, platform handlowych i firm SaaS?
  • Dlaczego Ruby on Rails to dobry wybór do tworzenia oprogramowania? Moje osobiste przemyślenia

Powiązane artykuły

Software Development

Tworzenie przyszłościowych aplikacji internetowych: spostrzeżenia zespołu ekspertów The Codest

Odkryj, w jaki sposób The Codest wyróżnia się w tworzeniu skalowalnych, interaktywnych aplikacji internetowych przy użyciu najnowocześniejszych technologii, zapewniając płynne doświadczenia użytkowników na wszystkich platformach. Dowiedz się, w jaki sposób nasza wiedza napędza transformację cyfrową i biznes...

THEECODEST
Software Development

10 najlepszych firm tworzących oprogramowanie na Łotwie

Dowiedz się więcej o najlepszych łotewskich firmach programistycznych i ich innowacyjnych rozwiązaniach w naszym najnowszym artykule. Odkryj, w jaki sposób ci liderzy technologiczni mogą pomóc w rozwoju Twojej firmy.

thecodest
Rozwiązania dla przedsiębiorstw i scaleupów

Podstawy tworzenia oprogramowania Java: Przewodnik po skutecznym outsourcingu

Zapoznaj się z tym niezbędnym przewodnikiem na temat skutecznego tworzenia oprogramowania Java outsourcing, aby zwiększyć wydajność, uzyskać dostęp do wiedzy specjalistycznej i osiągnąć sukces projektu z The Codest.

thecodest
Software Development

Kompletny przewodnik po outsourcingu w Polsce

Wzrost liczby outsourcing w Polsce jest napędzany przez postęp gospodarczy, edukacyjny i technologiczny, sprzyjający rozwojowi IT i przyjazny klimat dla biznesu.

TheCodest
Rozwiązania dla przedsiębiorstw i scaleupów

Kompletny przewodnik po narzędziach i technikach audytu IT

Audyty IT zapewniają bezpieczne, wydajne i zgodne z przepisami systemy. Dowiedz się więcej o ich znaczeniu, czytając cały artykuł.

The Codest
Jakub Jakubowicz CTO & Współzałożyciel

Subskrybuj naszą bazę wiedzy i bądź na bieżąco!

    O nas

    The Codest - Międzynarodowa firma programistyczna z centrami technologicznymi w Polsce.

    Wielka Brytania - siedziba główna

    • Office 303B, 182-184 High Street North E6 2JA
      Londyn, Anglia

    Polska - lokalne centra technologiczne

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Warszawa, Polska

      The Codest

    • Strona główna
    • O nas
    • Nasze Usługi
    • Case Studies
    • Nasze Know How
    • Kariera
    • Słownik

      Nasze Usługi

    • Konsultacje IT
    • Software Development
    • Backend Development
    • Frontend Development
    • Zespoły IT
    • Backend Dev
    • Inżynierowie rozwiązań chmurowych
    • Inżynierowie danych
    • Inne
    • Inżynierowie QA

      Raporty

    • Fakty i mity na temat współpracy z zewnętrznym partnerem programistycznym
    • Z USA do Europy: Dlaczego amerykańskie startupy decydują się na relokację do Europy?
    • Porównanie centrów rozwoju Tech Offshore: Tech Offshore Europa (Polska), ASEAN (Filipiny), Eurazja (Turcja)
    • Jakie są największe wyzwania CTO i CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Warunki korzystania z witryny

    Copyright © 2025 by The Codest. Wszelkie prawa zastrzeżone.

    pl_PLPolish
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek pl_PLPolish