Programao II, Guio 3 -- Speed - Moodle @ FCT-UNL

Propaganda
Speed
1. Este guião é sobre herança. O exercício consiste, no essencial, em definir uma classe
de base e duas classes derivadas dela. Vamos ainda usar algumas classes cuja
implementação já é fornecida e que também usam a herança e fazer algumas
experiências com elas. No final deste enunciado encontrará um diagrama de classes
UML relativo à solução pretendida. (Se não sabe de que estamos a falar quando
dizemos “herança” em programação, reveja rapidamente as aulas, consulte os
acetatos, os livros ou mesmo a internet. Este guião não vai explicar o que é a
herança). Nesta altura do campeonato já não é aceitável que o seu código não esteja
comentado de acordo com as convenções do Javadoc. Repare que no Eclipse pode
gerar o esqueleto dos comentários seleccionado o botão direito do rato e clicando na
opção “Source/Add Comment” ou Alt+Shift+J. Deve preencher os comentários
ANTES de fazer submissões ao Mooshak porque depois de aceite é tarde demais …
2. A classe de base é a classe Speedometer (velocímetro) cujos objectos podem ser
usados para modelar os velocímetros que encontramos nos automóveis e noutros
veículos. Um veículo ou acelera (para simplificar vamos apenas considerar que será
de forma uniforme), ou mantém a velocidade constante, ou trava (também de forma
uniforme). Para sabermos a velocidade que o velocímetro deve afixar, precisamos
de conhecer a duração (intervalo de tempo, por exemplo, 3 segundos ou duas horas,
vinte minutos e quarenta e nove segundos, expressa na classe em segundos) de cada
período de aceleração e travagem.
3. Comece então um novo projecto Speed no seu workspace e declare a classe
Speedometer, com as seguintes funções: Speed, que dá a velocidade corrente,
Travel, com um argumento de tipo double representando o intervalo de tempo em
que o veículo mantém a velocidade corrente, Duration, que retorna a duração da
viagem, desde o início (isto é, desde que o objecto foi inicializado), Accelerate, com
dois argumentos, a aceleração positiva e a duração do movimento uniformemente
acelerado, Brake, para travar, simétrica do anterior, com aceleração negativa, mas
representada por um argumento positivo na chamada da função, Stop, para parar,
com um argumento positivo representando a aceleração negativa até à imobilização
do veículo. Declare ainda uma função Display capaz de mostrar a velocidade
corrente e a duração da viagem, uma função SetSpeedUnits para seleccionar as
unidades com as quais a velocidade é mostrada na função Display e uma função
SpeedUnits para devolver o valor convencional que representa as unidades que estão
em vigor. Saiba que uma milha são 1609,3 metros e declare já a respectiva constante
estática (usando o classificador “final”). O argumento da função SetSpeedUnits, bem
como o valor de retorno da função SpeedUnits, devem ser de tipo String e os valores
reconhecidos são “m/s”, “km/h” e “mph” (metros por segundo, quilómetros por
hora, milhas por hora, respectivamente). A função Display mostra a velocidade em
formato de ponto fixo, com três casas decimais, usando as unidades correntemente
seleccionadas, seguida de um espaço, da cadeia que indica as unidades, de outro
espaço e da duração da viagem. A duração da viagem vem no formato
h:mm:ss.mmm, por exemplo 2:03:45.540 representa duas horas, três minutos,
quarenta e cinco segundos e quinhentos e quarenta milésimos de segundo. Quer
dizer, os minutos e os segundos ocupam sempre duas posições, mesmo que sejam
menos do que 10, e os milésimos de segundo usam sempre três algarismos.
4. Que membros de dados fazem falta? Sugiro um para a velocidade corrente, outro
para a duração da viagem, outro ainda para a velocidade máxima que o veículo pode
atingir e finalmente um para registar as tais unidades da velocidade mostrada com a
função Display. Não confunda as unidades da função Display com as unidades
usadas nos argumentos das funções e nos cálculos. Estas serão sempre as do sistema
internacional, ou seja, a duração é em segundos, a velocidade em metros por
segundo e a aceleração em metros por segundo quadrado.
5. Nesta classe declare pelo menos um construtor com um argumento para a
velocidade máxima. De resto, o veículo inicialmente está parado, a duração é nula e
a função Display usa metros por segundo. Note que a velocidade máxima é
constante para cada velocímetro, no sentido de que não há maneira de a modificar
após ter sido definida no construtor, mas pode variar de velocímetro para
velocímetro.
6. Já está? Antes de programarmos os corpos das funções da classe Speedometer,
refresquemos a nossa física. O problema envolve movimento uniformemente
acelerado e por isso vamos ter de recorrer às conhecidas leis da cinemática: d = v0t +
½ at2 e v = v0 + at, onde d é a distância percorrida, v0 é a velocidade inicial, a é a
aceleração, t é a duração do movimento e v é a velocidade final. (Lembra-se? Não
me diga que não!)
7. Passemos já à tarefa A que resolve o seguinte problema: temos uma partícula parada
e aplicamos-lhe uma sequência de forças, cada uma durante um intervalo de tempo.
Onde é que a partícula vai parar? Ou, melhor, em que posição estará a partícula
depois da aplicação dessas forças e qual a sua velocidade nesse instante? (Sim,
porque em geral a partícula não estará parada, pois não?) Claro que aqui nos
baseamos na fórmula F = ma, onde F é a força, m é a massa e a é a aceleração. Na
primeira linha do ficheiro de dados vem a posição inicial (coordenadas x e y) e a
massa da partícula, tudo números reais. Seguem-se linhas em número
indeterminado, cada uma com três números reais: a força aplicada, a direcção da
força em graus (zero graus é o sentido do semi-eixo positivo dos x e os graus
crescem no sentido contrário ao dos ponteiros do relógio) e a duração da aplicação
da força em segundos. A aplicação da força a uma partícula deverá ser modelada
com a função ApplyForce com os três argumentos citados (intensidade da força,
direcção e duração). Eis um exemplo:
1.0 2.0 10.0
20.0 0.0 3.0
30.0 90.0 1.0
10.0 225.0 4.0
8. O correspondente ficheiro de resultados tem duas linhas, cada uma com dois
números reais escritos em notação de ponto fixo com duas casas decimais e
separados por um espaço. Na primeira linha vêm as coordenadas x e y da partícula e
na segunda as componentes x e y da velocidade, tudo calculado para o instante em
que a última força cessa de ser aplicada. Com o exemplo acima, o resultado deve ser
o seguinte.
34.34 9.84
3.17 0.17
9. Nesta tarefa, deve usar as classes Particle e Point fornecidas junto com este
enunciado (pode encontrar o diagrama UML correspondente em baixo também).
Estas classes não estão na biblioteca que o Mooshak linca com o seu programa. Por
isso, tem de as submeter no seu ficheiro zip. Repare que a força aplicada a uma
partícula num espaço a duas dimensões pode ser decomposta em duas componentes,
uma segundo o eixo dos xx e outra dos yy, usando para isso as funções
trigonométricas do seno e co-seno (atenção que estas funções esperam que os
argumentos estejam em radianos e não em graus, pelo que é possível que lhe dê jeito
usar a constante Math.PI). Sabendo a força e a massa sabe-se a aceleração segundo
cada um dos eixos e, por isso basta aplicar as leis da cinética separadamente para
calcular as posições finais e as velocidades finais segundo esses mesmos eixos. Para
resolver este passo crie a classe MovingParticle, que herda de Particle, mas que,
para além de saber a sua posição, sabe também a sua velocidade. Nesta nova classe
crie a função ApplyForce atrás citada. Se este passo A lhe parecer demasiado
complicado, passe ao seguinte e volte aqui depois de acabar o passo C. É que uma
boa solução para o problema A é a inclusão de dois tacógrafos como variáveis da
classe MovingParticle, um para o movimento segundo o eixo dos xx e outro
segundo o dos yy. Mas para isso tem de construir o tacógrafo primeiro, não é? ☺
10. Quanto à precisão dos cálculos, como os resultados são escritos com apenas duas
casas decimais, nunca haverá problemas de falta de precisão, a não ser se
obtivermos um valor muito próximo de zero, mas inferior, situação esta em que
apareceria “-0.00”. Pois bem, o seu programa não deve nunca escrever -0.00. (Esta
observação aplica-se, com as devidas adaptações, no resto deste guião.) Nos
ficheiros oficiais no Mooshak todos os zeros de tipo double são escritos sem sinal,
garantidamente.
11. Regressemos à nossa classe Speedometer, para programar as funções. Não se
esqueça de declarar a constante estática com o valor adequado. Ao programar,
considere que a velocidade não pode ser negativa nem maior do que a velocidade
máxima. Acelerar ou travar para fora desses limites não tem efeito.
12. A tarefa B é a função de teste da classe Speedometer. A primeira linha do ficheiro
de dados contém um número real, a velocidade máxima. Seguem-se linhas de
comando que começam por “a”, “b”, “t”, “s”, “u” ou “d”, para testar as funções
Accelerate, Brake, Travel, Stop, SetSpeedUnits e Display, respectivamente. As
linhas que começam por “a” e “b” contêm mais dois números reais positivos que
representam a aceleração e a duração da aceleração. No caso do comando “a” o
veículo acelera; no caso do comando “b” o veículo trava. (Já sabemos que na
travagem a aceleração é negativa, mas é aqui dada em valor absoluto.) As linhas que
começam por “t” contêm mais um número real que representa a duração do
intervalo de tempo durante o qual o veículo segue com velocidade constante. As
linhas que começam por “s” contêm mais um número real que representa a
aceleração dessa travagem, que continua até o veículo se imobilizar. As linhas que
começam por “u” têm ainda uma cadeia “m/s”, “km/h” ou “mph” com o significado
anteriormente explicado registam que a partir de agora a função Display usa as
unidades indicadas. As linhas começadas por “d” não têm mais nada. Em resposta
ao comando “d”, o programa invoca a função Display para escrever na consola,
numa linha, uma mensagem com a velocidade e a distância percorrida, no formato
descrito anteriormente. Já agora crie uma outra chamada DisplayLn que faz o
mesmo mas salta para a linha seguinte (à semelhança do println). Eis um ficheiro de
teste:
30
a 1 20
d
t 5
a 3 5
d
t 1000
b 2 1
t 3000
d
u km/h
d
s 4
d
13. Note que os valores numéricos neste ficheiro de entrada são números reais (embora
neste pequeno exemplo sejam todos inteiros) e, por isso, devem ser lidos para
variáveis double. O correspondente ficheiro de resultados é o seguinte:
20.000 m/s 0:00:20.000
30.000 m/s 0:00:30.000
28.000 m/s 1:07:11.000
100.800 km/h 1:07:11.000
0.000 km/h 1:07:18.000
14. Pronto, a classe de base já está.
15. Um velocímetro é um instrumento muito útil, mas ficava ainda melhor se tivesse um
totalizador de distância percorrida. Pois bem, não vamos mexer na nossa classe
Speedometer, que já está pronta e testada, vamos sim declarar uma classe derivada,
isto é, uma nova classe que herda de Speedometer e que acrescenta esta
funcionalidade de totalizar a distância.
16. Declare então uma classe Tachograph, derivada de Speedometer, com três novas
funções: Distance, que calcula a distância percorrida desde o início ou desde a mais
recente reiniciação do totalizador, AverageSpeed, que dá a velocidade média desde a
mais recente reiniciação do totalizador (ou desde o início, se o totalizador nunca
tiver sido reiniciado), e Reset, que reinicia o totalizador, colocando-o a zero. A
classe derivada redefine também as funções herdadas Travel, Accelerate, Brake e
Stop, pois essas funções precisam agora de actualizar a distância percorrida. A
função Display também deve ser redefinida, de maneira a mostrar também a
distância percorrida e a velocidade média, nas unidades correspondentes às da
velocidade. A distância percorrida e a velocidade média são escritas com uma casa
decimal e com a indicação das unidades utilizadas. Assim a mensagem escrita pela
função Display contém, para além do que já é apresentado por
Speedometer.Display(), a distância percorrida, as unidades da distância percorrida
(“m”, “km” ou “miles”), a velocidade média e as unidades da velocidade. Entra cada
dois destes elementos de informação vem um espaço.
17. Programe agora as três novas funções, as funções redefinidas e o construtor. Em
relação à velocidade média, o caso em que a duração é zero (imediatamente no
início ou após um Reset) dá convencionalmente velocidade média zero. Como
exemplo, observe a programação da função Travel, sendo distance_ o membro de
dados da classe Tachograph que representa a distância percorrida:
void Travel (double duration)
{
super.Travel (duration);
distance_ += Speed() * duration;
}
18. A tarefa C é uma função de teste para a classe Tacograph. O funcionamento é
análogo ao da função para a tarefa B, mas há mais um comando, “r”, que reinicia o
totalizador. Todos os outros comandos são como antes, mas agora o comando “d”
serve para invocar a função Display redefinida. Uma vez mais tenha atenção que os
valores numéricos no ficheiro de entrada são números reais, embora no exemplo que
se segue só apareçam inteiros:
30
a 2 10
d
t 100
d
b 3 2
r
t 100
t 50
d
u mph
d
19. O correspondente ficheiro de resultados é o seguinte:
20.000
20.000
14.000
31.318
m/s
m/s
m/s
mph
0:00:10.000
0:01:50.000
0:04:22.000
0:04:22.000
100.0 m 10.0 m/s
2100.0 m 19.1 m/s
2100.0 m 14.0 m/s
1.3 miles 31.3 mph
20. Como há tantos acidentes causados por excesso de velocidade e como nós,
entretidos a conduzir, às vezes nem reparamos que vamos depressa demais, está em
estudo um novo modelo de velocímetro que apita de cada vez que o veículo
ultrapassa uma dada velocidade e que conta o número de vezes que isso acontece.
Assim, no fim de uma viagem podemos perguntar ao velocímetro: quantas
infracções cometi? Programe então uma nova classe SafetyTachograph, derivada de
Tachograph, capaz de suportar essa funcionalidade. A classe deve ter uma função
Reset para reiniciar a zero o contador de infracções a meio da viagem e pelo menos
outra denominada Infractions que indica o número de infracções cometidas desde
que o contador foi reiniciado (ou desde o início, se o totalizador nunca tiver sido
reiniciado). A velocidade máxima e a velocidade de segurança devem ser
estabelecidas no construtor. Repare que para se contabilizar uma nova infracção, é
necessário que se regresse a uma situação de legalidade (em que a velocidade não
exceda o limite de segurança) antes de a cometer.
21. A tarefa D é uma função para exercitar a classe SafetyTachograph. Aceita um
ficheiro de comandos como o das outras tarefas, mas com ligeiras diferenças. A
primeira linha contém agora dois números reais: a velocidade máxima (como antes)
e a velocidade de segurança. Há um novo comando, o comando “i”, sem mais nada
na linha, que invoca a função que reinicia o contador. Os comandos “a”, “b” e “s”
são processados como antes (não se esqueça que podemos ter valores reais e não só
inteiros). Os restantes são ignorados. No final a função escreve uma linha com um
número inteiro: o número de infracções de excesso de velocidade registadas no
velocímetro de segurança (desde o início da viagem ou desde a última vez que o
contador de infracções foi reiniciado). Considere o seguinte ficheiro de dados, como
exemplo:
50 15
a 3 4
t 5
a 1 2
t 5
a 2 1
i
b 2 1
a 2 1
t 5
b 2 1
t 5
a 3 2
b 5 1
t 4
a 2 1
b 2 2
t 6
a 3 2
s 5
22. O correspondente ficheiro de resultados, só com uma linha, é o seguinte:
4
23. Conduza sempre com cuidado. E programe sempre com cuidado.
Download