Programação com Objectos/Projecto de Programação com Objectos/Enunciado do Projecto de 2017-2018 (rascunho de Agosto)

From Wiki**3

< Programação com Objectos‎ | Projecto de Programação com Objectos
Revision as of 16:24, 2 September 2017 by Root (talk | contribs) (Menu de Gestão de Passageiros)

AVISOS - Avaliação em Época Normal

Esclarecimento de dúvidas:

  • Consultar sempre o corpo docente atempadamente: presencialmente ou através do endereço oficial da disciplina [1].
  • Não utilizar fontes de informação não oficialmente associadas ao corpo docente (podem colocar em causa a aprovação à disciplina).
  • Não são aceites justificações para violações destes conselhos: quaisquer consequências nefastas são da responsabilidade do aluno.

Requisitos para desenvolvimento, material de apoio e actualizações do enunciado (ver informação completa em Projecto de Programação com Objectos):

  • O material de apoio é de uso obrigatório e não pode ser alterado.
  • Verificar atempadamente (mínimo de 48 horas antes do final de cada prazo) os requisitos exigidos pelo processo de desenvolvimento.

Processo de avaliação (ver informação completa em Avaliação do Projecto):

  • Datas: 2017/10/18 12:00 (inicial); 2017/11/20 12:00 (intercalar); 2017/12/11 12:00 (final); 2017/12/11-2017/12/15 (teste prático).
  • Todas as entregas são cruciais para o bom desenvolvimento do projecto, sendo obrigatórias: a não realização de uma entrega implica a exclusão da avaliação do projecto e, por consequência, da avaliação da disciplina.
  • Verificar atempadamente (até 48 horas antes do final de cada prazo) os requisitos exigidos pelo processo de avaliação, incluindo a capacidade de acesso ao repositório.
  • Apenas se consideram para avaliação os projectos existentes no repositório oficial. Apenas se considera para avaliação o ramo 'main'.
  • Trabalhos não presentes no repositório no final do prazo têm classificação 0 (zero) (não são aceites outras formas de entrega). Não são admitidas justificações para atrasos em sincronizações do repositório. A indisponibilidade temporária do repositório ou de outros materiais, desde que inferior a 24 horas, não justifica atrasos na submissão de um trabalho.
  • A avaliação do projecto pressupõe o compromisso de honra de que o trabalho correspondente foi realizado pelos alunos correspondentes ao grupo de avaliação.
  • Fraudes na execução do projecto terão como resultado a exclusão dos alunos implicados do processo de avaliação.
Material de Uso Obrigatório
As bibliotecas po-uuilib e o conteúdo inicial do CVS são de uso obrigatório:
  • po-uuilib (classes de base) po-uuilib-201708311009.tar.bz2 (não pode ser alterada) - javadoc
  • mmt-core (classes do "core") (via CVS) (deve ser completada -- os nomes das classes fornecidas não podem ser alterados)
  • mmt-app (classes de interacção) (via CVS) (deve ser completada -- os nomes das classes fornecidas não podem ser alterados)
A máquina virtual, fornecida para desenvolvimento do projecto, já contém todo o material de apoio.
Uso Obrigatório: Repositório CVS
Apenas se consideram para avaliação os projectos existentes no repositório CVS oficial.

Trabalhos não presentes no repositório no final do prazo têm classificação 0 (zero) (não são aceites outras formas de entrega). Não são admitidas justificações para atrasos em sincronizações do repositório. A indisponibilidade temporária do repositório, desde que inferior a 24 horas, não justifica atrasos na submissão de um trabalho.

O objectivo do projecto é criar uma aplicação que gere os itinerários de um passageiro que usa comboios.

Os itinerários são constituídos por um ou mais segmentos, realizados por comboios que cumprem horários pré-definidos.

Neste texto, o tipo negrito indica um literal (i.e., é exactamente como apresentado); o símbolo indica um espaço; e o tipo itálico indica uma parte variável (i.e., uma descrição).

Estrutura dos horários, dos passageiros e dos itinerários

Existem dois conceitos básicos: horário e itinerário.

Estrutura de um horário

Um horário é um percurso realizado por um comboio ao longo de vários locais. A cada local está associado o momento de partida do comboio.

Exemplo de horário com duas classes de serviço: Serviço 180: Porto >> Faro
  Serviço #180 - Conforto 70.70 / Turística 51.50
  05:47 Porto - Campanhã
  05:52 Vila Nova de Gaia
  06:21 Aveiro
  06:46 Coimbra-B
  08:23 Lisboa - Oriente
  08:31 Lisboa - Entrecampos
  09:06 Pinhal Novo
  10:54 Tunes
  11:01 Albufeira - Ferreiras
  11:13 Loulé
  11:23 Faro
Exemplo de horário com uma classe de serviço: Serviço 850: Valença >> Porto
  Serviço #850 - Única 10.95
  07:36 Valença
  07:47 Vila Nova de Cerveira
  07:56 Caminha
  08:03 Âncora Praia
  08:16 Viana do Castelo
  08:20 Areia-Darque
  08:30 Barroselas
  08:37 Tamel
  08:53 Barcelos
  09:05 Nine
  09:13 Famalicão
  09:21 Trofa
  09:35 Ermesinde
  09:45 Porto - Campanhã

Cada segmento de um horário tem um custo base, que depende da distância entre os locais e do tipo de serviço. Por simplicidade, e sem limitação de funcionalidade, considera-se que o custo é uniforme entre estações, ou seja, se um horário tiver um custo total C, para uma dada classe de serviço, e tiver N estações, cada segmento terá um curso S = C / (N-1).

O preço pago pelo passageiro depende ainda do tipo de cliente, que pode ser normal ou de outros tipos (a adição de novos tipos deve ter impacto reduzido no código da aplicação).

Cada comboio suporta um ou mais serviços. As classes de serviço são "conforto", "turística" e "única".

Estrutura de um passageiro

Um passageiro tem um identificador único (número inteiro), um nome (cadeia de caracteres) e um tipo a que correspondem descontos na compra de itinerários.

Os passageiros normais não dispõem de qualquer tipo de desconto. Os passageiros frequentes (já compraram mais de 50000 km em mais de 10 itinerários) dispõem de 25% de desconto. Se o passageiro já tiver gasto mais de €10000, o desconto é de 50%, mantendo-se as condições anteriores.

Passageiros que viajam em classe turística não têm direito a descontos, independentemente do tipo de passageiro.

Estrutura de um itinerário

Um itinerário é um conjunto de segmentos, correspondentes a partes de horários. Cada segmento tem um local e hora de partida e um local e uma hora de chegada. É possível saber, através do horário associado, qual o percurso a realizar nesse segmento. O custo de cada segmento é o do percurso realizado no horário e serviço correspondente.

O custo do itinerário é a soma dos custos dos segmentos individuais. Dependendo do tipo de cliente, podem ser realizados descontos em alguns tipos de serviço. A classe de serviço está associada a cada segmento e não ao itinerário em si.

Funcionalidade da aplicação

A aplicação permite manter informação sobre horários e criar novos itinerários para o passageiro. Possui também várias formas de preservar o seu estado (não é possível manter várias versões do estado do interpretador em simultâneo).

Interpretação de expressões

As expressões, apresentadas ao interpretador em forma de texto, devem ser interpretadas antes de serem armazenadas. O texto original não é preservado. Note-se que o processo de interpretação (conversão do texto para estruturas/objectos) é diferente do de avaliação (conversão das estruturas/objectos para valores).

O texto a interpretar (expressões ou programas) pode ser providenciado via interface do próprio interpretador ou na forma de um ficheiro que contém um programa.

Uma falha de interpretação causa o lançamento de uma excepção no analisador do interpretador.

Note-se que não é necessário implementar de raiz o analisador de expressões e programas do interpretador. Um analisador já parcialmente concretizado, mmt.Parser, está disponível. Este analisador é responsável por processar um programa ou uma expressão e realizar a conversão de texto para a estrutura de dados correspondente. Será apenas necessário indicar as entidades utilizadas para representar um programa ou uma expressão.

Armazenamento de programas

O interpretador permite associar nomes a programas, preservando-os. Note-se que isto não corresponde a guardar valores calculados por esses programas, mas a guardar as suas descrições, i.e., as resultantes da interpretação da forma textual correspondente. Se um nome já estiver em uso, a associação ao programa anterior é perdida.

Interpretação de programas

A interpretação de programas e, consequentemente, das suas expressões, deve ser feita de forma flexível. Ou seja, deve ser possível -- sem alterar o código das expressões ou do interpretador -- definir novas formas de avaliação. Por omissão, a avaliação corresponde simplesmente à apresentação de uma forma textual do programa (e das suas expressões).

Uma outra avaliação possível é o cálculo dos valores das expressões do programa, correspondente à execução desse programa.

Serialização

É possível reiniciar, guardar e recuperar o estado actual da aplicação, preservando todos a informação de horários e itinerários.

Interacção com o utilizador

Descreve-se nesta secção a funcionalidade máxima da interface com o utilizador. Em geral, os comandos pedem toda a informação antes de proceder à sua validação (excepto onde indicado). Todos os menus têm automaticamente a opção Sair (fecha o menu).

As operações de pedido e apresentação de informação ao utilizador devem realizar-se através dos objectos form e display, respectivamente, presentes em cada comando. As mensagens são produzidas pelos métodos das bibliotecas de suporte (po-uuilib e mmt-app). As mensagens não podem ser usadas no núcleo da aplicação (mmt-core). Além disso, não podem ser definidas novas. Potenciais omissões devem ser esclarecidas antes de qualquer implementação.

As excepções usadas na interacção, excepto se indicado, são subclasses de pt.tecnico.po.ui.DialogException, são lançadas pelos comandos e tratadas por pt.tecnico.po.ui.Menu. Outras excepções não devem substituir as fornecidas nos casos descritos.

Note-se que os comandos e menus a seguir descritos, assim como o programa principal, já estão implementados nas classes das packages mmt.app, mmt.app.main e mmt.app.evaluator. Estas classes são de uso obrigatório e estão disponíveis no CVS (módulo mmt-app).

Menu Principal

As acções do menu permitem gerir a salvaguarda do estado da aplicação: Reiniciar, Abrir, Guardar e Consulta de Horários, Manipulação de Passageiros e Manipulação de Itinerários. A classe mmt.app.main.Message define os métodos para geração das mensagens de diálogo. Inicialmente, a aplicação apenas tem os horários que foram carregados no arranque.

Estes comandos já estão implementados nas classes da package mmt.app.main (disponível no CVS), respectivamente: DoReset, DoOpen, DoSave, DoOpenServicesMenu, DoOpenPassengersMenu, DoManageIninerary.

Salvaguarda do estado actual da aplicação

O conteúdo da aplicação (inclui todos os horários, passageiros e itinerários actualmente em memória) pode ser guardado para posterior recuperação (via serialização Java: java.io.Serializable). Na leitura e escrita do estado da aplicação, devem ser tratadas as excepções associadas. A funcionalidade é a seguinte:

  • Reiniciar -- Reinicia a aplicação: destrói toda a informação sobre passageiros e itinerários; mantém toda a informação sobre horários.
  • Abrir -- Carrega os dados de uma sessão anterior a partir de um ficheiro (ficando associado à aplicação). A informação a carregar compreende horários, passageiros e itinerários. Pede-se o nome do ficheiro a abrir (openFile()). Caso o ficheiro não exista, é apresentada a mensagem fileNotFound().
  • Guardar -- Guarda o estado actual da aplicação (horários, passageiros e itinerários) no ficheiro associado. Se não existir associação, pede-se o nome do ficheiro a utilizar, ficando a ele associado. Esta interacção realiza-se através do método newSaveAs(). Não é executada nenhuma acção se não existirem alterações desde a última salvaguarda.

As opções Reiniciar e Abrir substituem a informação na aplicação nas condições indicadas acima.

A opção Sair nunca guarda o estado da aplicação, mesmo que existam alterações.

Gestão e consulta de dados da apliacação

  • Consulta de Serviços -- Abre o menu de consulta de serviços (se existirem serviços registados).
  • Gestão de Passageiros -- Abre o menu de gestão de passageiros (se existirem passageiros registados).
  • Gestão de Itinerários -- Abre o menu de gestão de itinerários (se existirem itinerários registados).

Menu de Consulta de Serviços

Este menu permite efectuar consultas sobre a base de dados de serviços. A lista completa é a seguinte: Mostrar todos os serviços, Mostrar serviço com um dado número, Mostrar serviços com origem numa estação dada, Mostrar serviços com término numa estação dada.

As etiquetas das opções deste menu estão definidas na classe mmt.app.service.Label. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe mmt.app.service.Message.

Sempre que for pedido o identificador de um serviço (requestServiceId()) e o serviço não existir, é lançada a excepção mmt.app.NoSuchServiceException. Sempre que for pedido o nome de uma estção (requestStationName()) e a estação não existir, é lançada a excepção mmt.app.NoSuchStationException.

Estes comandos já estão implementados nas classes da package mmt.app.service (disponível no CVS), respectivamente: DoShowAllServices, DoShowServiceByNumber, DoShowServicesOriginatingAtStation, DoShowServicesEndingAtStation.

Mostrar todos os serviços

Apresenta informações sobre todos os serviços conhecidos. A lista é ordenada pelo identificador do serviço e o formato é o descrito em Mostrar serviço com um dado número.

Mostrar serviço com um dado número

Apresenta informações sobre um serviço. Depois de pedir o identificador do serviço (requestServiceId()), apresenta a informação sobre o serviço, tal como indicado no formato seguinte, para um serviço com X classes e N paragens (HH;MM representa o tempo em horas e minutos).

Formato de apresentação de serviço
  Serviço #número-do-serviço - classe-1 preço-da-classe-1 / ... / classe-X preço-da-classe-X
  HH:MM nome-de-estação-1
 ...
  HH:MM nome-de-estação-N
Exemplo de apresentação de serviço: Serviço 180: Porto >> Faro
  Serviço #180 - Conforto 70.70 / Turística 51.50
  05:47 Porto - Campanhã
  05:52 Vila Nova de Gaia
  06:21 Aveiro
  06:46 Coimbra-B
  08:23 Lisboa - Oriente
  08:31 Lisboa - Entrecampos
  09:06 Pinhal Novo
  10:54 Tunes
  11:01 Albufeira - Ferreiras
  11:13 Loulé
  11:23 Faro

Mostrar serviços com origem numa estação dada

Apresenta os números e estações terminais dos serviços com origem na estação indicada como resposta a requestStationName(), de acordo com o seguinte formato e ordenado por número de serviço:

número término

Exemplo (resposta com 2 serviços com origem em Lisboa - Santa Apolónia):

131 Braga
4445 Tomar

Mostrar serviços com término numa estação dada

Apresenta os números e estações de origem dos serviços com término na estação indicada como resposta a requestStationName(), de acordo com o seguinte formato e ordenado por número de serviço:

número origem

Exemplo (resposta com 2 serviços com término em Porto - Campanhã):

182 Faro
856 Valença

Menu de Gestão de Passageiros

Este menu permite efectuar operações sobre um passageiro. A lista completa é a seguinte: Mostrar passageiros, Mostrar passageiro, Registar passageiro, Alterar nome de passageiro, Suspender passageiro, Activar passageiro.

As etiquetas das opções deste menu estão definidas na classe mmt.app.passenger.Label. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe mmt.app.passenger.Message.

Sempre que for pedido o identificador de um passageiro (requestPassengerId()) e o passageiro não existir, é lançada a excepção mmt.app.NoSuchPassengerException.

Estes comandos já estão parcialmente implementados nas classes da package mmt.app.passenger (disponível no CVS), respectivamente: ShowPassengers, ShowPassengerDetails, RegisterPassenger, ChangePassengerName, SuspendPassenger, EnablePassenger.

Mostrar passageiros

Apresenta informações sobre todos os passageiros conhecidos. A lista é ordenada pelo identificador e o formato é o descrito em Mostrar passageiro.

Mostrar passageiro

Apresenta informações sobre um passageiro. Depois de pedir o identificador do passageiro (requestPassengerId()), apresenta o nome, o tipo de passageiro, o número de itinerários adquiridos, o valor acumulado de distâncias correspondentes aos itinerários adquiridos e o valor pago por esses itinerários. É ainda indicado se o passageiro está activo (ACTIVO) ou inactivo (INACTIVO).

Exemplo:

1234|John Doe|Normal|123|98765|9876.54|ACTIVO

Mostrar os itinerários associados ao passageiro

Apresenta os itinerários do passageiro cujo identificador é pedido através de requestPassengerId(). O formato de apresentação é como para os serviços, mas apenas se apresenta o segmento viajado e a classe seleccionada no segmento.


Registar passageiro

Permite registar um passageiro no sistema. É pedido o nome do passageiro (requestPassengerName()). O novo passageiro é do tipo Normal.

Alterar nome de passageiro

Permite alterar o nome do passageiro. Para tal, pede-se o identificador do passageiro cujo nome deve ser alterado (requestPassengerId()) e o novo nome (requestPassengerName()).

Suspender passageiro

Permite suspender (temporariamente) o passageiro indicado através do identificador pedido (requestPassengerId()). A suspensão de um passageiro apenas impede que reserve itinerários, mas não elimina os seus itinerários nem restante informação, sendo possível continuar a fazer consultas,

Activar passageiro

Permite activar o passageiro indicado através do identificador pedido (requestPassengerId()). O passageiro volta a poder adquirir itinerários.

Leitura de Dados a Partir de Ficheiros Textuais

Além das opções de manipulação de ficheiros descritas no menu principal, é possível iniciar a aplicação com um ficheiro de texto especificado pela propriedade Java import. Este ficheiro contém um programa que é avaliado pelo interpretador. O programa fica registado com o nome import.

Exemplo de ficheiro a importar

Este programa é composto por três expressões, a última das quais é um ciclo. <lisp>

(seq (add 1 2) (sub 1 2) (mul (add 1 2) 3))
(seq (add 1 2) (sub 1 2) (mul (add 1 2) 3) (div 2 5) "olá")
(seq (set ix 0) (while (lt ix 30) (seq (print "ix =" ix) (set ix (add ix 1)))))

</lisp>

Embora o interpretador de expressões tenha de assinalar problemas relativos à interpretação de expressões mal especificadas, assume-se que não existem entradas mal-formadas nestes ficheiros (embora tenham de ser detectadas).

Considerações sobre Flexibilidade e Eficiência

Devem ser possíveis extensões ou alterações de funcionalidade com impacto mínimo no código já produzido para a aplicação. O objectivo é aumentar a flexibilidade da aplicação relativamente ao suporte de novas funções. Em particular, a solução encontrada para salvaguardar textualmente o conteúdo do documento deve ser suficientemente flexível de modo a permitir visualizar o conteúdo de um documento noutro formato (por exemplo, XML) sem que isso implique alterações no código core da aplicação.

Execução dos Programas e Testes Automáticos

Usando os ficheiros test.import, test.in e test.out, é possível verificar automaticamente o resultado correcto do programa. Note-se que é necessária a definição apropriada da variável CLASSPATH (ou da opção equivalente -cp do comando java), para localizar as classes do programa, incluindo a que contém o método correspondente ao ponto de entrada da aplicação (mmt.app.App.main). As propriedades são tratadas automaticamente pelo código de apoio.

       java -Dimport=test.import -Din=test.in -Dout=test.outhyp mmt.app.App

Assumindo que aqueles ficheiros estão no directório onde é dado o comando de execução, o programa produz o ficheiro de saída test.outhyp. Em caso de sucesso, os ficheiros das saídas esperada (test.out) e obtida (test.outhyp) devem ser iguais. A comparação pode ser feita com o comando:

        diff -b test.out test.outhyp

Este comando não deve produzir qualquer resultado quando os ficheiros são iguais. Note-se, contudo, que este teste não garante o correcto funcionamento do código desenvolvido, apenas verificando alguns aspectos da sua funcionalidade.