O que você NÃO aprendeu sobre Programação Orientada a Objetos

Post on 24-May-2015

3.532 views 0 download

description

Slides da minha palestra na RubyConf BR 2013 Ruby é uma linguagem orientada a objetos, porém a maneira como aprendemos orientação a objetos geralmente foca no "O que?" e pouco no "Por quê?". Aprendemos o que é herança, polimorfismo, as diferenças entre classes e objetos, mas não discutimos questões mais relevantes como: o que caracteriza um bom design OO? Quais as vantagens e desvantagens de usar herança? Como OO me ajuda a resolver problemas do dia a dia? Nesta palestra vamos abordar estas e outras questões de design e modelagem OO através de exemplos. Pretendo desbancar alguns mitos - como modelar OO é modelar o mundo real - assim como compartilhar alguns princípios para ajudar você e seu time a melhorar a qualidade do código da sua aplicação Ruby.

Transcript of O que você NÃO aprendeu sobre Programação Orientada a Objetos

O que você NÃO aprendeu sobre Orientação a ObjetosDanilo Sato@dtsato

Danilo Sato@dtsato - www.dtsato.com

Desenvolvedor, Arquiteto, Coach, DevOps, Treinador

Como aprendemos OO?

Orientação a Objetos é:Herança +

Polimorfismo +Encapsulamento

Orientação a Objetos é:Modelar o Mundo Real

“A execução de um programa é considerada um modelo físico, simulando o comportamento de

uma parte real ou imaginária do mundo."-- Kristen Nygaard

“Programação orientada a objetos é uma péssima ideia, que só poderia ter nascido na

Califórnia."-- Edsger W. Dijkstra

“Programação orientada a objetos é uma péssima ideia, que só poderia ter nascido na

Califórnia."-- Edsger W. Dijkstra

“Na Ciência da Computação, arrogância é medida em nano-Dijkstras"

-- Alan Kay

Inventou o termo “Orientação a Objetos”

Inventou o termo “Orientação a Objetos”

Smalltalk

Células

Inventou o termo “Orientação a Objetos”

Inventou o termo “Orientação a Objetos”

“OO significa passagem de mensagem, retenção local, proteção e ocultação do estado

de um processo, e associação tardia de tudo"

Inventou o termo “Orientação a Objetos”

“OO significa passagem de mensagem, retenção local, proteção e ocultação do estado

de um processo, e associação tardia de tudo"

“A grande ideia é passagem de mensagem"

Inventou o termo “Orientação a Objetos”

“OO significa passagem de mensagem, retenção local, proteção e ocultação do estado

de um processo, e associação tardia de tudo"

“A grande ideia é passagem de mensagem"

“OO é um modelo computacional, não um paradigma de programação"

“...(Erlang) é a única linguagem orientada a objetos e talvez eu tenha sido prematuro em

dizer o que era orientação a objetos"

-- Joe Armstrong

Orientação a Objetos:Programa?Linguagem?Paradigma?

Modelo Computacional?

Orientação a Objetos:Programa?Linguagem?Paradigma?

Modelo Computacional?

Ninguém Concorda

Esta palestraé sobre oo

Esta palestraNão é sobre oo

Esta palestraé sobre DESIGNDESIGN

O que é ?DESIGN

DESIGN Código==

DESIGN Código==

Estrutura

Organização

FlexibilidadeTestabilidade

Legibilidade

Coesão

Acoplamento

Dependências

BOM reduz ocusto da mudança

DESIGN

DESIGN

Hipótese da stamina do DESIGN

Func

iona

lidad

es

Tempo

Func

iona

lidad

es

Tempo

Sem Design

Func

iona

lidad

es

Tempo

Bom Design

Sem Design

Func

iona

lidad

es

Tempo

Bom Design

Sem DesignOnde o design

se paga

2004!

Design foi esquecido

Rails

Design foi esquecido

RailsModelView

ControllerHelperMailer

...

Model

View

Controller

ZeroDesign

DesignÁgil

Up-frontDesign

DESIGN É BOM

Design “ativo”

DESIGN É BOM

Design “passivo”

DESIGN É BOM

Design ágil == Design evolutivo

DESIGN É BOM

Design ágil == Design evolutivo

TUDO É UM OBJETO!

TUDO É UM OBJETO?

TUDO É UM OBJETO?

class?

TUDO É UM OBJETO?

if?

class?

TUDO É UM OBJETO?

while?if?

class?

Smalltalk

WARNING!

O código que você está prestes a ler foi escrito com o propósito educacional. Não faça isso em casa ou coloque código parecido em produção.

class TrueClass def if_true(is_true, otherwise: -> {}) is_true.call end

def if_false(is_false, otherwise: -> {}) otherwise.call endend

class FalseClass def if_true(is_true, otherwise: -> {}) otherwise.call end

def if_false(is_false, otherwise: -> {}) is_false.call endend

Condições

2.0.0 > (2 > 1).if_true -> {2.0.0?> puts "sim"2.0.0?> }, otherwise: -> {2.0.0?> puts "não"2.0.0?> }sim => nil

2.0.0 > (1 > 2).if_true -> {2.0.0?> puts "sim"2.0.0?> }, otherwise: -> {2.0.0?> puts "não"2.0.0?> }não => nil

Condições

class Proc def while_true(&blk) self.call.if_true -> { blk.call while_true(&blk) } endend

2.0.0?> i = 02.0.0?> -> {i < 3}.while_true do2.0.0?> puts i2.0.0?> i += 12.0.0?> end012 => nil

Loops

Herança

Herança

“É um”

Ave

Pato Pinguim

+ voa()

+ voa() + voa()

class Bird def fly puts "flap, flap, flap" endend

class Penguin < Bird def fly raise "I don't know how to fly" endend

flock = [Bird.new, Bird.new, Bird.new]flock.each { |bird| bird.fly }# >> flap, flap, flap# >> flap, flap, flap# >> flap, flap, flap

class Bird def fly puts "flap, flap, flap" endend

class Penguin < Bird def fly raise "I don't know how to fly" endend

flock = [Penguin.new, Penguin.new, Penguin.new]flock.each { |bird| bird.fly }# ~> -:9:in `fly': I don't know how to fly (RuntimeError)# ~> from -:14:in `block in <main>'# ~> from -:14:in `each'

Princípio de Substituição de Liskov

Se S é um subtipo de T, então os objetos do tipo T podem ser

substituídos pelos objetos de tipo S em qualquer lugar do programa

Isto não é uma ave.Isto não é uma ave.

Herança

“É um”

Herança

“É um”

Herança

Herança?

Herança

class Book < ActiveRecord::Base def initialize(attributes = nil, options = {}) super @my_cache = {} end def number_of_pages @my_cache[:number_of_pages] ||= 10000 endend

Book.create( :title => "Lord of the Rings").number_of_pages # => 10000

Book.find_by( :title => "Lord of the Rings").number_of_pages # ~> -:8:in `number_of_pages': undefined method `[]' for nil:NilClass (NoMethodError)

class Book < ActiveRecord::Base def after_initialize @my_cache = {} end def number_of_pages @my_cache[:number_of_pages] ||= 10000 endend

Book.create( :title => "Lord of the Rings").number_of_pages # => 10000

Book.find_by( :title => "Lord of the Rings").number_of_pages # => 10000

Herança:Preciso entender o que a(s) classe(s)

Pai faz(em)!

class Deck < Array def initialize suits = %w(S H C D) indexes = %w(A 2 3 4 5 6 7 8 9 10 J Q K) cards = indexes.product(suits) super(cards) end

def draw(n) self.pop(n) endend

deck = Deck.newdeck.shuffle!.draw(5) # => [["6", "H"], ["Q", "H"], ["3", "C"], ["6", "S"], ["K", "C"]]

deck << ["A", "S"] << ["A", "S"]deck.count { |card| card[0] == "A"} # => 6 aces?

Não use herança se não usar todo o comportamento

do(s) pais(s)

Herança é perigoso

Herança é perigoso

Herança é perigoso

Herança é perigoso

Herança é perigoso

Use Herança quando há

especialização

Prefira hierarquias

rasas

BigDecimal RationalComplexFloatInteger

Fixnum Bignum

Numeric

Agregação e composição

Agregação Composição

“Tem um”

Agregação

Agregação

Agregação

composição

composição

composição

class Deck def initialize suits = %w(S H C D) indexes = %w(A 2 3 4 5 6 7 8 9 10 J Q K) @cards = indexes.product(suits) end

def count(&blk); @cards.count(&blk) end def <<(card) @cards << card unless @cards.include?(card) self endend

deck = Deck.newdeck << ["A", "S"] << ["A", "S"]deck.count { |card| card[0] == "A"} # => 4

Prefira composição ao

invés de Herança

Delegação

Delegação

require 'forwardable'class Deck extend Forwardable def_delegator :@cards, :pop, :draw def_delegators :@cards, :count

def initialize suits = %w(S H C D) indexes = %w(A 2 3 4 5 6 7 8 9 10 J Q K) @cards = indexes.product(suits) end

def <<(card) @cards << card unless @cards.include?(card) self endend

deck = Deck.newdeck.draw(5) # => [["Q", "D"], ["K", "S"], ["K", "H"], ["K", "C"], ["K", "D"]]

polimorfismo

polimorfismo

polimorfismo

1. Herança

1. Herança2. Duck Typing

1. Herança2. Duck Typing3. Mixins

permite separar abstração da concretização

permite separar “O que” do

“Como”

Princípio “Open-Closed”

Entidades de software como classes, módulos e funções devem

ser abertas para extensão, mas fechadas para modificação

-- Bertrand Meyer

MITO: Modelar OO é modelar o mundo real

Actor

Ghost Pacman

center, direction+ collidesWith(Actor)+ advance(millis)+ getNextDirection()

Actor

GhostStrategy

PacmanStrategy

MovementStrategy+ getNextDirection()

Actor

RandomStrategy

UserControlStrategy

MovementStrategy+ getNextDirection()

Actor

RandomStrategy

UserControlStrategy

MovementStrategy+ getNextDirection()

TargetChasingStrategy

+ getTarget()

o design evolui Conforme o

entendimento do domínio evolui

[ ] Metallica (10)[ ] Iron Maiden (5)[ ] AC/DC (15)[ ] ...

[ ] Black Album (5)[ ] Master of Puppets (5)[ ] Killers (5)[ ] ...

[x] Rock (50)[ ] Clássico (100)[ ] Jazz (80)[ ] Blues

Filtros

Artista

Album

Estilo

class Song < ActiveRecord::Base searchable do string :title string :album { album.title } string :artist { album.artist.name } ... end

def self.build_with_filters(p={}) search = Sunspot.new_search(Song) search.build do title = with(:title, p[:title]) if p[:title].present? artist = with(:artist, p[:artist]) if p[:artist].present? album = with(:album, p[:album]) if p[:album].present? ... facet :artist, exclude: artist facet :album, exclude: album end search endend

class Song < ActiveRecord::Base ... def self.build_with_filters(p={}) search = Sunspot.new_search(Song)

if p[:artist] to_reject = p[:artist_album].map do |artist_album| JSON.parse(artist_album)['album'] end p[:album].reject! do |element| to_reject.include?(element) end p[:album] = [{}] if p[:album].empty? end

search.build do ... end search endend

o domínionão é música

o domínioé busca

require 'forwadable'

module Search class SongDocument extend Forwardable def_delegators :@song, :title

def initialize(song) @song = song end

def album @song.album.title end

def artist @song.artist.name end ... endend

module Search class SongDocument def self.search_filters(p={}) [ Search::OptionalFilter.new(:artist, p), Search::AlbumFilter.new(:artist, :artist_album, p), Search::OptionalFilter.new(:title, p) ] end def self.build_with_filters(filters, p={}) Sunspot.new_search(Song).tap do |search| filters.each do |filter| filter.apply_to(search) end Search::Faceter.new(filters, p).apply_to(search) end end endend

Busca

Música

DocumentFilter

Facet

Indexer

SongAlbum

Artist

ContextosDelimitados

Busca

Música

DocumentFilter

Facet

Indexer

SongAlbum

Artist

Mapa de Contextos

Busca

Música

“Todos os modelos estão errados,alguns modelos são úteis"

-- George Box

MITO: Modelar OO é modelar o mundo real

MITO: Modelar OO é modelar o mundo real

Resumindo...

OO é passagemde mensagem

HerançaagregaçãoComposiçãodelegação

polimorfismo

DESIGN É BOM

ZeroDesign

DesignÁgil

Up-frontDesign

Design ágil == Design evolutivo

DESIGN É BOM

ZeroDesign

DesignÁgil

Up-frontDesign

Design ágil == Design evolutivo

Não modele o mundo real

modele oseu domínio

aprenda oseu domínio

evolua oseu domínio

se divirta!

Obrigado!

Danilo Sato@dtsato - www.dtsato.com

Desenvolvedor, Arquiteto, Coach, DevOps, Treinador

Referências

• “Practical Object-Oriented Design in Ruby” Sandi Metz

• “Domain-Driven Design: Tackling Complexity in the Heart of Software” Eric Evans

• “Analysis Patterns: Reusable Object Models” Martin Fowler

• “Patterns of Enterprise Application Architecture” Martin Fowler

• “Design Patterns: Elements of Reusable Object-Oriented Software” Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides

• “Growing Object-Oriented Software, Guided by Tests” Steve Freeman , Nat Pryce

• “Object-Oriented Software Construction” Bertrand Meyer

Livros: