Вы находитесь на странице: 1из 55

Programação
em
Java
para
a
Plataforma
Android
–
AULA
21


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.

Formas que estão fora da área de visão, ou que


são muito pequenas para serem vistas não
precisam ser desenhadas.

Aplicaremos essa técnica em nossa aplicação,


desenhando somente aquelas formas que
possuem uma área grande.

Implemente um método hasLargeArea que


receba uma lista de formas, e retorne a
sublista com as formas de área grande, isso é,
área maior que um certo limiar.
Primeira
TentaCva


private
List<Shape>
hasLargeArea(List<Shape>
shapes,
float
area)
{





List<Shape>
largeShapes
=
new
LinkedList<Shape>();

Qual o




for
(Shape
shape
:
shapes)
{

problema com






if
(shape
instanceof
Rectangle)
{
 esse código?








Rectangle
r
=
(Rectangle)
shape;









if
(r.getWidth()
*
r.getHeight()
>=
area)
{
largeShapes.add(r);
}







}
else
if
(shape
instanceof
Dot)
{









Dot
d
=
(Dot)
shape;









if
(d.getDiameter()
*
Math.PI
>=
area)
{
largeShapes.add(d);
}







}
else
if
(shape
instanceof
ImmutableCircle)
{









ImmutableCircle
c
=
(ImmutableCircle)
shape;









if
(c.r
*
Math.PI
>=
area)
{
largeShapes.add(c);
}







}
else
{
throw
new
RunCmeExcepCon("Forma
desconhecida!");
}





}





return
largeShapes;



}

Testando
via
um
cliente
simples

DrawShapesAcCvity.java


public
void
onCreate(Bundle
savedInstanceState)
{





super.onCreate(savedInstanceState);





setContentView(R.layout.main);





List<Shape>
shapes
=
new
LinkedList<Shape>();





shapes.add(new
Rectangle(0,
0,
10,
18));





shapes.add(new
Square(90,
90,
25));





shapes.add(new
Dot(12,
13,
Color.BLACK,
6));





shapes.add(new
Dot(22,
43,
Color.CYAN,
6));





shapes.add(new
Dot(8,
33,
Color.GREEN,
6));





ShapeWindow
shapeWindow
=
new
ShapeWindow(this,
280,
300);





for
(Shape
shape
:
hasLargeArea
(shapes,
20.0F))
{







shapeWindow.addShape(shape);





}





((LinearLayout)
findViewById(R.id.root)).addView(shapeWindow,
0);





shapeWindow.invalidate();



}

Novamente
a
Abertura/Fechamento

•  O
código
anterior
não
adere
ao
princípio
da

Abertura/Fechamento.

–  A
inspeção
de
Cpo
dinâmica
compromete
a

extensibilidade
do
código.

–  Podemos
resolver
isso
passando
o
conhecimento

de
área
para
cada
forma:


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.


Quais os Existe algum


Quando esse
problemas de truque para se
inconveniente
não se respeitar preservar esse
pode acontecer?
esse princípio? princípio?
A
Beleza
do
Simples

•  O
Simples
é
Belo…

–  mas
não
vende!


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.

1)  Esse método não deveria


fazer parte da interface de
ShapeWindow. Por que?

2)  Mas então, como podemos


encontrar a forma de maior
área? Formas são dados
privados de ShapeWindow.
Encontrando
a
maior
área

Implemente um método Shape
getMaxArea

getMaxArea que encontre a 





(final
Iterable<Shape>
shapes)
{

forma de maior área presente 

…

em uma Janela de Formas. }

1)  Esse método não deveria
fazer parte da interface de
ShapeWindow. Por que?

2)  Mas então, como podemos


encontrar a forma de maior
área? Formas são dados
privados de ShapeWindow.
Lendo
os
Pontos
de
Volta

•  Não
é
possível
saber
quais
formas
estão

presentes
em
ShapeWindow.
Resolva
esse

problema.


•  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.



boolean
hasNext();
 Ele é exatamente o que


nossa classe


T
next();
 ShapeWindow precisa.
}


Mas então como


fica a
implementação de
ShapeWindow?
A
Nova
Janela
de
Formas

public
class
IterShapeWindow
extends
View
implements
MonotonicSet<Shape>,





OnTouchListener,
ConstIterator<Shape>
{



private
List<Shape>
shapes
=
new
ArrayList<Shape>();
 Com essa


private
int
pos
=
0;
 lementação
imp


public
boolean
hasNext()
{
 perdemos algo




return
pos
<
this.shapes.size();
 importante. O quê



}
 foi que perdemos?



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.


Вам также может понравиться