Your SlideShare is downloading. ×
0
O que você NÃO
aprendeu sobre
Orientação a
Objetos
Danilo 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 mu...
“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
...
Inventou o termo “Orientação a
Objetos”
“OO significa passagem de mensagem,
retenção local, proteção e ocultação do estado
...
Inventou o termo “Orientação a
Objetos”
“OO significa passagem de mensagem,
retenção local, proteção e ocultação do estado
...
“...(Erlang) é a única linguagem orientada a
objetos e talvez eu tenha sido prematuro em
dizer o que era orientação a obje...
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 palestra
Não é sobre oo
Esta palestra
é sobre DESIGNDESIGN
O que é ?DESIGN
DESIGN Código==
DESIGN Código==
Estrutura
Organização
Flexibilidade
Testabilidade
Legibilidade
Coesão
Acoplamento
Dependências
BOM reduz o
custo da mudança
DESIGN
DESIGN
Hipótese da
stamina do DESIGN
Funcionalidades
Tempo
Funcionalidades
Tempo
Sem Design
Funcionalidades
Tempo
Bom Design
Sem Design
Funcionalidades
Tempo
Bom Design
Sem Design
Onde o design
se paga
2004!
Design foi esquecido
Rails
Design foi esquecido
Rails
Model
View
Controller
Helper
Mailer
...
Model
View
Controller
Zero
Design
Design
Ágil
Up-front
Design
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ó...
class TrueClass
def if_true(is_true, otherwise: -> {})
is_true.call
end
def if_false(is_false, otherwise: -> {})
otherwise...
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 >...
class Proc
def while_true(&blk)
self.call.if_true -> {
blk.call
while_true(&blk)
}
end
end
2.0.0?> i = 0
2.0.0?> -> {i < 3...
Herança
Herança
“É um”
Ave
Pato Pinguim
+ voa()
+ voa() + voa()
class Bird
def fly
puts "flap, flap, flap"
end
end
class Penguin < Bird
def fly
raise "I don't know how to fly"
end
end
fl...
class Bird
def fly
puts "flap, flap, flap"
end
end
class Penguin < Bird
def fly
raise "I don't know how to fly"
end
end
fl...
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 objeto...
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_page...
class Book < ActiveRecord::Base
def after_initialize
@my_cache = {}
end
def number_of_pages
@my_cache[:number_of_pages] ||...
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(sui...
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...
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 ...
polimorfismo
polimorfismo
polimorfismo
1. Herança
1. Herança
2. Duck Typing
1. Herança
2. Duck Typing
3. 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
fechada...
MITO: Modelar OO é
modelar o mundo real
Actor
Ghost Pacman
center, direction
+ collidesWith(Actor)
+ advance(millis)
+ getNextDirection()
Actor
Ghost
Strategy
Pacman
Strategy
MovementStrategy
+ getNextDirection()
Actor
Random
Strategy
UserControl
Strategy
MovementStrategy
+ getNextDirection()
Actor
Random
Strategy
UserControl
Strategy
MovementStrategy
+ getNextDirection()
TargetChasing
Strategy
+ 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...
class Song < ActiveRecord::Base
searchable do
string :title
string :album { album.title }
string :artist { album.artist.na...
class Song < ActiveRecord::Base
...
def self.build_with_filters(p={})
search = Sunspot.new_search(Song)
if p[:artist]
to_r...
o domínio
não é música
o domínio
é busca
require 'forwadable'
module Search
class SongDocument
extend Forwardable
def_delegators :@song, :title
def initialize(song...
module Search
class SongDocument
def self.search_filters(p={})
[
Search::OptionalFilter.new(:artist, p),
Search::AlbumFilt...
Busca
Música
Document
Filter
Facet
Indexer
Song
Album
Artist
Contextos
Delimitados
Busca
Música
Document
Filter
Facet
Indexer
Song
Album
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 é passagem
de mensagem
Herança
agregação
Composição
delegação
polimorfismo
DESIGN É BOM
Zero
Design
Design
Ágil
Up-front
Design
Design ágil == Design evolutivo
DESIGN É BOM
Zero
Design
Design
Ágil
Up-front
Design
Design ágil == Design evolutivo
Não modele o
mundo real
modele o
seu domínio
aprenda o
seu domínio
evolua o
seu 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 He...
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
O que você NÃO aprendeu sobre Programação Orientada a Objetos
Upcoming SlideShare
Loading in...5
×

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

3,605

Published on

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.

Published in: Technology
3 Comments
35 Likes
Statistics
Notes
No Downloads
Views
Total Views
3,605
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
88
Comments
3
Likes
35
Embeds 0
No embeds

No notes for slide

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

  1. 1. O que você NÃO aprendeu sobre Orientação a Objetos Danilo Sato @dtsato
  2. 2. Danilo Sato @dtsato - www.dtsato.com Desenvolvedor, Arquiteto, Coach, DevOps, Treinador
  3. 3. Como aprendemos OO?
  4. 4. Orientação a Objetos é: Herança + Polimorfismo + Encapsulamento
  5. 5. Orientação a Objetos é: Modelar o Mundo Real
  6. 6. “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
  7. 7. “Programação orientada a objetos é uma péssima ideia, que só poderia ter nascido na Califórnia." -- Edsger W. Dijkstra
  8. 8. “Programação orientada a objetos é uma péssima ideia, que só poderia ter nascido na Califórnia." -- Edsger W. Dijkstra
  9. 9. “Na Ciência da Computação, arrogância é medida em nano-Dijkstras" -- Alan Kay
  10. 10. Inventou o termo “Orientação a Objetos”
  11. 11. Inventou o termo “Orientação a Objetos” Smalltalk
  12. 12. Células
  13. 13. Inventou o termo “Orientação a Objetos”
  14. 14. 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"
  15. 15. 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"
  16. 16. 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"
  17. 17. “...(Erlang) é a única linguagem orientada a objetos e talvez eu tenha sido prematuro em dizer o que era orientação a objetos" -- Joe Armstrong
  18. 18. Orientação a Objetos: Programa? Linguagem? Paradigma? Modelo Computacional?
  19. 19. Orientação a Objetos: Programa? Linguagem? Paradigma? Modelo Computacional? Ninguém Concorda
  20. 20. Esta palestra é sobre oo
  21. 21. Esta palestra Não é sobre oo
  22. 22. Esta palestra é sobre DESIGNDESIGN
  23. 23. O que é ?DESIGN
  24. 24. DESIGN Código==
  25. 25. DESIGN Código== Estrutura Organização Flexibilidade Testabilidade Legibilidade Coesão Acoplamento Dependências
  26. 26. BOM reduz o custo da mudança DESIGN
  27. 27. DESIGN
  28. 28. Hipótese da stamina do DESIGN
  29. 29. Funcionalidades Tempo
  30. 30. Funcionalidades Tempo Sem Design
  31. 31. Funcionalidades Tempo Bom Design Sem Design
  32. 32. Funcionalidades Tempo Bom Design Sem Design Onde o design se paga
  33. 33. 2004!
  34. 34. Design foi esquecido Rails
  35. 35. Design foi esquecido Rails Model View Controller Helper Mailer ...
  36. 36. Model View Controller
  37. 37. Zero Design Design Ágil Up-front Design
  38. 38. DESIGN É BOM Design “ativo”
  39. 39. DESIGN É BOM Design “passivo”
  40. 40. DESIGN É BOM Design ágil == Design evolutivo
  41. 41. DESIGN É BOM Design ágil == Design evolutivo
  42. 42. TUDO É UM OBJETO!
  43. 43. TUDO É UM OBJETO?
  44. 44. TUDO É UM OBJETO? class?
  45. 45. TUDO É UM OBJETO? if? class?
  46. 46. TUDO É UM OBJETO? while? if? class?
  47. 47. Smalltalk
  48. 48. 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.
  49. 49. class TrueClass def if_true(is_true, otherwise: -> {}) is_true.call end def if_false(is_false, otherwise: -> {}) otherwise.call end end class FalseClass def if_true(is_true, otherwise: -> {}) otherwise.call end def if_false(is_false, otherwise: -> {}) is_false.call end end Condições
  50. 50. 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
  51. 51. class Proc def while_true(&blk) self.call.if_true -> { blk.call while_true(&blk) } end end 2.0.0?> i = 0 2.0.0?> -> {i < 3}.while_true do 2.0.0?> puts i 2.0.0?> i += 1 2.0.0?> end 0 1 2 => nil Loops
  52. 52. Herança
  53. 53. Herança “É um”
  54. 54. Ave Pato Pinguim + voa() + voa() + voa()
  55. 55. class Bird def fly puts "flap, flap, flap" end end class Penguin < Bird def fly raise "I don't know how to fly" end end flock = [Bird.new, Bird.new, Bird.new] flock.each { |bird| bird.fly } # >> flap, flap, flap # >> flap, flap, flap # >> flap, flap, flap
  56. 56. class Bird def fly puts "flap, flap, flap" end end class Penguin < Bird def fly raise "I don't know how to fly" end end 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'
  57. 57. 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
  58. 58. Isto não é uma ave.Isto não é uma ave.
  59. 59. Herança “É um”
  60. 60. Herança “É um”
  61. 61. Herança
  62. 62. Herança ?
  63. 63. Herança
  64. 64. class Book < ActiveRecord::Base def initialize(attributes = nil, options = {}) super @my_cache = {} end def number_of_pages @my_cache[:number_of_pages] ||= 10000 end end 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)
  65. 65. class Book < ActiveRecord::Base def after_initialize @my_cache = {} end def number_of_pages @my_cache[:number_of_pages] ||= 10000 end end Book.create( :title => "Lord of the Rings").number_of_pages # => 10000 Book.find_by( :title => "Lord of the Rings").number_of_pages # => 10000
  66. 66. Herança: Preciso entender o que a(s) classe(s) Pai faz(em)!
  67. 67. 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) end end deck = Deck.new deck.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?
  68. 68. Não use herança se não usar todo o comportamento do(s) pais(s)
  69. 69. Herança é perigoso
  70. 70. Herança é perigoso
  71. 71. Herança é perigoso
  72. 72. Herança é perigoso
  73. 73. Herança é perigoso
  74. 74. Use Herança quando há especialização
  75. 75. Prefira hierarquias rasas
  76. 76. BigDecimal RationalComplexFloatInteger Fixnum Bignum Numeric
  77. 77. Agregação e composição
  78. 78. Agregação Composição “Tem um”
  79. 79. Agregação
  80. 80. Agregação
  81. 81. Agregação
  82. 82. composição
  83. 83. composição
  84. 84. composição
  85. 85. 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 end end deck = Deck.new deck << ["A", "S"] << ["A", "S"] deck.count { |card| card[0] == "A"} # => 4
  86. 86. Prefira composição ao invés de Herança
  87. 87. Delegação
  88. 88. Delegação
  89. 89. 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 end end deck = Deck.new deck.draw(5) # => [["Q", "D"], ["K", "S"], ["K", "H"], ["K", "C"], ["K", "D"]]
  90. 90. polimorfismo
  91. 91. polimorfismo
  92. 92. polimorfismo
  93. 93. 1. Herança
  94. 94. 1. Herança 2. Duck Typing
  95. 95. 1. Herança 2. Duck Typing 3. Mixins
  96. 96. permite separar abstração da concretização
  97. 97. permite separar “O que” do “Como”
  98. 98. 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
  99. 99. MITO: Modelar OO é modelar o mundo real
  100. 100. Actor Ghost Pacman center, direction + collidesWith(Actor) + advance(millis) + getNextDirection()
  101. 101. Actor Ghost Strategy Pacman Strategy MovementStrategy + getNextDirection()
  102. 102. Actor Random Strategy UserControl Strategy MovementStrategy + getNextDirection()
  103. 103. Actor Random Strategy UserControl Strategy MovementStrategy + getNextDirection() TargetChasing Strategy + getTarget()
  104. 104. o design evolui Conforme o entendimento do domínio evolui
  105. 105. [ ] 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) Filtros Artista Album Estilo
  106. 106. 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 end end
  107. 107. 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 end end
  108. 108. o domínio não é música
  109. 109. o domínio é busca
  110. 110. 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 ... end end
  111. 111. 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 end end
  112. 112. Busca Música Document Filter Facet Indexer Song Album Artist
  113. 113. Contextos Delimitados Busca Música Document Filter Facet Indexer Song Album Artist
  114. 114. Mapa de Contextos Busca Música
  115. 115. “Todos os modelos estão errados, alguns modelos são úteis" -- George Box
  116. 116. MITO: Modelar OO é modelar o mundo real
  117. 117. MITO: Modelar OO é modelar o mundo real
  118. 118. Resumindo...
  119. 119. OO é passagem de mensagem
  120. 120. Herança agregação Composição delegação polimorfismo
  121. 121. DESIGN É BOM Zero Design Design Ágil Up-front Design Design ágil == Design evolutivo
  122. 122. DESIGN É BOM Zero Design Design Ágil Up-front Design Design ágil == Design evolutivo
  123. 123. Não modele o mundo real
  124. 124. modele o seu domínio
  125. 125. aprenda o seu domínio
  126. 126. evolua o seu domínio
  127. 127. se divirta!
  128. 128. Obrigado! Danilo Sato @dtsato - www.dtsato.com Desenvolvedor, Arquiteto, Coach, DevOps, Treinador
  129. 129. 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:
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×