Top Banner
REVOLUTION OBIEKTOWE PODEJŚCIE DO ROZWIĄZAŃ KOLEJOWYCH Tomek Jasiulek Twitter: @TomaszJasiulek Github: tomasz-jasiulek
63

Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

Jul 20, 2015

Download

Technology

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

REVOLUTIONOBIEKTOWE PODEJŚCIE

DO ROZWIĄZAŃ KOLEJOWYCH

Tomek Jasiulek

Twitter : @TomaszJasiulekGithub: tomasz-jasiulek

Page 2: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

REVOLUTION RAILS REVOLUTION ?RAILS EVOLUTION ?

Page 3: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

REWOLUCJA

Page 4: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

EWOLUCJA

Page 5: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

PODSTAWOWY ZESTAW NARZĘDZI KOLEJARZA

• Kontroler / Mailer

• Model

• Widok/Szablon

• Helper

Page 6: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

ZALETY

• Łatwe w użyciu

• Proste do nauki

• Szybki proces developmentu

• Dużo dostępnej informacji - guides, stack-overflow, dokumentacja

• Sprawdza się w małych projektach

Page 7: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

WADY• Helper’y zaśmiecają globalną przestrzeń

• Przerośnięte modele

• Skomplikowany kod kontrolerów

• Łamanie SRP

• Kod Spaghetti w przypadku bardziej skomplikowanej logiki biznesowej

• Callback hell

Page 8: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

FAT MODEL, SKINNY CONTROLLER

• Jeszcze bardziej rozdmuchane modele

• Zgrabniejszy kod kontrolerów

• Przeniesienie problemu w inne miejsce

• No i dalej mamy kłopoty…

Page 9: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

JAK ŻYĆ?

Page 10: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

OOP NA RATUNEK• SINGLE RESPONSIBILITY

• OPEN-CLOSE

• LISKOV SUBSTITUTION

• INTERFACE SEGREGATION

• DEPENDENCY INVERSION PRINCIPLE

• LAW OF DEMETER

• INHERITANCE

• COMPOSITION

• DELEGATION

Page 11: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

“ZAAWANSOWANE” ROZWIĄZANIA KOLEJOWE W PRAKTYCE

Page 12: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

SPOILER: NIE BĘDZIE DIAGRAMÓW UML

Page 13: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach
Page 14: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

IDZIEMY NA DIETĘ… A NASZ CEL TO:• Klasy modeli ActiveRecord i tak mają dużą odpowiedzialność - nie dokładajmy im

dodaktowego ciężaru - potrzebna im dieta

• Klasy kontrolerów ActionController powinny być zgrabne, by widoczny był przepływ informacji od żądania do odpowiedzi serwera - niekoniecznie wszystkie szczegóły logiki biznesowej

• Szablony HTML, ERB, HAML - czyste, przejrzyste, bez zbędnej logiki zaciemniającej strukturę widoku

• Helpery - w railsach i tak jest ich dużo, nie dokładajmy specyficznych zachowań do globalnej przestrzeni nazw

Page 15: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

DODATKOWE SKUTKI DIETY

• Łatwiejsze testowanie aplikacji

• Oprogramowanie bardziej odporne na zmiany

• Kod rozdzielony na mniejsze i prostsze do ogarnięcia klasy o zawężonej odpowiedzialności (klasy PORO)

Page 16: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

CZEGO BĘDZIE WYMAGAĆ OD NAS DIETA?

• Dyscypliny

• Myślenia

• Implementacji dodatkowych klas - w większości PORO

Page 17: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

DEKORATOR

• Tak naprawdę od tego wszystko się zaczyna

• Podstawa wielu innych rozwiązań

• Rózni się tylko intencja programisty - jego zamiar wobec dekorowanego obiektu

Page 18: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

DEKORATOR

• Z wykorzystaniem kompozycji dodajemy, zmieniamy lub rozszerzamy bieżące zachowanie obiektu z poszanowaniem zasady zamknięcia - dekorowany obiekt pozostaje nietknięty, a jednocześnie otwarty na rozszerzenie.

• Proste w implementacji w Ruby ze względu na duck typing

Page 19: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

PRESENTER

• Pozwalają uporządkować kod odpowiadający za prezentację obiektu

• Odciążają klasy modelowe z metod związanych z prezentacją danych w określonych formatach

• Mniej helperów i metod z przedrostkami w widokach

Page 20: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

PRESENTER - KLASA BAZOWAclass BasePresenter attr_reader :object, :template

alias_method :h, :template

delegate :t, :current_locale, to: :h

def initialize(object, template) @object = object @template = template endend

Page 21: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

PRESENTER - HELPER

module ApplicationHelper def present(object, klass = nil) klass ||= "#{object.class}Presenter".safe_constantize return unless klass

presenter = klass.new(object, self) yield presenter if block_given? presenter endend

Page 22: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach
Page 23: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

PRESENTER - JAKO MODUŁmodule TaggablePresenter def tags_section return if object_tags.empty?

h.content_tag(:section, tags, class: 'keywords') end alias_method :keywords_section, :tags_section

def tags return if object_tags.empty?

h.capture do h.content_tag(:span) do h.concat t('.tags') + ' ' h.concat comma_separated_linked_tags end end end alias_method :keywords, :tags

private

def object_tags object.tags_for_locale(current_locale) end

def linked_tags object_tags.map do |tag| h.link_to(tag.name, h.search_path(q: tag.name)) end end

def comma_separated_linked_tags separate_by_comma(linked_tags) endend

Page 24: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

PRESENTER - PRZYKŁADOWA KLASAclass EventPresenter < BasePresenter include TaggablePresenter alias_method :event, :object delegate :event_type, :venue, :name, to: :event

def summary to_safe_html event.summary end

def start_date(date_format = :medium_datetime) if date_format.eql?(:date) h.l event.start_date.to_date else h.l event.start_date, format: date_format end end

def description to_safe_html event.description end

def event_type_logo_url h.image_url("/events/types/#{event_type_name_underscored}.png") end

def event_type_name event_type.name end

def event_type_name_underscored event_type_name.downcase.tr(' ', '_') end

def organizer return 'USZANOWANKO!!!' if event.organised_by_up? event.organiser_name end

def event_coverage if event.event_coverage.present? && event.event_coverage.published? event.event_coverage end end

private

def to_safe_html(text) text.to_s.html_safe endend

Page 25: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach
Page 26: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

PRESENTER - SZABLON HTML- present event do |presenter| .header = image_tag(presenter.event_type_logo_url, alt: presenter.event_type_name) %h3= link_to presenter.name, event %span.date= presenter.start_date .content %p= presenter.summary .details %p = t '.venue' %strong= presenter.venue %br = t '.organiser' %strong = presenter.organizer %span.more = link_to t('.details'), event

Page 27: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

PRESENTER - PODSUMOWANIE

Zalety:

• Jasne rozdzielenie logiki prezentacji od samej formy prezentacji(szablonu)

• Przejrzysty szablony(erb, haml itd…)

UWAGA!

• Mieszanie odpowiedzialności - presenter nie powinien tworzyć skomplikowanej struktury HTML a jedynie formatować dane i przygotowywać do prezentacji w formie np. HTML

Page 28: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach
Page 29: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

SERIALIZER - KREWNIAK PRESENTER’AGEM: ACTIVE_MODEL_SERIALIZERS

Cechy:

• Obiektowe podeście do serializacji

• Prezentuje obiekt w formacie JSON

• Idea podobna, tylko inny efekt końcowy

Zalety:

• Odciążenie modeli oraz kontrolerów od odpowiedzialności za serializację obiektów do formatu JSON

• Przeniesie tej odpowiedzialności do konkretnych klas serializerów, adekwatnych do przypadków użycia

Page 30: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

SERVICE OBJECT• Dedykowane klasy realizujące logikę

biznesową

• Wyciągamy logikę biznesową z kontrolerów i modeli i enkapsulujemy ją w postaci dedykowanych klas

• Wykorzystanie kompozycji

• Łatwe wykorzystanie klasy w innym kontekście np. w tasku rake’owym, workerze

Page 31: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

SERVICE OBJECT - PRZYKŁAD

class NewsletterSubscription < ActiveRecord::Base belongs_to :user validates :email, presence: true,

uniqueness: true, format: Devise.email_regexp

validates :accept_terms, acceptance: { accept: true }end

class UserSubscriber attr_accessor :user

def initialize(user) @user = user end

def call subscription = NewsletterSubscription.new subscription.user = @user subscription.email = @user.email subscription.subscribed = true subscription.accept_terms = true subscription.subscribe_date = Time.current subscription.save! endend

Page 32: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

SERVICE OBJECTS - PODSUMOWANIE

• Pozbywamy się zbędnych callbacków z modeli

• Zwiększamy czytelność kodu kontrolerów, dzięki wyodrębnieniu logiki biznesowej w osobne miejsce

• Modele nie sprawiają niespodzianek np. w przypadku importu (wysyłanie maili w callbackach itp.)

Page 33: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach
Page 34: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

VALUE OBJECT• Dedykowany klasa, która enkapsuluje wartość

oraz jej specyficzne zachowanie

• Można powiedzieć, że taki wrapper na prymitywne wartości lub wartości kalkulowane dynamicznie

• Nie zmieniają swojego wewnętrznego stanu

• Identyfikowany przez wartość

• Pozwala na porównywanie, sortowanie itp.

• Przykład: Fixnum, Range, Money

Page 35: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

VALUE OBJECT - PRZYKŁAD

class Ratio include Comparable extend Forwardable

def_delegators :value, :zero?

attr_reader :teachers, :students, :value

def self.zero new(0, 0) end

def initialize(teachers, students) @teachers = teachers.to_f @students = students.to_f if students == 0 @value = 0.0 else @value = @teachers/@students end end

def <=>(other) value <=> other.value end

def to_s "#{teachers.round}:#{students.round}" endend

Page 36: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

VALUE OBJECT - INNE PRZYKŁADY

• NUMER TELEFONU - możliwość wyodrebnienia numeru kierunkowego, pełnego numeru w postaci łańcucha tekstowego

• ADRES EMAIL - możliwość wyodrębnienia nazwy użytkownika, domeny lub pełnego w postaci łańcucha tekstowego

Page 37: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

VALUE OBJECT - PODSUMOWANIE

• Zachowanie pewnej klasy obiektów w jednym miejscu - a nie rozsiane po całej aplikacji

• Ewentualne zmiany, rozszerzenia wymagają zmian tylko i wyłącznie w jednej klasie

• Operujemy jeden poziom wyżej - operowanie na prymitywach niesie za sobą zagrożenia i ograniczenia

Page 38: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach
Page 39: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

ZAGADKA?

class Car < ActiveRecord::Base enum air_conditioning: { blank: 0, manual: 1, climatronic: 2 }end

CO ZWRÓCI WYWOŁANIE:

car = Car.newcar.air_conditioning = 'climatronic'car.present?

Page 40: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

ROZWIĄZANIE ZAGADKI

FALSE

Pozdrawiamy core team Railsów ;)

Page 41: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

NULL OBJECT

Page 42: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

NA RATUNEK NULL OBJECT

• Alernatywa dla: object.nil?, object.kind_of?, object.present?

• Implementuje interfejs obiektu zastępowanego

• Obiekt zastępczy, który udaje obiekt zastępowany, gdy ten nie istnieje(np. nie ma go w bazie/repozytorium)

Page 43: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

NULL OBJECT - PRZYKŁAD

class SidebarPresenter < BasePresenter def render_items h.capture do sidebar_items.each do |sidebar_item| h.concat render_sidebar_item(sidebar_item) end end end

def sidebar @sidebar ||= home? ? Sidebar.home : Sidebar.primary @sidebar ||= Domain::NullSidebar.new end

private

def sidebar_items sidebar.sidebar_items.includes(:sidebar_item_type) end

def render_sidebar_item(item) presenter = SidebarItems::PresenterFactory.create(item, h) presenter.render_item end

def home? h.controller_name.eql?('home') endend

Page 44: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

NULL OBJECT - PRZYKŁAD

class NullSidebar < NullObject def sidebar_items SidebarItem.none endend

class NullObject def method_missing(*args, &block) self endend

najprostsza implementacja(ma kilka wad)

Page 45: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

NULL OBJECT - PODSUMOWANIE• Nigdy więcej undefined method nazwa_metody for nil:NilClass

• Można skupić się na zadaniu, zamiast ciągłym sprawdzaniu wartości lub tożsamości obiektów

UWAGA!

• Należy uważać na trudne do wykrycia błędy, która na pierwszy rzut oka wyglądają jak normalne działanie programu

Page 46: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach
Page 47: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

INNE ROZWIĄZANIA

Page 48: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

FORM OBJECT

Sposób na ogarnięcie skomplikowanych formularzy i rozprawienie się z accepts_nested_attributes_for. W skrócie klasa modelowa, ale nie dziedzicząca po ActiveRecord::Base(tak, takie klasy też istnieją i mogą być modelami), której obiekty trzymają stan obiektu formularza i enkapsulują np. możliwe opcje, walidacje w danym przypadku użycia

Page 49: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

FORM OBJECT

• ActiveModel::Model

• Virtus

• Reform

• …

Page 50: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

QUERY OBJECT

Sposób na łańcuszek wywołań na relacjach ActiveRecord oraz na inne skomplikowane zapytania do bazy danych.

Page 51: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

QUERY OBJECT

@news = News.published .with_translations(locale) .not_promoted .visible .by_position .with_tile .merge(Tile.with_translations(locale)) .limit(limit)

Page 52: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

QUERY OBJECT

Zasada jest prosta - wyciągamy skomplikowane zapytanie do osobnej klasy PORO a wynik wywołania udostępniamy na zewnątrz

Page 53: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

PODSUMOWANIE

Page 54: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach
Page 55: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

MVC TO NIE WSZYSTKO

Page 56: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

FAT MODEL SKINNY CONTROLLER?NIE!

KAŻDA KLASA POWINNA SIĘ ZDROWO ODŻYWIAĆ I BYĆ SKINNY

przynajmniej w założeniu… ;)

Page 57: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

NIE BÓJMY SIĘ PROGRAMOWANIA OBIEKTOWEGO

nie jest takie trudne jak się na pierwszy rzut oka wydaje

Page 58: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

POMYŚLMY O PROGRAMISTACH, KTÓRZY WEJDĄ PO NAS W NASZ KOD

Zróbmy wszystko, aby liczba przekleństw dążyła do zera ;)

Page 59: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

WYKORZYSTAJMY TO CO W RAILSACH DOBRE

ale nie ograniczajmy się tylko i wyłącznie do tego co oferują

Page 60: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

DODAJMY SZCZYPTĘ PROGRAMOWANIA OBIEKTOWEGO

uważając by nie przekombinować!

Page 61: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

A POWSTANIE LEKKOSTRAWNE I SMACZNE DANIE*

*zakładając że logika biznesowa projektu nie jest zagmatwana ;)

Page 62: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

DZIĘKUJĘ ZA UWAGĘ

Page 63: Uszanowanko Programowanko #3 - REvolution - czyli o bardziej obiektowym podejściu w Railsach

PYTANIA?