CENTRO DE TECNOLOGIA
DEPARTAMENTO DE COMPUTAÇÃO E AUTOMAÇÃO ENGENHARIA DE COMPUTAÇÃO
MATHEUS ESTEVAM DE CARVALHO PESSOA
C-SYNCKER – DESENVOLVIMENTO DE UM ASSISTENTE COMPUTACIONAL PARA GERAÇÃO DE RESULTADOS PRÉ-COMPOSICIONAIS
NATAL – RN 2019
C-SYNCKER – DESENVOLVIMENTO DE UM ASSISTENTE COMPUTACIONAL PARA GERAÇÃO DE RESULTADOS PRÉ-COMPOSICIONAIS
Trabalho apresentado à banca examinadora da Universidade Federal do Rio Grande do Norte, como requisito parcial para a obtenção do título de bacharel em Engenharia de Computação, sob a orientação do professor Dr. Alexandre Reche e Silva.
NATAL – RN 2019
C-SYNCKER – DESENVOLVIMENTO DE UM ASSISTENTE COMPUTACIONAL PARA GERAÇÃO DE RESULTADOS PRÉ-COMPOSICIONAIS
Trabalho apresentado à banca examinadora da Universidade Federal do Rio Grande do Norte, como requisito parcial para a obtenção do título de bacharel em Engenharia de Computação, sob a orientação do professor Dr. Alexandre Reche e Silva.
Aprovada em: ___/___/____
BANCA EXAMINADORA
_______________________________________________________ PROF. DR. ALEXANDRE RECHE E SILVA
UFRN ORIENTADOR
_______________________________________________________ PROF. DR. FRANCISCO JOSÉ TARGINO VIDAL
UFRN
MEMBRO DA BANCA
________________________________________________________ Me. AGAMENON CLEMENTE DE MORAIS JUNIOR
UFRN
C-SYNCKER : desenvolvimento de um assistente computacional para geração de resultados pré-composicionais / Matheus Estevam de Carvalho Pessoa. - 2019.
72 f.: il.
Monografia (graduação) - Universidade Federal do Rio Grande do Norte, Centro de Tecnologia, Curso de Engenharia de Computação. Natal, RN, 2019.
Orientador: Prof. Dr. Alexandre Reche e Silva.
1. Engenharia de Software - Monografia. 2. C-Sycnker -
Monografia. 3. Arquitetura MVC - Monografia. 4. Sistema Schillinger - Composição Musical - Monografia. I. Silva, Alexandre Reche e. II. Título.
RN/UF/BCZM CDU 004.41
fui agraciado desde meus primeiros anos.
As minhas irmãs, Carol e Lígia, por serem minhas lanternas quando estou afogado.
A Camila, com quem aprendi o valor do esforço e da dedicação e pelo carinho e torcida.
A Thiago, porque rir é sempre o melhor remédio.
A Alexandre, pelas orientações, músicas e ensinamentos e a Agamenon, por todas as tardes de conversas e traduções que tanto contribuíram para este trabalho. A Vidal, pelas orientações e trocas de conhecimento.
Aos amigos que torceram e me deram forças para seguir no caminho certo. A Deus, pois, mesmo não sendo o mais devoto, guiou-me através das dificuldades.
aborda a composição musical através de qualidades musicais que apresentam quantidades, i.e., valores numéricos que podem ser combinados para gerar novos resultados. Essa forma de representação possibilita a criação de procedimentos computacionais que automatizam a tarefa de criar as combinações através de operações específicas, permitindo aos músicos uma maior agilidade e flexibilidade na tarefa de composição. O objetivo deste trabalho foi desenvolver o C-Syncker, uma aplicação baseada em uma arquitetura extensível, boas práticas de programação e padrões de projeto, que auxilia na composição musical, através das técnicas propostas no SSCM, gerando resultados visuais no formato de partitura ou audíveis como arquivos de áudio sintetizados. Realizou-se então um estudo sobre engenharia de software baseado em Bezerra (2015), para entender as etapas de desenvolvimento, de Buschmann (et al, 1996), para uso do MVC, e Gamma (et al, 2000), para aplicação dos padrões de projeto. Diante disso, obteve-se uma aplicação que gera resultados pré-composicionais. Foi possível constatar que usar o MVC junto com padrões de projeto aumenta a reutilização de código, melhora sua legibilidade e permite criar um sistema que pode ser mantido e legado à outros programadores para melhorá-lo.
Palavras-chave: Sistema Schillinger de Composição Musical; Arquitetura MVC; Engenharia de Software; Padrões de Projeto; C-Syncker.
According to Silva (2016), the Schillinger System of Musical Composition (SSCM) (2004) approaches the musical composition through musical qualities that presents quantities, i.e. numerical values that can be combined to generate new results. This representation enables the creation of computational procedures that automate the task of creating the combinations through specific operations, allowing the musicians greater agility and flexibility in the composition task. The purpose of this work was to develop the C-Syncker, an application based on an extensible architecture, good programming practices and design patterns, that assists in musical composition through the techniques proposed in the SSCM, generating visual results as sheet music or audible format as synthesized audio files. A study on software engineering based on Bezerra (2015) was carried out to understand the developmental stages, of Buschmann et al. (1996) for the use of MVC and Gamma et al. for design patterns. Therefore, an application was obtained that generates pre-compositional results. It was found that using MVC along with design patterns increases code reuse, improves readability, and enables to create a system that can be maintained and bequeathed to other programmers to improve it.
Keywords: Schillinger System of Musical Composition; MVC Architecture; Software Engeneering; Design Patterns; C-Syncker.
Figura 2 - Resultado da associação entre alturas e durações...19
Figura 3 - Cálculo da sincronização de dois geradores de pulsos...20
Figura 4 - Resultado gráfico da resultante de interferência...20
Figura 5 - Diagrama de Casos de Uso do C-Syncker...30
Figura 6 - Classes pertencentes ao pacote Model...31
Figura 7 - Classes que integram a camada de Serviços...32
Figura 8 - Relação entre os controladores e suas visões...34
Figura 9 - Diagrama de classes das visões do C-Syncker...35
Figura 10 - Opções do MenuGeral...35
Figura 11 - Diagrama Observador...37
Figura 12 - O padrão Observador usado para notificar eventos...37
Figura 13 - Padrão Estratégia...38
Figura 14 - Padrão Strategy aplicado aos controladores...39
Figura 15 - Estrutura de Menus...39
Figura 16 - Estrutura de diretórios do MVC...41
Figura 17 - Classe Nota...42
Figura 18 - Classe NotaDAO e seus métodos...43
Figura 19 - Classe Modelo...43
Figura 20 - Classe Visao...44
Figura 21 - Classe Menu...45
Figura 22 - Classe MenuGeral...46
Figura 23 - Classe MenuDados...46
Figura 24 - Classe MenuOperacoes...47
Figura 25 - Classe MenuResultados...48
Figura 26 - Classe Controlador...49
Figura 27 - Classe ControladorDoMenuGeral...50
Figura 28 - Classe ControladorDeDados...51
Figura 29 - Classe ControladorDeOperacoes...51
Figura 30 - Classe ControladorDeResultados...52
Figura 31 - Classe NotaService...53
Figura 32 - Classe ProcessadorDeDadosService...54
Figura 33 - Classe ProcessadorDeOperacoesService...54
Figura 34 - Classe da operação de sincronizar...55
Figura 35 - Classe da operação de desdobrar...55
Figura 36 - Classe ProcessadorDeResultadosService...55
Figura 37 - Interface Observador...56
Figura 38 - Interface Evento...56
Figura 39 - Classe que implementa a estratégia de decisão do Controlador...57
Tabela 1 - Relação de equivalência da notação musical...17
Tabela 2 - Operações descritas por Silva (2016)...20
Tabela 3 - Opções do MenuDeDados...35
Tabela 4 - Opções do MenuDeOperacoes...35
1.1 Justificativa...13
1.2 Objetivos...14
1.3 Metodologia...14
1.4 Organização do Trabalho...15
2. Fundamentação teórica...16
2.1 Propriedades do som, do conceito de nota e o SSCM...16
2.1.1 Propriedades do som...16 2.1.1.1 Altura...16 2.1.1.2 Duração...16 2.1.1.3 Intensidade...16 2.1.1.4 Timbre...17 2.1.2 Do conceito de nota...18
2.1.3 Sistema Schillinger de Composição Musical (SSCM)...19
2.2 Engenharia de Software...21 2.2.1 Análise de requisitos...23 2.2.2 Especificação...23 2.2.3 Arquitetura de software...23 2.2.4 Implementação...24 2.2.5 Teste...27 2.2.6 Documentação...27 2.2.7 Manutenção...27 3. Descrição da implementação...28 3.1 Análise de requisitos...28
3.1.1 Entrevistas com o especialista...28
3.1.2 Problemas de implementação...29
3.1.2.1 Princípios de programação...29
3.2 Especificação...29
3.2.1 Diagrama de casos de uso...29
3.3 Arquitetura de software...30
3.3.1 Padrão de Arquitetura Modelo-Visão-Controlador (Model-View-Controller) (MVC) ... 30
3.3.2 Padrões de Projeto...36
3.3.2.1 O padrão Observador (Observer)...36
3.3.2.2 O padrão Estratégia (Strategy)...38
3.3.2.3 O padrão Composto (Composite)...39
3.4 Implementação...40
3.4.1 Primeira versão...40
3.4.2 Padrão de Arquitetura MVC...40
3.4.3 Padrões de Projeto...56
3.4.3.1 O padrão Observador (Observer)...56
3.4.3.2 O padrão Estratégia (Strategy)...57
3.4.3.3 O padrão Composto (Composite)...58
3.5 Documentação...58
5. Referências... 61
Apêndice A – Requisitos funcionais, diagrama de casos de uso e de classes...63
Apêndice B – Algoritmo de sincronização de listas...68
1. Introdução
Segundo Silva (2016), o Sistema Schillinger de Composição Musical (SSCM) (1946) aborda a composição musical através de qualidades musicais que apresentam quantidades, i.e., valores numéricos que podem ser combinados para gerar novos resultados. Essa forma de representação possibilita a criação de procedimentos computacionais que automatizam a tarefa de criar as combinações através de operações específicas, permitindo aos músicos uma maior agilidade e flexibilidade na tarefa de composição.
Assim, usando como base a composição através do Sistema Schillinger, foi desenvolvido um assistente computacional, o C-Syncker. Ele foi escrito na linguagem C++ e foi projetado para auxiliar estudantes de Música e até músicos profissionais a compor com base nos métodos descritos por Schillinger.
Já existem alguns softwares no mercado que podem resolver alguns dos problemas de Composição, como é o caso do Symbolic Composer, do OpenMusic e do Opus Modus. Esses programas, no entanto, são proprietários. Localizando esforços no contexto acadêmico, são citados trabalhos similares na implementação de técnicas oriundas do Sistema Schilllinger como RANKIN (2012), JONES (2011) e DEGAZIO (2004 e 1998).
Uma outra abordagem possível é o uso de programas que solucionam problemas pontuais, como geração de partituras, criação e execução de MIDI (Musical Instrument Digital Interface) entre outros que podem ser combinados para gerar os resultados desejados.
Mesmo com as soluções existentes para composição, um software livre capaz de realizar as operações como propostas por Schillinger ainda era necessário. Assim, este trabalho apresenta uma versão de uma calculadora de operações composicionais, o C-Syncker.
A proposta de desenvolvimento do C-Syncker foi feita a partir de um projeto de pesquisa desenvolvido na Escola de Música da Universidade Federal do Rio Grande do Norte, UFRN.
Um assistente com características semelhantes, o PD-Syncker, já existe. Ele foi escrito em Pure Data, uma linguagem visual criada para processamento de sinais (PUCKETTE, 2019). A versão usada para a construção do software, PD-Extended, perdeu seu suporte pela comunidade, o que inviabilizou a continuação daquele.
Um outro assistente existente é o J-Syncker, escrito em Java. O J-Syncker (SILVA, et al, 2013) implementa as técnicas estudadas no SSCM. Possui uma interface gráfica para manipulação dos dados e permite exportar seus resultados para o Musescore e para arquivos MIDI.
1.1 Justificativa
Tendo em vista as limitações do J-Syncker, como uma sequência única para inserção de dados no programa, falta de flexibilidade para adição de funcionalidades
ferramenta, o C-Syncker, que auxilie a composição musical para músicos estudantes e profissionais.
O novo software desenvolvido, o C-Syncker, foi pensado para suportar mudanças e incorporações de novas funcionalidades. Assim, o sistema pode crescer para atender necessidades que surjam, sem comprometer seu funcionamento.
Considerando então a incorporação de novas funcionalidades no C-Syncker, foi necessário projetar uma arquitetura que permita a extensão sem que as partes existentes deixem de funcionar.
O software C-Syncker, portanto, foi construído para ser capaz de facilitar o trabalho de composição através de suas operações, permitindo que o usuário mantenha o foco em decisões de mais alto nível.
1.2 Objetivos
O objetivo deste trabalho foi desenvolver uma aplicação baseada em uma arquitetura extensível, boas práticas de programação e padrões de projeto, que possa auxiliar na composição musical, através das técnicas propostas no SSCM, gerando resultados visuais no formato de partitura ou audíveis como arquivos de áudio sintetizados.
Para permitir o desenvolvimento de uma ferramenta que auxilie na composição, é necessário modelar as quantidades das qualidades musicais descritas por Schillinger, de forma que sejam representadas computacionalmente. Assim, essas quantidades podem ser manipuladas dentro do programa, gerando os resultados pré-composicionais.
Como o C-Syncker deve ser capaz de suportar novas funcionalidades e de se comunicar com outras ferramentas existentes, foi preciso pensar em uma arquitetura de sistema que permita alterações sem que as partes já concebidas parem de funcionar corretamente.
1.3 Metodologia
Para a implementação do C-Syncker foram usadas metodologias de desenvolvimento de software como descrito por Bezerra (2015) que trata da modelagem de sistemas usando UML (Unified Modeling Language) e as etapas modernas do desenvolvimento como análise de requisitos, especificação, arquitetura de software, implementação, teste, documentação e manutenção.
O modelo arquitetural proposto para o sistema se baseia no MVC, descrito por Buschmann (et al, 1996) em seu livro. A esse modelo é adicionada uma camada de serviços, que melhora ainda mais a separação de responsabilidades entre os componentes da aplicação.
O C-Syncker foi construído baseado em boas práticas de programação e aplicação de padrões de projeto para permitir maior reuso de código e manutenibilidade. Os padrões utilizados foram o Observador (Observer), Estratégia
(Strategy) e Composto (Composite), que foram descritos por Gamma (et al, 2000) em sua obra, uma referência clássica da área de desenvolvimento.
Como complemento aos padrões de projeto, princípios de desenvolvimento descritos por Aniche (2015) foram usados para desenvolver códigos mais simples e coesos que não acumulam tantas responsabilidades em um único lugar.
Os esforços iniciais para o desenvolvimento do C-Syncker foram aplicados em estudos sobre música e o SSCM para que fosse possível entender como a aplicação seria desenvolvida.
Após o entendimento, foi iniciada a etapa de análise de requisitos. A etapa de codificação foi iniciada, gerando alguns protótipos para testes de funcionalidades. O código foi reunido em um conjunto de classes que, em um primeiro momento, não haviam sido projetadas corretamente, o que levou a um grande código com muitas inconsistências e instabilidades.
Percebeu-se então que a aplicação de uma arquitetura e de padrões de projeto eram necessários para construir uma aplicação que pudesse ser desenvolvida por diversas pessoas. Assim, boa parte do tempo de desenvolvimento do trabalho foi dedicado a estudar engenharia de software, arquitetura de sistemas e padrões de projeto.
1.4 Organização do Trabalho
Este trabalho está organizado da seguinte forma: o capítulo dois faz uma revisão de literatura sobre trabalhos que se relacionam com os estudos e resultados obtidos na presente pesquisa, abordando os principais conceitos usados, os autores e as teorias que suportam o desenvolvimento do C-Syncker.
A implementação da solução é descrita no capítulo três, bem como a estruturação da arquitetura do sistema suportado pelos padrões de projetos. Nesse capítulo são mostrados os diagramas UML que modelam o software.
Nas considerações finais são discutidos os resultados obtidos, novas linhas de trabalho são propostas e um apanhado geral sobre o trabalho, relatando as principais dificuldades encontradas e as limitações do projeto.
2. Fundamentação teórica
2.1 Propriedades do som, do conceito de nota e o SSCM
Nessa seção, serão abordados os principais conceitos da Música usados neste trabalho. Os termos descritos buscam esclarecer e permitir ao leitor a compreensão do que foi feito, mantendo a simplicidade e objetividade.
2.1.1 Propriedades do som
De acordo com Silva (2016), o som é percebido através de algumas características chamadas qualidades musicais. São elas altura, intensidade, duração e timbre. Esses atributos do som podem variar ao longo do tempo.
2.1.1.1 Altura
Segundo Lippmann (2011), é necessário desambiguar os conceitos de altura e intensidade, sendo este relacionado com a amplitude da onda, som mais alto ou mais baixo, e aquele relativo à frequência.
De acordo com Ibañez (2010, p. 51), “[…] altura é determinada pela frequência de uma onda […] dentro de determinado referencial em um intervalo de tempo.”. Assim, a altura pode ser variada mudando a frequência de emissão da onda, tornando o som mais agudo ou mais grave.
Lippmann (2011) afirma que a existência de notas musicais é possível graças a as variações nas frequências de emissão da onda sonora, definindo que as notas Dó, Ré, Mi, Fá, Sol, Lá e Si são formas de nomear frequências específicas.
2.1.1.2 Duração
O conceito de duração, de acordo com Silva (2016), traz diversas informações como a velocidade da execução da música, tamanho dos ataques, o agrupamento das batidas em compassos e o ritmo das seções. Além disso, a duração também trata do tempo que dura a execução de um som.
Para Schurmann (1990, p. 45), “é no tempo que se localizam as durações sonoras e que se efetuam as associações rítmicas; são as entidades rítmicas que de fato se caracterizam por uma estrutura temporal”.
2.1.1.3 Intensidade
De acordo com Lippmann (2011, p. 30), o conceito de intensidade sonora pode ser descrita pela seguinte definição
A intensidade do som resulta da força das vibrações, da amplitude de uma onda sonora. Quanto maior for a energia para gerar um som, maior será a amplitude (tamanho) da onda sonora e mais intensidade terá esse som.
dissemos, reconhecemos se a ação que produz o som está sendo realizada de maneira tranqüila ou abrupta [...]”.
Silva (2016), trata das funções da intensidade no som de maneira mais direta, ao especificar que ela permite a dinâmica na execução, caracterizada pelas mudanças de intensidade; localização dos silêncios, quando a amplitude do som é nula; e, por fim, permite diferenciar eventos, ao evidenciar os que devem ser percebidos naquele momento da execução.
2.1.1.4 Timbre
Enquanto a altura trata das frequências emitidas, a duração do intervalo entre som e silêncio e a intensidade sobre o volume sonoro, o timbre permite distinguir qual a fonte que emite o som (IBAÑEZ, 2010).
O autor define ainda que
[...] essa distinção de timbres entre dois sons pode ser percebida devido à variação na amplitude da frequência e dos harmônicos, resultando numa onda não mais senoidal, mas sim uma onda irregular cheia de cristas e vales (IBAÑEZ, 2010, p. 54).
Segundo Silva (2016), o timbre traz ainda a instrumentação, que é a possibilidade de combinar diferentes instrumentos, e a textura, que trata do papel de vozes (instrumentos) que estão soando ao mesmo tempo, gerando as misturas sonoras.
Resumindo, Silva (2016, p. 59) usa as propriedades do som como ramos estruturantes de um mapeamento (heurístico) das chamadas “qualidades musicais”. Na figura 1 são mostradas as qualidades musicais.
Figura 1 - Qualidades musicais
2.1.2 Do conceito de nota
As notas são nomeadas com as alturas e, por muito tempo, essa foi a única qualidade musical escrita para representar notas1 (MED, 1996).
É importante ressaltar que a nota musical é, na verdade, um aglomerado de qualidades musicais. Durante a execução de uma nota estão presentes, além da altura, duração, intensidade e outras características sonoras (IBAÑEZ, 2010).
Uma nota possui atributos. (Os atributos representam as qualidades musicais, como altura, oitava, acidente, duração, intensidade etc. Ver seção 3.4.2) As notas da melodia variam no tocante a seus atributos.
A variação do atributo altura pode ser representada através de uma lista de letras (cifras), por exemplo (c,d,e,g,g,e,f,f). A variação do atributo duração pode ser representada através de uma lista numérica. Os valores desta lista representam as durações comumente grafadas como figuras musicais. Atribuindo-se 1 à menor figura de um trecho musical (por exemplo, semicolcheia igual a 1), produz-se a seguinte equivalência mostrada na Tabela 1.
Tabela 1 - Relação de equivalência da notação musical
Figura musical Nome Duração
Mínima 8
Semínima 4
Colcheia 2
Semicolcheia 1
Fonte: autoria própria.
Associando-se a listas de durações (2,2,4,4,4,4,4,8) com a lista de alturas (c,d,e,g,g,e,f,f) obtém-se o resultado mostrado na figura 2. Como será mostrado adiante (ver seção 2.1.3), uma lista resultante pode ser representada por (c2,e2,d4,g4,g4,e4,f4,f8).
1 As alturas são representadas por sete nomes: Dó, Ré, Mi, Fá, Sol, Lá e Si. Posteriormente elas receberam cifragem na forma de letras: c, d, e, f, g, a, b. (Costuma-se usar caixa baixa para notas e caixa alta para representar cifragem de acordes).
Figura 2 - Resultado da associação entre alturas e durações
Fonte: Autoria própria
Uma das contribuições do enfoque dado no SSCM é tratar da mesma forma os diversos atributos (qualidades) de uma nota. Uma melodia é composta de várias notas. Os valores (quantidades) dos atributos variam ao longo da melodia e essa variação pode ser representada através de listas. Posteriormente, uma série de operações2 podem ser empregadas sobre essas listas, produzindo resultados para
uso em uma composição musical.
2.1.3 Sistema Schillinger de Composição Musical (SSCM)
Joseph Schillinger foi um estudioso da Música responsável por desenvolver um sistema próprio de composição musical. Seu trabalho foi apresentado em dois volumes que contém 12 livros (SILVA, 2010).
O Sistema Schillinger de Composição Musical, SSCM, possui um primeiro conjunto de técnicas de composição que são descritas no Livro I – Teoria do Ritmo. Essas técnicas são usadas em suas demais obras, juntamente com outras que tratam dos elementos musicais como harmonia e melodia para a construção de peças (SILVA, 2010).
A ideia base da Teoria do Ritmo é a de sobreposição de pulsos que é chamada de resultante de interferência (MORAIS, 2015). Segundo o autor, a aplicação desta e de outras técnicas sobre as quantidades musicais permitem a criação de resultados pré-composicionais.
Em sua obra, Schillinger descreve o processo de resultante de interferência entre dois geradores de pulsos com frequência constante como o resultado do mínimo múltiplo comum das frequências dos pulsos, dividido por cada uma das frequências (SILVA, 2010).
Em seu trabalho, Silva (2010) apresenta um exemplo prático da resultante de interferência entre dois geradores que pulsam com 2 e 3 unidades de frequência. O exemplo é reproduzido na figura 3.
2 Essas operações são, em maior ou menor grau, correlatas às várias técnicas clássicas de composição musical, como os assim chamados dispositivos contrapontísticos: aumentação, diminuição, retrogradação, inversão etc. Ver Silva (2016, p. 64).
geradores de pulsos.
Fonte: Silva (2010)
Após descobrir o menor divisor comum das duas frequências, divide-se esse resultado por cada uma das frequências, gerando assim o número de vezes que o pulso irá se repetir para que o resultado caiba dentro de um intervalo. Cada pulso significa uma batida no ritmo. (SILVA, 2010). Na figura abaixo é mostrada a resultante de interferência graficamente3.
Figura 4 - Resultado gráfico da resultante de interferência
Fonte: Silva (2010)
Em seu trabalho, Silva (2016) elenca algumas dessas operações que podem ser usadas em conjunto com a resultante de interferência para produzir resultados. A tabela 2 traz as operações descritas pelo autor.
Tabela 2 - Operações descritas por Silva (2016)
Acumulação Interseção Ordenação
Apensação Máximo Partição
Busca MDC-MMC Permutação
Combinação Mínimo Potência
Derivada Modo Reversão
Divisão Módulo Rotação
Embaralhamento Multiplicação Soma Fatoração Operações de deslocamento Subtração
Fonte: Silva (2016. Tradução e ordenação alfabética nossa.)
Silva (2016) também define uma representação para um conjunto de qualidades musicais na forma de lista ou uma sequência de valores separados por vírgula. As operações presentes na tabela 2 bem como a resultante de interferência podem e são aplicadas sobre as listas de quantidades oriundas de qualidades musicais.
Com a representação dos dados (qualidades e suas quantidades) e as operações propostas por Silva, o autor sugere que tal procedimento de composição pode ser representado de forma computacional, facilitando a geração do que chama de resultados pré-composicionais.
2.2 Engenharia de Software
O processo de desenvolvimento descrito a seguir foi baseado em Bezerra (2016) e foi usado na construção do C-Syncker.
De acordo com Bezerra (2016), é de grande importância realizar o projeto do sistema antes de começar a construí-lo. Ele faz uma comparação com a construção civil em que antes de construir um edifício, arquitetos e engenheiros fazem diversas plantas. O autor lista ainda várias razões para criar um modelo.
Dentre as justificativas de Bezerra (2016) para construir modelos, as que foram consideradas mais relevantes são o gerenciamento de complexidade, a comunicação entre as pessoas envolvidas e a redução dos custos de desenvolvimento.
Para gerenciar a complexidade, o autor cita as limitações humanas de lidar com complexidade alta. Para resolver isso, ele diz que “modelos se baseiam em Princípio de Abstração, segundo o qual só as características relevantes à resolução de um problema devem ser consideradas” (BEZERRA, 2016, p. 17). Ou seja, usar um modelo permite que os esforços sejam concentrados em entender o que é mais relevante em um projeto, deixando de lado aspectos menos importantes.
A comunicação entre as pessoas envolvidas na elaboração do sistema também é importante, pois evita erros de entendimento sobre o que se espera do
Assim, o modelo serve também como um difusor de informações entre os envolvidos no projeto, pois, durante sua elaboração é muito comum surgirem dúvidas e novas ideias acerca do sistema. O modelo se torna uma referência do projeto (BEZERRA, 2016).
O terceiro aspecto benéfico da criação de um modelo, de acordo com Bezerra (2016), é a redução dos custos associados ao desenvolvimento. O autor explica que humanos estão sujeitos à cometerem erros que podem ser individuais ou de comunicação. Os modelos permitem que esses erros sejam identificados e corrigidos ainda na fase de modelagem e faz a ressalva de que modelos são mais baratos de construir do que o projeto real.
Para a construção de modelos de software, são usados, principalmente, diagramas gráficos que representam fluxo de ações dentro do sistema, funcionamento lógico das partes entre outros. Bezerra define esse processo de modelagem da seguinte maneira:
A modelagem de sistemas de software consiste na utilização de notações gráficas e textuais com o objetivo de construir modelos que representam as partes essenciais de um sistema, considerando-se várias perspectivas diferentes e complementares (BEZERRA, 2016, p. 17).
Graças aos diagramas desenhados, o desenvolvedor e a equipe obtém uma melhor visão do sistema tanto de suas partes constituintes quanto de seu funcionamento e comunicação.
Para fazer a modelagem de sistemas é usada a linguagem UML (Unified Modeling Language). O modelo construído independe da linguagem que será usada para construir o sistema. Bezerra (2016, p. 26) define UML como “[…] uma linguagem visual para modelar sistemas orientados a objetos. […] Por meio dos elementos gráficos da linguagem é possível construir diagramas que representam diversas perspectivas do sistema”. Cada elemento da UML possui uma sintaxe, que diz como o elemento deve ser desenhado, e uma semântica, que esclarece o significado do símbolo e quando ele deve ser utilizado.
Desenvolver software é um tarefa complexa. Isso também se reflete no grande número de projetos de software que não chegam ao fim ou que desperdiçam tempo e dinheiro. Para reduzir esses riscos é necessário definir um processo de desenvolvimento de software que classifique quais são as atividades a serem executadas; quais os prazos, responsáveis e o modo de executar; métricas para analisar o progresso do projeto e uma padronização do desenvolvimento (BEZERRA, 2016, p. 30). Existem diversos processos de desenvolvimento que diferem no modo de organizar suas atividades de construção de sistemas. Ainda assim, é possível identificar atividades que são comuns a todos os processos.
As etapas do desenvolvimento de software trazidas por Bezerra (2016) são descritas nos próximo tópicos. Essa contextualização é necessária para entender quais são as atividades que compõem um processo de desenvolvimento e de que maneira elas auxiliam na construção do sistema.
2.2.1 Análise de requisitos
Durante essa etapa do processo, busca-se entender o problema que será resolvido. É nela em que se conversa com o cliente, criam-se os primeiros diagramas representando as ações possíveis no sistema e em que o especialista auxilia na compreensão do domínio de trabalho.
Segundo Bezerra (2016), às necessidades do cliente são chamadas de requisitos. O resultado do levantamento dessas necessidades, de acordo com o autor, é o chamado documento de requisitos, cujas principais seções são os requisitos funcionais, que definem funcionalidades, os não funcionais, que tratam de qualidades do sistema, e os requisitos normativos, que trazem as restrições do sistema.
Já Eckel (2000), em sua etapa “o que estamos fazendo”, sugere algumas perguntas gerais como “quem irá usar o sistema;?”, “o que o sistema faz?” e “como o sistema faz?” para que o desenvolvedor comece a entender o problema.
Bezerra (2016) define a análise de requisitos como a etapa mais importante do processo de desenvolvimento, pois trata do entendimento sobre o que o sistema deve fazer para satisfazer as necessidades do cliente. Essa etapa funciona como a bússola que guia o desenvolvimento do sistema durante toda sua duração.
2.2.2 Especificação
Especificação é a etapa em que, a partir dos requisitos elicitados durante as entrevistas e validações, os modelos do sistema são construídos. Bezerra (2016) descreve a atividade de especificação como a análise das partes do sistema e suas interações, desconsiderando detalhes de como o projeto será feito.
A modelagem de software, comumente, usa diagramas de caso de uso para a representação das funcionalidades do sistema. Sommerville (2011) afirma que os diagramas de caso de uso dão uma visão simples das interações entre usuário e sistema.
Eckel (2000) justifica que o uso de diagramas de caso de uso na modelagem de sistemas, evita que o desenvolvedor se prenda a detalhes de implementação, que podem acabar atrasando o projeto.
2.2.3 Arquitetura de software
A terceira etapa do processo de desenvolvimento de software é o planejamento e a definição de uma arquitetura. Bezerra (2016) divide esse passo em duas partes, uma que chama projeto de arquitetura e outra de projeto detalhado.
Neste trabalho, a parte mais importante é o projeto detalhado, pois é nele em que se definem comunicações entre objetos, interface com o usuário entre outras partes. O diagrama de casos de uso, criado nas etapas anteriores, dá origem aqui ao diagrama de classes e, a partir deste é projetada a arquitetura do sistema.
como um sistema deve ser organizado e com a estrutura geral desse sistema. […] É o elo crítico entre o projeto e a engenharia de requisitos, pois identifica os principais componentes estruturais de um sistema e os relacionamentos entre eles (SOMMERVILLE, 2011, p. 103).
O padrão de arquitetura utilizado pelo C-Syncker é o Modelo-Visão-Controlador (Model-View-Controller) (MVC). Esse padrão é indicado quando o software possui uma interface com o usuário, pois elas são muito suscetíveis à mudanças (BUSCHMANN ET AL, 1996).
O MVC é dividido em três partes. Modelo (Model) define as entidades que modelam o sistema. Visão (View) reúne os elementos responsáveis por exibir os dados processados para o usuário. Controlador (Controller) interpreta as ações do usuário no programa, entrega as informações para elementos da Visão e faz requisições ao banco de dados.
No padrão MVC, todas as classes que definem os dados do sistema, portanto, que modelam o sistema, são chamadas de domínio e pertencem ao Modelo. Essas classes são as entidades que são salvas em um banco de dados (BUSCHMANN ET AL, 1996).
As classes controladoras agem como acopladores entre as visões e os modelos. Isso não permite acesso às entidades de dados partindo das visões e nem que a camada de negócios receba requisições diretamente. Um dos ganhos dessa abordagem é isolar erros que possam acontecer, facilitando a correção por saber quem é o responsável pela função que falhou.
Os controladores não possuem lógicas de processamento de dados, devendo apenas receber entradas, entregar as informações pedidas pelo usuário ou acessar o serviço responsável por realizar algum processamento requerido (BUSCHMANN ET AL, 1996).
Por fim, o padrão MVC define um conjunto de classes responsáveis por apresentar dados ao usuário e ler informações dele. A esse conjunto se dá o nome de visão (view) (BUSCHMANN ET AL, 1996). As visões representam a interface (gráfica ou não) com o usuário e não devem conter lógica de negócio como acesso a banco de dados ou conversão de valores entre outros. Sua única responsabilidade deve ser a de renderizar o que for enviado por sua controladora.
2.2.4 Implementação
Após a modelagem do sistema seguindo a arquitetura definida, juntamente com a criação dos casos de uso, a etapa de codificação do software é iniciada. Bezerra (2016, p. 36), descreve essa etapa como “[…] a tradução da descrição computacional obtida na fase de projeto em código executável mediante o uso de uma ou mais linguagens de programação”.
O C-Syncker já possuía código funcional antes das etapas do processo de desenvolvimento serem formalmente estudadas e aplicadas. Assim a primeira parte da implementação foi a adequação do código à arquitetura MVC. Após isso foram
aplicados padrões de projeto.
O padrão de arquitetura MVC utiliza três padrões de projeto em sua forma clássica, podendo adicionar outros para complementar funcionalidades. Os utilizados são o Observador (Observer), Estratégia (Strategy) e Composto (Composite). Em conjunto esses três padrões conferem ainda mais flexibilidade, menos acoplamento entre classes e maior divisão de responsabilidades.
O primeiro padrão de projeto implementado foi o Observador (Observer). De acordo com Gamma et al. (2000) essa estrutura permite criar um sistema de notificações em relacionamentos um-para-muitos entre objetos. Assim, quando um objeto modifica seu estado, todos os demais que dependem dele são avisados dessa mudança. No Observador é definida uma lista com todos os objetos observadores interessados em um determinado assunto. A qualquer momento, um novo observador pode ser adicionado ou removido da lista. Ao receber um dado novo ou modificar uma informação, o assunto deve atualizar todos os membros da lista, mantendo-os sincronizados com os dados mais recentes (FREEMAN ET AL, 2004).
O segundo padrão de projeto utilizado foi a Estratégia (Strategy). Mesmo sendo um dos padrões mais simples, carrega em si toda a poderosa estrutura que o polimorfismo agrega às linguagens orientadas. Aqui é definida uma classe base virtual que define um comportamento de quem outras classes herdam. Assim, o comportamento genérico herdado da interface pode ser especializado pelas classes descendentes. A Estratégia é utilizado quando “muitas classes relacionadas diferem somente em seu comportamento. As estratégias fornecem uma maneira de configurar uma classe com um dentre muitos comportamentos” (GAMMA ET AL, 2000, p. 293).
O terceiro padrão usado foi o Composto (Composite). Essa estrutura permite, de acordo com Gamma et al. (2000, p. 160) “compor objetos em estruturas de árvores para representar hierarquias partes-todo. […] clientes tratam de maneira uniforme objetos individuais e composições de objetos”. O Composto opera em estruturas recursivas como componentes gráficos que possuem outros componentes associados ou diretórios que contém outros diretórios. Quando aplicado, cria uma referência interna para uma classe primitiva que é herdada por todos os elementos de comportamento semelhante. Assim, essas novas classes podem conter qualquer item que seja filho da classe base.
Para realizar acessos ao banco virtual do sistema (ver seção 3.3.4), foi usado o padrão de projeto DAO (acrônimo de Data Access Object). Esse padrão define uma separação entre a lógica de acesso ao banco de dados e as classes que acessam o banco. Essa separação ocorre porque “o DAO implementa a lógica necessária para acessar a fonte de dados” (ORACLE, 2019). Como esse padrão usa uma interface para expor seus métodos e essa interface não se altera, mudar a base de dados (banco relacional, arquivos etc.) não afeta as classes que apenas usam os métodos. Com isso, as classes de serviço não conhecem detalhes da implementação do acesso ao banco.
usada de fato (bancos relacionais, não relacionais etc), (2) o uso de interfaces no lugar de classes concretas (DAO é uma interface que é herdada e implementada) e (3) facilidade de realização de testes unitários para garantir o funcionamento correto da aplicação (SHUBHAM, 2019).
Aplicar padrões de projeto ao código possibilita um maior reuso de código e melhora a manutenibilidade do software, porém, por vezes é necessário que, antes de aplicar um padrão para resolver um problema, as classes estejam bem escritas e suas responsabilidades bem definidas. Em outras palavras é preciso utilizar boas práticas de programação.
Quando o código está bem escrito seguindo princípios de programação, a aplicação de padrões de projeto se torna mais fácil, pois é possível observar se alguma classe pode ter uma interface, se é melhor usar herança ou composição entre outros. Isso mostra que é necessário pensar nas classes e na forma como elas se encaixam no projeto antes de escrever o código (ANICHE, 2015).
Um dos muitos princípios existentes sobre boas práticas é o SOLID4,
acrônimo que reúne boas práticas de programação descritas por Robert C. Martin (2000). Esse princípio versa fortemente sobre o gerenciamento de dependências entre classes, i.e., se preocupa com códigos que dependem de outros códigos para funcionar.
O primeiro princípio é a responsabilidade única (single responsability principle) que as classes devem possuir. De acordo com Aniche (2015), esse fundamento torna a classe mais simples de ser mantida, com um código menor e com maior possibilidade de ser reusada.
O segundo princípio é o aberto-fechado (open-closed principle). Segundo Martin (2000), esse é o mais importante entre os cinco, pois fala sobre a capacidade de poder alterar o comportamento de um módulo (classe) sem que seja necessário alterar o código dele. Essa descrição tem grande importância porque reforça uma grande vantagem dentro da orientação a objetos que é permitir que um mesmo código seja usado de diferentes maneiras.
O terceiro princípio é a substituição de Liskov (Liskov substituition principle). Ele trata das restrições que são impostas pelo mecanismo de herança. As classe derivadas devem respeitar o que foi definido na classe base, sejam essas definições regras para as entradas, métodos ou retornos de métodos (ANICHE, 2015). Martin (2000), afirma que seguir esse princípio garante que a classe derivada pode ser substituída pela classe base se as pré-condições, ou seja, parâmetros de construtor ou operações, daquela não forem mais restritivas que as dessa e as pós-condições, i.e., saídas de métodos herdados, não forem mais fracas.
O quarto princípio é a Segregação de Interfaces (interface segregation principle). Esse princípio se assemelha ao primeiro, que trata de classes. Aqui é
4 Acrônimo para Single Responsibility Principle,Open-Closed, Principle, Liskov Substitutive Principle,
afirmado que interfaces simples são melhores e mais reutilizáveis do que aquelas que possuem muitos métodos (ANICHE, 2015). Esse princípio mostra sua importância quando se usam interfaces para conferir comportamentos específicos. Pode acontecer de um ou mais métodos de uma interface não serem necessários dentro de classes e ela seria obrigada a definir o método da mesma maneira. Isso pode gerar inconsistência no código, quando se espera um comportamento que não está definido para uma classe.
O quinto princípio é a inversão de dependências (dependency inversion principle). Ele é descrito por Martin (2000, p. 12) da seguinte maneira “[…] Cada dependência no projeto deve mirar em uma interface ou uma classe abstrata. Nenhuma dependência deve mirar uma classe concreta”. Este princípio confere reúso e flexibilidade para o código que o utiliza. Quando se depende de interfaces tem-se a segurança de que elas mudam pouco, são enxutas e que obrigam quem as implementa a seguir um contrato. Assim há a garantia de que uma operação que dependa de interface sempre ocorrerá, pois o comportamento é assegurado pelo contrato.
2.2.5 Teste
O objetivo da etapa de testes é garantir que os componentes desenvolvidos na etapa de implementação estejam funcionando o mais próximo possível do comportamento descrito pelos requisitos funcionais do sistema. Assim, garante-se com uma certa margem de segurança, que as próximas partes a serem desenvolvidas serão integradas a um conjunto de funcionalidades que se comportam da maneira esperada, minimizando os erros e entregando um software melhor para o usuário final (BEZERRA, 2016).
2.2.6 Documentação
A fase de documentação ocorre durante todo o processo de desenvolvimento. Isso porque todas as etapas anteriores geram documentação para o sistema (SOMMERVILLE, 2011).
Diagrama de casos de uso, requisitos funcionais, modelo arquitetural, diagrama de classes, o código e os testes realizados agem como guias para o funcionamento do sistema, permitindo que a equipe possa entregar o projeto para outros que poderão mantê-lo, seja adicionando novas funcionalidades ou corrigindo algum erro que não foi notado.
2.2.7
Manutenção
Etapa final do processo de desenvolvimento. Bezerra (2016) descreve essa atividade como a entrega do sistema ao usuário na forma de uma aplicação executável, com manuais de utilização e as configurações de uso realizadas, podendo haver a migração de dados preexistentes de outros sistemas para o software.
3. Descrição da implementação
Neste capítulo serão apresentados os resultados obtidos em cada etapa do desenvolvimento de software. A primeira etapa foi a elicitação de requisitos junto aos interessados no software, feita através de entrevistas e geração de casos de uso e protótipos. Também foram descritas as dificuldades encontradas durante essa etapa. Após isso, os requisitos foram validados junto ao especialista para a geração do diagrama de casos de uso.
A etapa seguinte trata de uma descrição detalhada de como o modelo arquitetural do sistema foi construído baseado em MVC. A etapa seguinte, a implementação, também traz comentários e trechos do código criado, mostrando como os padrões de projeto foram usados e as partes que compõem a arquitetura. As etapas de teste, documentação e manutenção são brevemente comentadas, pois o sistema ainda está em desenvolvimento.
3.1 Análise de requisitos
Neste subtópico serão apresentadas as etapas seguidas durante a fase inicial do desenvolvimento do C-Syncker em que foram elicitados os requisitos funcionais do sistema
3.1.1 Entrevistas com o especialista
A etapa inicial do software foi a definição de suas funcionalidades. Para tanto, foram realizadas entrevistas com o especialista de Música, iniciando a imersão na representação do problema (problem space).
“A problem space is all of the various components that go into creating a resolution for a problem. Think of it like a frame, which acts as something of a border to help define an area. A problem space helps you or, on a larger scale, a business, figure out what the problem is, work through ways to correct them and then drives implementation of the appropriate solution. The ultimate purpose is to take corrective action for some identified problem”5 (HENDRICKS, 2019).
(Ver etapa de implementação, seção 3.3, para maior entendimento sobre a representação do problema para o C-Syncker).
Como resultado, foram produzidos artefatos UML do processo de software que pudessem contribuir na representação do problema. Os documentos produzidos nessa etapa foram a tabela de requisitos funcionais do sistema, o diagrama de caso de uso e o diagrama de classes (que foi sendo aperfeiçoado à medida que se percebiam novas necessidades) e podem ser conferidos no Apêndice A.
5 Uma representação do problema são todos os vários componentes presentes na criação de uma solução para um problema. Pense nisso como um quadro que age como uma fronteira para ajudar a definir uma área. Uma representação de problema ajuda você ou, em uma escala maior, um negócio, a descobrir o que o problema é, trabalhar para solucioná-lo e então entregar a implementação de uma solução apropriada. O objetivo final é tomar medidas corretivas para algum problema identificado. (HENDRICKS, 2019. Tradução nossa.)
3.1.2 Problemas de implementação
No início do projeto, não se pensou em aplicar um processo de desenvolvimento. Assim, pouco depois de obter alguns requisitos funcionais do sistema, a codificação das funcionalidades foi iniciada.
Com o passar dos meses e o crescimento do código, tornou-se muito complexo realizar alterações e adição de novas funcionalidades, pois havia altíssimo nível de dependência entre as classes e repetição de código. Assim, ao mudar um determinado trecho do código, as outras partes que estavam funcionando, paravam. Era necessário modificar em todos os locais que o trecho alterado aparecia.
O código rapidamente aumentou de complexidade e tornou-se impraticável mantê-lo. Com base nisso, foi decidido que um estudo dos processos de desenvolvimento era necessário, bem como a adoção de uma arquitetura.
3.1.2.1 Princípios de programação
Um ponto de partida dos estudos surgiu de experiências e trabalhos externos realizados pelo programador. O entendimento e adoção da arquitetura MVC foi uma decisão acertada, pois permitiu desenvolver conhecimentos úteis para os desafios posteriores do desenvolvimento.
Outra linha seguida em paralelo ao estudo da arquitetura foi o de princípios de programação SOLID (descritos na seção 2.2.4). Esses princípios versam sobre as responsabilidades que devem ser assumidas quando se usa linguagens orientadas a objetos. Eles alertam o desenvolvedor para planejar bem as classes antes de codificá-las, tomando cuidado com implementações que possuem muitas dependências de outros códigos, classes que acumulam responsabilidades entre outros. O entendimento dos princípios SOLID construíram uma base para o entendimento de conceitos mais avançados de programação, os padrões de projeto. Esses são conceitos antigos na área, mas que exigem domínio da orientação a objetos para conseguir usá-los bem.
Os estudos levaram a construção da estrutura atual do C-Syncker. O uso de uma arquitetura MVC, com classes planejadas previamente e a aplicação de padrões de projeto.
3.2 Especificação
Nessa seção são descritos os casos de uso obtidos nas entrevistas. Os casos de uso obtidos geraram a tabela de requisitos funcionais do sistema.
3.2.1 Diagrama de casos de uso
Um diagrama de casos de uso foi criado com o fim de facilitar a compreensão das funções existentes no sistema e possibilitar uma comunicação visual. Nele estão representadas as ações que podem ser realizadas pelo usuário na interação com o software. Na figura 5 é visto o diagrama criado.
Fonte: Autoria própria
Na figura 2 estão representados o usuário e o sistema. O usuário é responsável por enviar e recuperar dados do sistema. O sistema é responsável por processar esses dados e retornar resultados
O diagrama de casos de uso é uma representação das funcionalidades que o sistema oferecerá. As funcionalidades previstas para a versão inicial do software são (1) informa qualidades (musicais), (2) solicitação de operações, (3) visualiza resultados, (4) salva lista na memória e (5) recupera resultados.
3.3 Arquitetura de software
Nessa seção serão apresentados conceitos e os diagramas relativos a arquitetura de software. Os diagramas de classe foram divididos para facilitar a apresentação da estrutura.
3.3.1 Padrão de Arquitetura Modelo-Visão-Controlador
(Model-View-Controller) (MVC)
A arquitetura escolhida para o sistema foi o MVC. Essa escolha é baseada no estudo de livros sobre arquiteturas e padrões (BUSCHMANN e GAMMA,
respectivamente), que justificam essa sendo uma boa escolha quando o sistema apresenta uma interface com o usuário.
Interfaces são elementos que podem mudar bastante e, por isso, precisam de flexibilidade para mudar e de garantia que as partes que funcionam permaneçam assim. O MVC oferece essas qualidades pois é construído a partir da separação entre dados e lógica de negócio, classes de apresentação de dados e estruturas de controle e coordenação de ações.
Para encaixar o código existente na estrutura MVC foi necessário aplicar uma refatoração que permitiu desacoplar, ou seja, separar, as diferentes responsabilidades das classes já codificadas. Depois foram criadas novas estruturas para englobar as diferentes responsabilidades identificadas no desacoplamento. Todo esse trabalho foi feito de forma cautelosa para que uma versão anterior do código não fosse desperdiçado.
O primeiro passo para refatoração foi identificar as partes que compõem o padrão de arquitetura usado.
O Modelo (Model), como descrito na seção 2.2.3, contém as classes de dados do programa, assim como as responsáveis por processamento de dados. No C-Syncker, foi realizada uma segunda separação, criando uma camada de serviços que fica responsável por parte do processamento de dados. Os acessos ao banco ainda ficam no Modelo.
A Visão (View) contém todas as classes responsáveis por apresentar dados. No C-Syncker as informações do usuário são obtidas através de menus textuais, que permitem o usuário selecionar opções.
O Controlador (Controller) contém as classes que entregam dados para as visões apresentarem, interpretam as entradas do usuário e solicitam aos serviços e aos modelos os processamentos necessários para entregar o resultado esperado.
Definida a arquitetura do sistema, criou-se um diagrama de classes do sistema, explicitando as entidades presentes e suas dependências. Dessa forma, foi possível entender qual era o papel de cada classe e como refatorá-la para se encaixar no MVC.
No C-Syncker, a entidade que representa os dados é a nota. Portanto, essa classe pertence ao Modelo da aplicação, pois ela define o domínio do sistema. Na figura 6 é mostrado o diagrama das classes que fazem parte do Modelo.
pertencentes ao pacote Model
Fonte: Autoria própria
A classe Nota contém as qualidades musicais. O programa se estrutura em função desta classe. Ela foi projetada para suportar informações obtidas de diferentes fontes de dados, como entrada do usuário, mapa mental, musicXML, teclado MIDI etc.
A classe BancoEmMemoria armazena as listas informadas pelo usuário, bem como os resultados das operações sobre listas, agindo como um banco de dados apenas em memória, ou seja, que perde suas informações quando o programa é encerrado. Foi decidido que na primeira versão do sistema, não haveria necessidade de incluir um banco de dados físico já que os dados poderiam ser salvos em arquivos6 caso fosse necessário uma persistência.
Para manipular o banco virtual inserindo, alterando ou lendo dados, foi criada a classe NotaDAO que realiza as operações de criar, ler, atualizar e deletar (CRUD7, em Inglês). O acrônimo DAO vem de objeto de acesso a dados (data
access object), um padrão de implementação bastante utilizado em aplicações que fazem acesso a banco de dados.
No modelo arquitetural MVC a lógica de negócio deve estar contida no Modelo. Isso, pode acarretar um alto nível de dependência entre os componentes, dificultando mudanças no software. Assim, as entidades do modelo tiveram suas responsabilidades reduzidas ao criar uma camada de regras de negócio da aplicação chamada de Serviço (Service). A adição de Serviço à arquitetura, não modifica o MVC, podendo ou não ser adicionada de acordo com o que o programador ou analista decidir.
A camada de serviços concentra funcionalidades de acesso ao banco de dados por meio da interface DAO, verificação de informações para não colocar dados errados no banco, leitura e escrita de fontes externas ao software entre outros. Essa concentração de tarefas permite que tanto os modelos quanto os controladores sejam concisos e coerentes com suas funcionalidades. Na figura 7 são mostrados os principais serviços existentes na aplicação desenvolvida.
6 Essa funcionalidade estará presente em versões futuras do sistema.
Figura 7 - Classes que integram a camada de Serviços
Fonte: autoria própria
A classe NotaService auxilia a manipulação do banco de dados através da interface NotaDAO, fazendo também verificações nas informações passadas pelo usuário para garantir sua consistência.
Para permitir a compatibilidade do C-Syncker com o software Freeplane (2019) e foi criada a classe MMSynckerService8, responsável por ler, editar e salvar
dados gerados por esse programa.
A classe ProcessadorDeDadosService é responsável por fazer conversões de dados dentro do programa. Essa funcionalidade é necessária pois os dados vindos da entrada do usuário não estão no formato padrão de nota que o programa usa, sendo necessário realizar conversões.
Para decidir e realizar as operações, a princípio, propostas pelo SSCM, foi implementada a classe ProcessadorDeOperacoesService. Esse Serviço, por conter muitas operações, foi reescrito para reduzir sua complexidade e acúmulo de responsabilidades.
A última classe do diagrama, ProcessadorDeResultadosService, se encarrega de realizar todos os tratamentos necessários para a geração dos resultados composicionais. O programa pode apresentar dois tipos de saídas, uma partitura ou um arquivo MIDI. Essa classe faz uso de ferramentas externas como o Lilypond (2013) para a criação das informações de saída.
A principal vantagem de usar uma camada de serviços é que se houver necessidade de mudar os controladores ou as entidades de domínio, ou mesmo o banco, as regras de negócio permanecem as mesmas, não necessitando alterar a interface definida para se adequar às mudanças feitas.
Para realizar a comunicação entre os dados disponíveis e os menus de acesso, a arquitetura MVC define uma entidade chamada Controlador. Ela se responsabiliza por entender as ações do usuário ao interagir com a interface gráfica e por requisitar ao Modelo os dados ou processamentos necessários para finalizar a ação desejada.
O C-Syncker utiliza quatro controladores, cada um relacionado a um menu
8 MM é a extensão de um arquivo de mapa mental (mind map) gerado pelo software Freeplane usado para representar a estrutura musical em forma de árvore
Figura 8 - Relação entre os controladores e suas visões
Fonte: autoria própria
A classe ControladorDoMenuGeral no topo da figura 8 é iniciada juntamente com a aplicação. É ela que se encarrega de interpretar qual menu o usuário deseja acessar e criar o controlador apropriado para isso. Sua visão, a classe MenuGeral, contém as opções de entrada de dados, operações e geração de resultados.
A classe ControladorDoMenuDeDados possui a função de inicializar a visão MenuDeDados e de entender as ações do usuário. Ela é responsável por pegar os dados após serem informados na tela e usar os métodos dos serviços disponíveis para converter em informação que pode ser usada dentro do programa. Os valores são armazenados no banco virtual após processamento.
A classe ControladorDoMenuDeOperacoes determina quais operações o usuário escolheu através do MenuDeOperacoes e quais as listas que serão usadas no processamento. A informação da operação desejada é passada para o ProcessadorDeOperacoesService que a realiza. Os resultados do processamento são guardados no banco virtual através do uso de NotaService pelo controlador.
A classe ControladorDoMenuDeResultados, se encarrega de interpretar quais são as saídas que o usuário deseja, partitura ou MIDI, e também de repassar para o MenuDeResultados as informações do banco virtual que devem ser exibidas a pedido do usuário.
Para a exibição de dados o padrão MVC define as classes de visão que podem obter dados de entrada do usuário, receber dados de sua controladora ou exibí-los na tela.
O C-Syncker, em sua versão inicial, funciona no modo texto, ou seja, tanto as entradas de dados no programa quanto as saídas são feitas via linha de comando. (Essa foi uma decisão tomada para permitir que o núcleo do sistema estivesse completamente funcional e ganhar tempo para pensar na interface gráfica). Em versões posteriores do sistema também será possível inserir dados através de um arquivo de mapa mental gerado pelo Freeplane (2019).
classe base chamada Visão (View). Essa classe contém métodos que todas as visões devem possuir. Na figura 9 é mostrado o diagrama de classes das visões.
Figura 9 - Diagrama de classes das visões do C-Syncker
Fonte: autoria própria
Os menus mostrados na figura 10 são classes com conjuntos de métodos que mostram textos no terminal, permitindo ao usuário digitar quais dados irá informar, decidir as operações a serem realizadas sobre as informações e selecionar uma saída para os resultados.
Figura 10 - Opções do MenuGeral
Fonte: autoria própria
O MenuGeral é inicializado com o programa e mostra ao usuário quais são as opções dentro do C-Syncker. Na figura 10 são mostradas as opções disponíveis dentro dessa visão. No MenuGeral o usuário pode escolher entre inserir dados no programa, acessar as operações disponíveis ou visualizar resultados e gerar partituras ou MIDI. Cada uma dessas opções, ao ser escolhida, gera uma chamada a um dos controladores associados a essas visões.
A primeira opção do MenuGeral é acessar o MenuDeDados, que permite ao usuário informar as qualidades musicais alturas, durações, intensidades ou informar
menu.
Tabela 3 - Opções do MenuDeDados
1. MenuDeDados
1.1 Informe Qualidade 1.1.1 Alturas
1.2 Carregue Arquivo .mm 1.1.2 Durações 1.1.3 Timbre 1.1.4 Intensidades
Fonte: autoria própria
No MenuDeDados é possível informar uma qualidade musical, escolhendo entre altura, duração, métrica e intensidade. Também é possível carregar um arquivo externo do Freeplane que possua a informação dessas qualidades.
O segundo menu apresenta ao usuário todas as operações disponíveis no C-Syncker. Na tabela 4 são mostradas as opções disponíveis no MenuDeOperacoes.
Tabela 4 - Opções do MenuDeOperacoes
2. MenuDeOperacoes 2.1 Sincronize 2.2 Derive 2.3 Acumule 2.4 Reverta 2.5 Exclua
Fonte: autoria própria
O terceiro menu disponível é o MenuDeResultados. Ele mostra as saídas disponíveis ao usuário, permitindo a visualização na tela das listas que estão salvas na memória, geração de MIDIs ou de partituras. A tabela 5 mostra as opções do menu.
Tabela 5 - Opções do MenuDeResultados
3. MenuDeResultados 3.1 ExibaMemórias 3.2 ToqueMIDI 3.3 MostrePartitura
Fonte: autoria própria
3.3.2 Padrões de Projeto
No programa C-Syncker, os três padrões clássicos Observador (Observer), Estratégia (Strategy) e Composto (Composite) foram usados em conjunto com a arquitetura. A seguir será descrito onde eles aparecem e de que maneira são usados no software.
3.3.2.1 O padrão Observador (Observer)
notificar para os objetos interessados as alterações que aconteceram em uma classe definida como observável. No C-Syncker, a classe NotaDAO precisa ser monitorada para notificar que realizou alterações no banco, seja por inserir novas listas ou alterar listas existentes. Portanto, NotaDAO é do tipo observável. A notificação enviada é usada para atualizar as visões ou os controladores sobre o novo dado alterado no banco, mantendo assim o usuário ciente das informações existentes no banco e das mudanças que ocorrem. Na figura 11 é mostrado o diagrama das classes sob esse padrão.
Figura 11 - Diagrama Observador
Fonte: autoria própria
O Modelo assume duas funções, a de pacote de classes com responsabilidades semelhantes e a de classe base que possui métodos comuns que são herdados por todos os membros que pertencem ao pacote. Na figura 11 também é mostrado que o Modelo possui uma referência a classe virtual Observador, cuja responsabilidade é registrar as classes de controle e de visão interessadas em receber notificações de mudanças de dados. Todas as demais classes filhas do Modelo possuem essa referência por herança.
Além de ser usado como mecanismo de notificação para mudanças no banco de dados, o padrão Observador também foi usado para criar um sistema de eventos entre as visões e os controladores. Sempre que o usuário realizar a ação de escolher um elemento do menu, uma notificação da ação é enviada ao controlador, que decidirá então como manipular os dados informados ou irá executar as operações desejadas. A figura 12 ilustra as classes envolvidas.
Figura 12 - O padrão Observador usado para notificar eventos
Fonte: autoria própria
As visões fazem herança com a classe Visao (View), como mostrado na figura 9. Assim, ao adicionar uma referência à classe virtual Evento dentro da classe mãe, todas as demais terão acesso a essa referência. Já as classes de controladores herdam da classe Evento. Com isso, essas classes podem ser
realiza no programa).
3.3.2.2 O padrão Estratégia (Strategy)
O segundo padrão de projeto utilizado foi a Estratégia. Esse padrão implementa uma interface comum de dados que é usada como objeto genérico para realizar operações. Na figura 13 é mostrado um diagrama com o uso da Estratégia.
Figura 13 - Padrão Estratégia
Fonte: autoria própria
Nesse diagrama estão representadas as operações propostas para o C-Syncker em sua atual versão. Em versões de código anteriores, cada operação era definida dentro de uma única classe, o que gerava um código muito complexo e com muitas responsabilidades em um só lugar. Logo, foi entendido que uma melhor abordagem seria transformar cada operação em um objeto, podendo alternar entre eles em tempo de execução conforme o usuário solicitasse.
Concentrar as criações de objetos em um único local melhora a manutenção e reduz a complexidade do código. Isso evita códigos grandes sujeitos a falhas e necessidade de consertos. A estrutura de transformar cada operação em uma classe exige saber qual o objeto está sendo chamado. Usar estruturas de decisão é muito comum nesses casos.
A classe Operacao é a interface que define o comportamento padrão das operações. Existe uma referência para ela dentro da classe DecidaOperacao. Essa
referência é retornada após DecidaOperacao definir qual operação foi escolhida pela usuário através do MenuDeOperacoes.
O padrão Estratégia também foi aplicado aos controladores, pois é necessário decidir qual controlador deve ser inicializado quando o usuário escolhe uma das opções do programa através de MenuGeral.
Figura 14 - Padrão Strategy aplicado aos controladores
Fonte: autoria própria
A classe DecidaControlador mostrada na figura 14 define uma estratégia para que ControladorDoMenuGeral consiga criar o controlador apropriado para a opção que o usuário informou selecionou no MenuGeral.
3.3.2.3 O padrão Composto (Composite)
O terceiro padrão de projeto utilizado foi o Composto. Esse padrão consegue representar objetos que podem conter estruturas recursivas. Exemplos dessas estruturas são diretórios contendo subdiretórios, menus com submenus etc. O uso desse padrão foi bastante apropriado, uma vez que o C-Syncker possui uma interface baseada em menus textuais que podem conter outros elementos.
Para a implementação do padrão Composto foi criada uma classe Menu que possui estruturas necessárias a um menu que for criado e que serão transmitidas por herança para classes filhas. Essas estruturas são métodos que executam ações como mostrar o menu na tela e ler dados do usuário. Na figura 15 é mostrada a estrutura dos menus dentro da aplicação.
Figura 15 - Estrutura de Menus
Fonte: autoria própria
Cada menu, com exceção da classe Menu, que é virtual, pode conter submenus ou apenas elementos simples. Essa última é a opção usada no C-Syncker, já que não havia necessidade de implementar subníveis, o que também dificultaria a navegação do usuário devido a interface textual.