Академический Документы
Профессиональный Документы
Культура Документы
Princípios
de
Projeto
de
Classes
‐
II
• A
classe
como
unidade
de
reúso
de
código
• Princípios
que
garantem
a
boa
práCca
da
programação
orientada
a
objetos
• Principio
da
Segregação
de
Interfaces
• Principio
da
Inversão
de
Dependências
• O
padrão
de
projetos
template
• A
lei
das
interfaces
mais
genéricas
Princípio
da
Inversão
de
Dependências
• Classes
mais
abstratas
não
devem
depender
de
classes
mais
concretas.
– Na
verdade,
justamente
o
contrário
deve
acontecer.
• Em
outras
palavras:
classes
provedoras
devem
ser
mais
abstratas,
e
classes
clientes
podem
ser
mais
concretas.
que é uma classe
cliente? E uma
Quando uma classe
classe depende O que significa provedora?
de outra?
uma classe ser
"mais abstrata"?
Princípio
da
Inversão
de
Dependências
É um truque comum na programação de
interfaces de jogos desenharmos somente as
formas que podem ser vistas pelo usuário.
public
interface
Shape
{
void
draw(Canvas
canvas,
Paint
paint);
void
transpose(int
x,
int
y);
double
getArea();
}
Área
é
coisa
de
Forma
public
interface
Shape
{
void
draw(Canvas
canvas,
Paint
paint);
void
transpose(int
x,
int
y);
double
getArea();
}
public
final
class
Dot
implements
Shape
{
private
final
int
diameter;
//
...
public
double
getArea()
{
return
this.diameter
*
Math.PI;
}
}
public
class
ImmutableCircle
implements
Shape
{
public
class
Rectangle
implements
Shape
{
public
final
r;
private
w,
h;
//
…
//
...
public
double
getArea()
{
return
this.r
*
Math.PI;
}
public
double
getArea()
{
}
return
this.w
*
this.h;
}
}
Doce
para
os
Olhos
• O
resultado
final
é,
sem
dúvida,
muito
mais
agradável:
private
List<Shape>
hasLargeArea2(List<Shape>
shapes,
float
area)
{
List<Shape>
largeShapes
=
new
LinkedList<Shape>();
for
(Shape
shape
:
shapes)
{
if
(shape.getArea()
>
area)
{
largeShapes.add(shape);
}
}
return
largeShapes;
}
Mas
e
o
reúso?
• O
resultado
final
é,
sem
dúvida,
muito
mais
agradável:
private
List<Shape>
hasLargeArea2(List<Shape>
shapes,
float
area)
{
List<Shape>
largeShapes
=
new
LinkedList<Shape>();
for
(Shape
shape
:
shapes)
{
Em que outras
if
(shape.getArea()
>
area)
{
situações
largeShapes.add(shape);
poderíamos usar
}
essa função?
}
Seria possível
return
largeShapes;
Q u a l a e s trutura
projetar um
}
código mais essencial desse
reusável?
código?
Filtros
• O
algoritmo
anterior
é
um
filtro.
– Dada
uma
lista
L0,
e
um
predicado
p,
o
filtro
produz
uma
segunda
lista
L1
que
contém
todo
elemento
e
em
L0
tal
que
p(e)
seja
verdade.
– Esse
filtro
é
a
estrutura
essencial
da
função
anterior.
– A
criação
de
funções
reusáveis
involve
uma
constante
busca
pela
estrutura
essencial
de
cada
programa.
Mas… como
programar um
filtro reutilizável?
Classes
Abstratas
em
Ação
public
abstract
class
Filter<E>
{
O que é esse <E>
na declaração da
public
List<E>
filter(Iterable<E>
inputs)
{
classe?
List<E>
results
=
new
LinkedList<E>();
for
(E
e
:
inputs)
{
O que significa
if
(predicate(e))
{
essa palavra
results.add(e);
abstract?
}
}
return
results;
Como utilizar
}
essa classe?
abstract
boolean
predicate(E
e);
}
Simulando
Funções
de
Alta
Ordem
Qual padrão de A linguagem Java
projetos não permite que um
acabamos de método seja passado
implementar?
como parâmetro de
outro método.
Desenvolvedores
public
abstract
class
Filter<E>
{
fazem longas
ginásticas para
public
List<E>
filter(Iterable<E>
inputs)
{
contornar essa
List<E>
results
=
new
LinkedList<E>();
limitação.
for
(E
e
:
inputs)
{
if
(predicate(e))
{
for
(Shape
shape
:
(new
Filter<Shape>()
{
results.add(e);
}
@Override
}
boolean
predicate(Shape
s)
{
return
results;
return
s.getArea()
>
20.0;
}
}}).filter(shapes))
{
abstract
boolean
predicate(E
e);
shapeWindow.addShape(shape);
}
}
Templates
• Um
template
é
um
algoritmo
com
uma
lacuna.
• Essa
é
a
base
dos
arcabouços
de
so~ware.
• Essas
lacunas
são
representadas
pelas
interfaces
sobre
as
quais
o
template
atua.
• Clientes
do
template
devem
implementar
as
interfaces
previstas
em
seu
contrato.
Qual a vantagem O uso de template
s
da programação é chamado de
orientada a Inversão de Por que?
templates?
Dependências.
Estudo
de
Caso
Considere
o
projeto
de
um
jogo,
Glóbulos,
que
depende
de
duas
importantes
bibliotecas:
animanix.jar
•
animanix.jar,
que
provê
facilidades
para
o
desenvolvimento
de
animações
•
easy_physics.jar,
que
provê
várias
funções
para
o
cálculo
de
tensores
e
forças
elásCcas.
Glóbulos
Ambas
as
bibliotecas
são
código
livre,
e
seus
desenvolvedores
costumam
modificar
as
suas
interfaces
com
uma
certa
frequência.
É
interessante
que
o
projeto
de
Glóbulos
possa
conCnuar
uClizando
as
úl@mas
versões
das
bibliotecas
externas,
mas
é
ainda
mais
importante
que
o
código
de
Glóbulos
não
precise
ser
muito
easy_physics.jar
modificado
sempre
que
novas
versões
das
bibliotecas
forem
lançadas.
O
que
fazer?
Adaptadores
em
Ação
Uma
forma
de
aCngir
as
metas
propostas
é
interpor
entre
o
projeto
de
Glóbulos
e
as
bibliotecas
externas
camadas
de
animanix.jar
adaptadores.
Nesse
caso,
são
necessárias
duas
camadas,
uma
para
animanix.jar
animaCon_layer
e
outra
para
easy_physics.jar.
Quando
as
bibliotecas
forem
modificadas,
apenas
os
adaptadores
Glóbulos
sofrerão
alterações.
A
lógica
de
Glóbulos,
contudo,
permanecerá
inalterada.
physics_layer
Essa
é
uma
forma
de
inversão
de
dependências,
pois
a
implementação
de
Glóbulos
passa
a
depender
das
interfaces
dos
adaptadores,
as
quais
precisam
ser
implementadas
de
forma
mais
easy_physics.jar
genérica
possível.
Adaptadores
• Adaptador
é
um
padrão
de
projetos.
– Possivelmente
o
mais
simples
padrão
estrutural
de
projetos.
• Um
adaptador
é
um
objeto
que
adapta
a
interface
de
uma
classe
provedora
à
interface
esperada
pela
classe
cliente.
O tempo todo
nós vimos um
adaptador. Qual?
Em
Busca
da
Estrutura
Essencial
• Para
implementarmos
so~ware
reuClizável,
é
preciso
sermos
capazes
de
encontrar
a
estrutura
essencial
de
cada
aplicação.
– A
forma
de
preencher
essa
estrutura
é
irrelevante.
O que vocês
acham que esse
desenho
representa?
Qual a estrutur
a
essencial dessa
aplicação?
Em
Busca
da
Estrutura
Essencial
• Para
implementarmos
so~ware
reuClizável,
é
preciso
sermos
capazes
de
encontrar
a
estrutura
essencial
de
cada
aplicação.
– A
forma
de
preencher
essa
estrutura
é
irrelevante.
Temos aqui uma
relação de
escutador/
tratador.
O nosso já
conhecido
padrão
Observer.
Princípio
da
Segregação
de
Interfaces
• Clientes
não
devem
ser
forçados
a
depender
de
interfaces
que
eles
não
usam.
Explique isso.
A
Beleza
do
Simples
• O
Simples
é
Belo…
– mas
não
vende!
• Em
geral,
quanto
mais
"capacidades"
um
produto
tem,
ao
mesmo
custo,
mas
procurado
ele
é.
• Essa
idéia,
contudo,
não
pode
ser
aplicada
com
tanto
sucesso
em
desenvolvimento
de
programas.
Porque?
public
class
ShapeWindow
extends
View
{
private
List<Shape>
shapes
=
new
ArrayList<Shape>();
public
OldShapeWindow(Context
context,
final
int
width,
final
int
height)
{
super(context);
setFocusable(true);
Já vimos essa
setMinimumWidth(width);
classe antes.
setMinimumHeight(height);
Onde?
}
protected
void
onMeasure(int
widthMeasureSpec,
int
heightMeasureSpec)
{
setMeasuredDimension(getSuggestedMinimumWidth(),
getSuggestedMinimumHeight());
Quais os
}
problemas
public
void
addShape(Shape
shape)
{
this.shapes.add(shape);
}
d esse projeto?
protected
void
onDraw(Canvas
canvas)
{
Paint
paint
=
new
Paint();
paint.setStyle(Style.FILL);
Como
paint.setColor(Color.WHITE);
melhorá-lo?
canvas.drawRect(1,
1,
this.getWidth()
‐
1,
this.getHeight()
‐
1,
paint);
paint.setColor(Color.BLACK);
paint.setStyle(Style.STROKE);
for
(Shape
shape
:
shapes)
{
shape.draw(canvas,
paint);
}
}
}
Gerando
Formas
Aleatoriamente
public
class
ShapeWindow
extends
View
{
private
List<Shape>
shapes
=
new
ArrayList<Shape>();
public
OldShapeWindow(Context
context,
final
int
width,
final
int
height)
{
Implemente um método que
super(context);
crie formas aleatoriamente.
setFocusable(true);
setMinimumWidth(width);
Esse método é útil para
setMinimumHeight(height);
}
testarmos nossa aplicação. É
protected
void
onMeasure(int
widthMeasureSpec,
int
heightMeasureSpec)
{
claro que mesmo um método
setMeasuredDimension(getSuggestedMinimumWidth(),
getSuggestedMinimumHeight());
tão simple requer decisões de
}
public
void
addShape(Shape
shape)
{
projeto.
this.shapes.add(shape);
}
protected
void
onDraw(Canvas
canvas)
{
Que tal se o nosso método
Paint
paint
=
new
Paint();
paint.setStyle(Style.FILL);
recebesse uma "Janela de
paint.setColor(Color.WHITE);
Formas", e inserisse as formas
canvas.drawRect(1,
1,
this.getWidth()
‐
1,
this.getHeight()
‐
1,
paint);
paint.setColor(Color.BLACK);
diretamente na janela?
paint.setStyle(Style.STROKE);
for
(Shape
shape
:
shapes)
{
shape.draw(canvas,
paint);
}
}
}
Gerando
Formas
Aleatoriamente
public
void
generateRandomShapes(ShapeWindow
window,
int
numShapes,
int
maxX,
int
maxY)
{
Random
r
=
new
Random();
for
(int
i
=
0;
i
<
numShapes;
i++)
{
Esse projeto
int
rx
=
r.nextInt()
%
maxX;
possui
int
ry
=
r.nextInt()
%
maxY;
problemas.
int
size
=
r.nextInt()
%
100;
switch
(r.nextInt()
%
3)
{
Quais?
case
0:
window.addShape(new
Rectangle(rx,
ry,
size,
size));
break;
case
1:
window.addShape(new
Square(rx,
ry,
size));
break;
default:
window.addShape(new
ImmutableCircle(rx,
ry,
size));
break;
}
}
}
Gerando
Formas
Aleatoriamente
public
void
generateRandomShapes(ShapeWindow
window,
int
numShapes,
int
maxX,
int
maxY)
{
Random
r
=
new
Random();
for
(int
i
=
0;
i
<
numShapes;
i++)
{
int
rx
=
r.nextInt()
%
maxX;
Esse método funciona
int
ry
=
r.nextInt()
%
maxY;
somente com janelas
int
size
=
r.nextInt()
%
100;
de formas. Ele poderia
switch
(r.nextInt()
%
3)
{
ser mais geral. Qual é
case
0:
a sua estrutura
window.addShape(new
Rectangle(rx,
ry,
size,
size));
essencial?
break;
case
1:
window.addShape(new
Square(rx,
ry,
size));
Quais parâmetros o
break;
método deveria
default:
receber? Em outras
window.addShape(new
ImmutableCircle(rx,
ry,
size));
palavras: qual a
break;
menor interface que
}
atende a esse método?
}
}
Gerando
Formas
Aleatoriamente
public
void
generateRandomShapes(MonotonicSet<Shape>
set,
int
numShapes,
int
maxX,
int
maxY)
{
Random
r
=
new
Random();
for
(int
i
=
0;
i
<
numShapes;
i++)
{
Como seria
int
rx
=
r.nextInt()
%
maxX;
essa
int
ry
=
r.nextInt()
%
maxY;
Será que ela
interface?
int
size
=
r.nextInt()
%
100;
teria somente
switch
(r.nextInt()
%
3)
{
o método add?
case
0:
set.add(new
Rectangle(rx,
ry,
size,
size));
break;
case
1:
set.add(new
Square(rx,
ry,
size));
break;
default:
set.add(new
ImmutableCircle(rx,
ry,
size));
break;
}
}
}
Equilíbrio
public
void
generateRandomShapes(MonotonicSet<Shape>
set,
int
numShapes,
int
maxX,
int
maxY)
{
Random
r
=
new
Random();
for
(int
i
=
0;
i
<
numShapes;
i++)
{
Precisamos sempre
int
rx
=
r.nextInt()
%
maxX;
tentar encontrar um
int
ry
=
r.nextInt()
%
maxY;
equilíbrio entre o simples
int
size
=
r.nextInt()
%
100;
e o geral. Uma interface
public
interface
MonotonicSet<E>
{
switch
(r.nextInt()
%
3)
{
precisa ser simples
case
0:
(pequena), porém ela
void
add(E
e);
set.add(new
Rectangle(rx,
ry,
size,
size));
deve conter todos os
break;
métodos de sua
case
1:
boolean
contains(E
e);
responsabilidade.
set.add(new
Square(rx,
ry,
size));
break;
int
size();
default:
aria
E como fic
set.add(new
ImmutableCircle(rx,
ry,
size));
w
S h apeWindo
break;
}
a g o r a?
}
}
}
Janelas
e
Listas
public
class
ShapeWindow
extends
View
implements
MonotonicSet<Shape>
{
private
List<Shape>
shapes
=
new
ArrayList<Shape>();
public
void
add(Shape
e)
{
this.shapes.add(e);
• Java
não
provê
herança
}
múlCpla.
public
boolean
contains(Shape
e)
{
– Mas
podemos
simular
esse
return
this.shapes.contains(e);
recurso
por
meio
de
}
interfaces.
public
int
size()
{
return
this.shapes.size();
View
MonotonicSet
}
}
ShapeWindow
Janelas
e
Listas
va
Porque Ja
public
class
ShapeWindow
extends
View
implements
MonotonicSet<Shape>
{
não provê
private
List<Shape>
shapes
=
new
ArrayList<Shape>();
herança
múltipla?
public
void
add(Shape
e)
{
this.shapes.add(e);
• Java
não
provê
herança
}
múlCpla.
public
boolean
contains(Shape
e)
{
– Mas
podemos
simular
esse
return
this.shapes.contains(e);
recurso
por
meio
de
}
interfaces.
public
int
size()
{
Qual a diferença
entre estender
return
this.shapes.size();
View
MonotonicSet
}
múltiplas classes,
e implementar
}
múltiplas
interfaces?
ShapeWindow
Mas,
será
que
a
mistura
faz
senCdo?
• Poderíamos
tentar
reCrar
a
lista
de
formas
da
janela.
Isso,
contudo,
não
é
simples.
Por
que?
public
class
ShapeWindow
extends
View
implements
MonotonicSet<Shape>
{
private
List<Shape>
shapes
=
new
ArrayList<Shape>();
@Override
protected
void
onDraw(Canvas
canvas)
{
...
}
public
void
add(Shape
e)
{
this.shapes.add(e);
}
public
boolean
contains(Shape
e)
{
return
this.shapes.contains(e);
}
public
int
size()
{return
this.shapes.size();
}
}
Mas,
será
que
a
mistura
faz
senCdo?
• Poderíamos
tentar
reCrar
a
lista
de
formas
da
janela.
Isso,
contudo,
não
é
simples.
Por
que?
public
class
ShapeWindow
extends
View
implements
MonotonicSet<Shape>
{
private
List<Shape>
shapes
=
new
ArrayList<Shape>();
E, além do mais,
@Override
pode ser que não
protected
void
onDraw(Canvas
canvas)
{
seja interessante
...
fazer essa
}
dissociação. Por
public
void
add(Shape
e)
{
this.shapes.add(e);
}
que?
public
boolean
contains(Shape
e)
{
return
this.shapes.contains(e);
}
public
int
size()
{return
this.shapes.size();
}
}
ACvando/DesaCvando
Formas
Que objeto irá
lidar com os Que infos
Modifique a Janela de Formas,
eventos de existem em
para que ela possa tratar
toque?
um evento de
eventos de toque.
toque?
Uma vez que o usuário encosta Que
de Será que
em um ponto da janela, proprieda poderíamos
r
somente as formas que estão precisa se usar um filtro?
naquele ponto devem ser filtrada?
mostradas.
PerCnência
em
Formas
public
final
class
Dot
implements
Shape
{
• Vamos
evitar
quebrar
o
private
int
x,
y;
private
final
int
diameter;
princípio
da
abertura
public
boolean
contains(int
x,
int
y)
{
int
px
=
Math.abs(this.x
‐
x)
*
Math.abs(this.x
‐
x);
int
py
=
Math.abs(this.y
‐
y)
*
Math.abs(this.y
‐
y);
fechamento:
return
Math.sqrt(px
+
py)
<
this.diameter;
}
}
– Uma
forma
sabe
dizer
se
ela
public
class
Rectangle
implements
Shape
{
private
int
x,
y,
w,
h;
contém
um
ponto
ou
não.
public
boolean
contains(int
x,
int
y)
{
boolean
inX
=
x
>
this.x
&&
x
<
this.x
+
this.w;
boolean
inY
=
y
>
this.y
&&
y
<
this.y
+
this.h;
Precisamos
return
inX
&&
inY;
}
tratar o }
evento de
public
class
ImmutableCircle
implements
Shape
{
public
interface
Shape
{
toque.
public
final
int
x,
y,
r;
void
draw(Canvas
canvas,
Paint
paint);
public
boolean
contains(int
x,
int
y)
{
int
px
=
Math.abs(this.x
‐
x)
*
Math.abs(this.x
‐
x);
void
transpose(int
x,
int
y);
int
py
=
Math.abs(this.y
‐
y)
*
Math.abs(this.y
‐
y);
double
getArea();
return
Math.sqrt(px
+
py)
<
this.r;
}
boolean
contains(int
x,
int
y);
}
}
TouchListener
O que estamos
tramando com
esses dois
vetores?
public
class
ShapeWindow
extends
View
implements
MonotonicSet<Shape>,
OnTouchListener
{
private
List<Shape>
shapes
=
new
ArrayList<Shape>(),
currentShapes;
public
ShapeWindow(Context
context,
final
int
width,
final
int
height)
{
}
protected void onDraw(Canvas canvas) { }
public
boolean
onTouch(View
v,
MoConEvent
event)
{
}
Como seria o Podemos u
s sar
}
método Como filtrar a Filter
onTouch?
formas?
novament
e?
Usando
o
Filtro
Novamente
public
boolean
onTouch(View
v,
MoConEvent
event)
{
final
int
cx
=
(int)event.getX();
final
int
cy
=
(int)event.getY();
currentShapes
=
(new
Filter<Shape>(){
É óbvio que
@Override
podemos usar o
boolean
predicate(Shape
e)
{
filtro novamente:
return
e.contains(cx,
cy);
ele foi feito para
}}).filter(this.shapes);
ser reutilizável!
this.invalidate();
return
true;
Como ficaria o
}
méto
do
Por que?
onDraw?
Desenhando
as
Formas
Filtradas
@Override
protected
void
onDraw(Canvas
canvas)
{
Paint
paint
=
new
Paint();
paint.setStyle(Style.FILL);
paint.setColor(Color.WHITE);
canvas.drawRect(1,
1,
this.getWidth()
‐
1,
this.getHeight()
‐
1,
paint);
paint.setColor(Color.BLACK);
Em que esse
paint.setStyle(Style.STROKE);
método difere
for
(Shape
shape
:
currentShapes)
{
de sua primeir
a
shape.draw(canvas,
paint);
versão?
}
Agora falta
}
somente atua
lizar
de
o construtor
.
ShapeWindow
Atualizando
o
Construtor
da
Classe
public
ShapeWindow(Context
context,
final
int
width,
final
int
height)
{
super(context);
setFocusable(true);
setMinimumWidth(width);
O que faz essa
setMinimumHeight(height);
chamada?
setOnTouchListener(this);
currentShapes
=
shapes;
}
Qual o propósito Que interfaces
disso?
ShapeWin dow
implementa
agora?
Encontrando
a
maior
área
Implemente um método
getMaxArea que encontre a
forma de maior área presente
em uma Janela de Formas.
• Quais
decisões
de
projeto
devem
ser
tomadas?
• De
que
formas
diferentes
podemos
resolver
esse
desafio?
– Vantagens,
desvantagens?
A
Interface
Iterable
• Podemos
adicionar
mais
uma
interface
a
ShapeWindow:
Iterable.
– Objetos
desse
Cpo
podem
ser
usados
no
laço
"for‐
each"
que
passou
a
exisCr
em
Java
1.5
– Por
exemplo:
for
(E
e
:
inputs)
{
Quais são os
if
(predicate(e))
{
passos para
results.add(e);
implementarmos
}
Iterable?
}
Implementando
Iterable
public
class
ShapeWindow
extends
View
implements
MonotonicSet<Shape>,
OnTouchListener,
Iterable<Shape>
{
private
List<Shape>
shapes
=
new
ArrayList<Shape>();
class
ShapeWindowIterator
implements
Iterator<Shape>
{
public
boolean
hasNext()
{
}
Como seria a
public
Shape
next()
{
}
implementação
de cada um
public
void
remove()
{
}
desses métodos?
}
public
Iterator<Shape>
iterator()
{
return
new
ShapeWindowIterator();
}
}
Implementando
um
Iterador
class
ShapeWindowIterator
implements
Iterator<Shape>
{
private
int
pos
=
0;
public
boolean
hasNext()
{
return
pos
<
shapes.size();
}
public
Shape
next()
{
return
shapes.get(pos++);
}
public
void
remove()
{
throw
new
RunCmeExcepCon
("A‡empt
to
remove
shapes
from
window.");
}
}
Maior
Área
• Ainda
precisamos
implementar
o
método
getMaxArea.
Implemente esse
Shape
getMaxArea
método usando o
(final
Iterable<Shape>
shapes)
{
nosso template
…
Filter.
}
O que deveria
ser filtrado?
Como obter o
máximo?
Filtrando
o
Maior
Elemento
Shape
getMaxArea(final
Iterable<Shape>
shapes)
{
List<Shape>
largeShapes
=
(new
Filter<Shape>()
{
Esse código é
Shape
maxSoFar
=
null;
realmente
@Override
complicado.
boolean
predicate(Shape
e)
{
Explique-o.
boolean
hasChanged
=
false;
if
(maxSoFar
==
null
||
maxSoFar.getArea()
<
e.getArea())
{
maxSoFar
=
e;
Ser
hasChanged
=
true;
penáa qreuue vale a
sar código Como poderíamos
}
quando o
implementar essa
return
hasChanged;
resultado fica função de forma
assim?
}
mais simples?
}).filter(shapes);
return
largeShapes.get(largeShapes.size()
‐
1);
}
Simplificando
getMax
Shape
getMaxArea2(final
Iterable<Shape>
shapes)
{
Shape
maxSoFar
=
null;
for
(Shape
s
:
shapes)
{
if
(maxSoFar
==
null
||
s.getArea()
>
maxSoFar.getArea())
{
maxSoFar
=
s;
}
}
Shape
getMaxArea(final
Iterable<Shape>
shapes)
{
return
maxSoFar;
List<Shape>
largeShapes
=
(new
Filter<Shape>()
{
}
Shape
maxSoFar
=
null;
@Override
boolean
predicate(Shape
e)
{
Comparando as
boolean
hasChanged
=
false;
duas
if
(maxSoFar
==
null
||
maxSoFar.getArea()
<
e.getArea())
{
implementações,
maxSoFar
=
e;
quando o reúso de
hasChanged
=
true;
código vale a pena?
}
return
hasChanged;
}
}).filter(shapes);
return
largeShapes.get(largeShapes.size()
‐
1);
}
O
Bom
e
Velho
PSL
public
class
ShapeWindow
extends
View
implements
MonotonicSet<Shape>,
OnTouchListener,
Iterable<Shape>
{
private
List<Shape>
shapes
=
new
ArrayList<Shape>();
class
ShapeWindowIterator
implements
Iterator<Shape>
{
private
int
pos
=
0;
public
boolean
hasNext()
{
Por que a nossa Implemente um
return
pos
<
shapes.size();
implementação método que
}
de iteradores remove a forma
public
Shape
next()
{
fere o Princípio de maior área.
da Substituição ?
return
shapes.get(pos++);
}
public
void
remove()
{
throw
new
RunCmeExcepCon("A‡empt
to
remove
shapes
from
window.");
}
}
public
Iterator<Shape>
iterator()
{return
new
ShapeWindowIterator();
}
}
Removendo
um
Item
do
Iterador
• O
método
abaixo
remove
uma
forma
específica
do
iterador
de
formas.
• Ele
é
genérico,
e
pode
ser
uClizado
em
diversos
contextos
diferentes.
void
removeShape(final
Iterator<Shape>
shapes,
Shape
toBeRemoved)
{
for
(Shape
s
=
shapes.next();
shapes.hasNext();
s
=
shapes.next())
{
if
(s
==
toBeRemoved)
{
shapes.remove();
Como usar esse
return;
método para
}
remover a forma
}
de maior área?
}
Removendo
a
Forma
de
Maior
Área
E o que vai
acontecer
void
removeMaxAreaShape(ShapeWindow
shapes)
{
quando
Shape
maxAreaShape
=
getMaxArea(shapes);
executarmos
esse método?
removeShape(shapes.iterator(),
maxAreaShape);
}
void
removeShape(final
Iterator<Shape>
shapes,
Shape
toBeRemoved)
{
for
(Shape
s
=
shapes.next();
shapes.hasNext();
s
=
shapes.next())
{
if
(s
==
toBeRemoved)
{
shapes.remove();
return;
}
}
}
Removendo
a
Forma
de
Maior
Área
void
removeShape(final
Iterator<Shape>
shapes,
Shape
toBeRemoved)
{
for
(Shape
s
=
shapes.next();
shapes.hasNext();
s
=
shapes.next())
{
if
(s
==
toBeRemoved)
{
shapes.remove();
return;
}
}
}
void
removeMaxAreaShape(ShapeWindow
shapes)
{
Shape
maxAreaShape
=
getMaxArea(shapes);
removeShape(shapes.iterator(),
maxAreaShape);
}
Não estamos
seguindo o Como resolver
Princípio da esse problema?
Substituição.
Deixando
os
Tipos
Trabalharem
public
interface
ConstIterator<T>
{
Essa interface não possui
qualquer método para
void
start();
remover elementos da
estrutura iterada.
public
Shape
next()
{
return
this.shapes.get(pos++);
Como ficaria o
}
método que
recupera a forma
public
void
start()
{
de maior área?
pos
=
0;
}
}
A
Nova
Janela
de
Formas
public
class
IterShapeWindow
extends
View
implements
MonotonicSet<Shape>,
OnTouchListener,
ConstIterator<Shape>
{
private
List<Shape>
shapes
=
new
ArrayList<Shape>();
private
int
pos
=
0;
public
boolean
hasNext()
{
return
pos
<
this.shapes.size();
}
Shape
getMaxArea3(final
ConstIterator<Shape>
shapes)
{
Shape
maxSoFar
=
null;
public
Shape
next()
{
shapes.start();
return
this.shapes.get(pos++);
}
for
(Shape
s
=
shapes.next();
shapes.hasNext();
s
=
shapes.next())
{
if
(maxSoFar
==
null
||
s.getArea()
>
maxSoFar.getArea())
{
public
void
start()
{
maxSoFar
=
s;
pos
=
0;
}
}
}
}
return
maxSoFar;
}
Um
Apanhado
de
Interfaces
View MonotonicSet ConstIterator<Shape>
add start
onMeasure contains OnTouchListener next
onDraw size onTouch hasNext
ShapeWindow
shapes Clientes podem usar instâncias
currentShapes de ShapeWindow como
pos ShapeWindow, ou como View,
ShapeWindow ou como MonotonicSet, ou
onMeasure
onDraw
como…
add
contains E métodos que se aplicam a
size MonotonicSet (ou view, ou…)
onTouch
start
podem ser usados sobre
hasNext ShapeWindow, ou sobre outros
next MonotonicSets.
Recapitulando
• Princípio
da
Inversão
de
Dependências:
código
mais
abstrato
não
deve
depender
de
código
mais
concreto.
• Princípio
da
Segregação
de
Interfaces:
cada
classe
deve
ter
somente
uma
responsabilidade.