Upload
rodrigomizael
View
217
Download
0
Embed Size (px)
DESCRIPTION
:Apresentando Model-View-Presenter, o MVC focado na visualização - Java Free
Citation preview
Assine08007033000 SAC Batepapo Email Notcias Esporte Entretenimento Mulher Shopping BUSCAR
2,9milCurtir 131 SIGA@JAVAFREE
Login Registrese
Home>Artigos>EngenhariadeSoftware>
ApresentandoModelViewPresenter,oMVCfocadonavisualizaoPublicadoporTutoriaisAdminem04/10/2012209.339visualizaes
comentrios:1
DanielFernandesMartins
Neste artigo sero abordados os aspectos principais do padroMVP, ouModelViewPresenter. Sero explicados quais problemasmotivaram a criao dessepadro,comoeleresolvetaisproblemase,principalmente,osasvantagensedesvantagensdeseusaroMVP.Parademonstraressesconceitosdeformaprtica,nsdesenvolveremosumaaplicaodeexemploutilizandoSwing.
OmodelodeprogramaoMVPoquepodemoschamardeumaderivaodomodeloMVC,quesurgiucomoSmalltalk.NoSmalltalk,oscomponentesvisuais,comoascaixasdetextoebotes,soregidosportrsabstraescentrais:Model,VieweController.
Model:Soinformaesqueindicamoestadodocomponente,como,porexemplo,otextodeumTextFieldouaindicaoonoffdeumCheckBoxView:AcessaosdadosdoModeleespecificacomoosdadosdoModelsomostradosaousurio,como,porexemplo,umtextodentrodeumTextBoxouumindicandoqueumCheckBoxestmarcadoController:ComponenteparamapearasaesdousurionaView(asquaisocorremnormalmenteatravsdeeventos)efazemcomqueoModelsejamodificado.Paracitarumexemplo,quandoumCheckBox?marcado?recebeumeventodeclick,oControllermapeiaessaaodousurioemodificaoModel,indicandoqueeste agoraestdesmarcado.OModel, por sua vez, notifica aView, indicandomudanaemseuestado.AView recebeanotificaoe renderizaoCheckBoxdesmarcadonatela.AFigura1ilustraainteraoentreoModel,aVieweoController:
Figura1?ModeloMVC
Estemodelodeprogramaofuncionamuitobemnocontextodecomponentesindividuais,comoexplicadoanteriormente.Porm,duranteodesenvolvimento,necessrioagruparvriosdessescomponentessobcontextosespecficosque,juntos,representamodomniodoproblemaasersolucionadopelosistema.
Imagineumajanelaquesirvaparacadastrarclientesnosistema.agrupadoumconjuntodediversostiposdecontrolesvisuais(View)querepresentamumalistadeobjetosClientenosistema(Model).Oresultadodainteraodousuriocomajanelatraduzidoemeventos(Controller),quecontrolamofluxodaaplicaoemodificamoestadodoscontrolesdajanela,atualizandotambmosobjetosdenegcio.
Namaioriadoscasos,ocdigoresponsvelpelotratamentodoseventosecontroledefluxodaJanelaficadentrodeumamesmaclasse.Temosentoumaquebrade camadas: a View no deve conhecer nem o modelo que representa e nem a lgica de apresentao, j que temos alguns tipos de ao na lgica deapresentaoquesoregidaspelodomniodoproblemaemquesto.Outroproblemademanter tantoocdigodemontagemdatelaquantootratamentodeeventosumaclasseextremamentegrande,difcildemanter,difcildeexpandirecomcdigodeimpossvelreutilizao.
aqueentraoMVP!
Motivaes do MVPAdiferenaentreoMVCeoMVPficabasicamentenoconceito,jqueasfunesdo?C?(Controller)doMVCsosemelhantesao?P?(Presenter)doMVP.
Imaginenovamenteoexemplodocadastrodeclientes.OModelacoleodeobjetosClientequesomanipuladospelajanela.AViewajanelapropriamentedita.OPresenterresponsvelporinterceptaroseventosgeradosnaView,comafinalidadedecontrolaralgicadeapresentao.
Portanto, foco principal do MVP separar a lgica de apresentao da apresentao em si. Com isso, conseguimos alternar entre diferentes apresentaesfacilmente,atravsda reutilizaoda lgicadeapresentao.Almdisso, conseguimos realizar testesnaclasse responsvelpela lgicadeapresentaosemprecisarutilizaraViewparaisso.Ganhamostambmnoquesitomanuteno,jqueasresponsabilidadesforamdivididasemmaisclassesespecializadasefceisdeentender.
MVP na Prtica
HOME NOTCIAS ARTIGOS FRUM BUSCA ENVIARNOTCIA CONTRIBUIR
Jqueo?corao?doMVPaseparaodalgicadeapresentaodaapresentaoemsi,temoscomoresultadoumaViewquenocontmmuitocdigoalmnecessrioparaorganizaroscomponentesnatela.Issopareceserocaminhomaiscorreto,poisafunodaViewdevesersomenteoferecerumaformadeousuriointeragircomosistema.NoteaquiumasemelhanacomopadroMVCclssico.
Considereoseguintediagramadeclasses:
Figura2?Estruturadeclassesdoexemplo,seguindooMVP.
Paracriaranossaaplicaodeexemplo,utilizaremoscomobaseodiagramadeclassesmostradonaFigura2.
TodasasViewscapazesdecadastrarclientesdevemimplementarainterfaceCadastroClienteView.Essainterfacedefinemtodosget()eset()paraconfigurarosvaloresdoscomponentes,habilitar/desabilitarentradadedadosnoscomponentesvisuaisequaisqueroutrasoperaesquesejamnecessrias.EssasoperaessoutilizadaspeloPresenter,quenocasoaclasseCadastroClientePresenter.ComomostradonaFigura2,aclasseCadastroClientePresenterseregistracomoobservadordoseventosdisparadosnaView,eostrata.
ParailustrarmelhorainteraoentreasclassesdefinidasnaFigura2,considereoseguintediagramadeseqncia:
Figura3?Procedimentodeinserodeumnovoclientenosistema
Note que o diagrama est dividido em trs partes. A primeira parte mostra como ocorre a instanciao da janela de cadastro de clientes: um objetopresenterInstanceobtmoobjetowindowInstanceatravsdecriaoexplcita(comooperadornew)ouinjetadoviaIoC.Emseguida,oobjetopresenterInstanceinicializaoModel,quenocasocompostoporumacoleodeobjetosCliente.Finalmente,oobjetopresenterInstanceexibeajanela.(Parte1)
Ajanelaficaaguardandoousurioinserirumnovocliente.Quandoousurioclicanoboto?Inserir?,oobjetopresenterInstanceexecutaocdigoresponsvelporliberar os campos para que o usurio digite os dados do novo cliente, atravs de chamadas aosmtodos definidos na interface CadastroClienteView, que implementadapeloobjetowindowInstance.(Parte2)
Paraconfirmarocadastro,ousuriopressionaoboto?Confirmar?.Emseguida,oobjetopresenterInstancecriaumanovainstnciadaclasseClienteepreencheseusatributos,deacordocomosvaloresdigitadosnajanela.Depois,oPresenteradicionaesseclientenalistadeclientescadastradoseatualizaaView.(Parte3)
Paraaplicarosconceitosvistosataqui,vamoscriarumaaplicaodeexemploutilizandoopatternMVP.
Aplicativo de DemonstraoAsimagensabaixodemonstramonossoaplicativoemfuncionamento:
Figura4?Janeladecadastrodeclientes
Figura5?Inserindoumnovocliente
Figura6?Novoclientecadastradonosistema
Figura7?Removendoumclientecadastrado
Aaplicaodeexemplobastantesimples,masmostracomoaplicaropatternecomoutilizloparatornarodesenvolvimentodeaplicaesrichclientmenostraumtico.
Ferramentas utilizadasParacriaodesseexemplo,aconselhoautilizaodaIDENetBeans,devidoagrandefacilidadedecriaode interfacesgrficascomSwing.NesteprogramatambmusaremosoframeworkSpringpara?colar?oscomponentesdaaplicaosemquesejanecessrioescrevercdigoparaisso.
Criando o ModelInicieumnovoprojetonoNetBeanscomqualquernome.Emseguida,crieumaclassechamadaCliente,nopacoteorg.javafree.mvp.model.EssaclasseseronossoModel.
Cliente.java
packageorg.javafree.mvp.model;publicclassCliente{privateLongid;privateStringnome;privateStringtelefoneResidencial;privateStringtelefoneComercial;privateStringtelefoneCelular;privateStringemail;publicCliente(){}publicCliente(Longid,Stringnome,StringtelefoneResidencial,StringtelefoneComercial,StringtelefoneCelular,Stringemail){setId(id);setNome(nome);setTelefoneResidencial(telefoneResidencial);setTelefoneComercial(telefoneComercial);setTelefoneCelular(telefoneCelular);setEmail(email);}//MtodosGeteSetpublicObjectclone(){returnnewCliente(this.id,this.nome,this.telefoneResidencial,this.telefoneComercial,this.telefoneCelular,this.email);}publicbooleanequals(Objectobj){if(objinstanceofCliente){Clientec=(Cliente)obj;returnc.getId().equals(id);}returnfalse;}}
Criando a ViewCrieumnovoJFramechamadoCadastroClienteWindow,nopacoteorg.javafree.mvp.gui.cadastro.
Crieumlayoutparecidocomafiguraabaixo:
Figura8?Layoutdaaplicao
EstainterfacecompostaporseteJLabel,seisJTextField,seisJButton,umJScrollPaneeumJTable:
Classe Objetos
JLabel lbClienteId,lbClienteNome,lbClienteTelResidencial,lbClienteTelComercial,lbClienteTelCelular,lbClienteEmail,lbClientesCadastradosJTextField txtClienteId,txtClienteNome,txtClienteTelResidencial,txtClienteTelComercial,txtClienteTelCelular,txtClienteEmailJButton btnInserir,btnRemover,btnAlterar,btnConfirmar,btnCancelar,btnSairJScrollPane spClientesCadastradosJTable tableClientesCadastradosParapermitirqueoPresenterinterajacomaViewsemsaberoseutipo,precisamoscriarumainterface,quedeveserimplementadapelanossaView.Assim,nsnoestaremosprendendooPresenteraumanicaView.
Aindanopacoteorg.javafree.mvp.gui.cadastro,crieumaclassechamadaCadastroClienteView.
CadastroClienteView.java
packageorg.javafree.mvp.gui.cadastro;publicinterfaceCadastroClienteView{//GetseSetsdosatributosquerepresentamomodelpublicvoidsetId(Longid);publicLonggetId();publicvoidsetNome(Stringnome);publicStringgetNome();//...publicvoidpackAndShow();publicvoidclearFields();publicintlinhaSelecionadaTableClientes();publicvoidenableTxtClienteId(booleanarg);publicvoidenableTxtClienteNome(booleanarg);//...publicvoidsetClientesTableModel(ClientesTableModelmodel);publicClientesTableModelgetClientesTableModel();publicvoidrefreshTableClientes();//MtodosparaconfiguraroslistenersquecompealgicadeapresentaopublicvoidsetInserirActionListener(ActionListenerlistener);publicvoidsetRemoverActionListener(ActionListenerlistener);publicvoidsetAlterarActionListener(ActionListenerlistener);//...}
Modifique a classe CadastroClienteWindow, fazendo que ela implemente a interface CadastroClienteView. Adicione o seguinte cdigo na classeCadastroClienteWindow:
listadetodososclientescadastradosnosistema.
ClientesTableModel.java
packageorg.javafree.mvp.gui.cadastro;publicclassClientesTableModelextendsAbstractTableModel{privateListclientes;publicClientesTableModel(Listclientes){this.clientes=clientes;}publicObjectgetValueAt(introwIndex,intcolumnIndex){Clientecliente=clientes.get(rowIndex);if(cliente!=null){switch(columnIndex){case0:returncliente.getId();case1:returncliente.getNome();case2:returncliente.getEmail();}}returnnull;}publicintgetRowCount(){if(clientes!=null){returnthis.clientes.size();}return0;}publicintgetColumnCount(){//Id,Nome,Emailreturn3;}publicClientegetCliente(introw){if(row>=0){returnthis.clientes.get(row);}returnnull;}}
Criando o PresenterAgoradefiniremosaclassePresenter,queserresponsvelpelalgicadeapresentaodonossoaplicativo.CrieumaclassechamadaCadastroClientePresenter,nopacoteorg.javafree.mvp.presenter.cadastro.
CadastroClientePresenter.java
packageorg.javafree.mvp.presenter.cadastro;publicclassCadastroClientePresenter{privateClientecliente;privateListmodel;privateCadastroClienteViewview;publicvoidcreateView(){this.novoCliente();this.setUpViewListeners();this.habilitarEdicao(false);//...view.packAndShow();}publicvoidsetUpViewListeners(){//Implementadoposteriormente}publicvoidupdateModelFromView(){cliente.setId(view.getId());cliente.setNome(view.getNome());
//...}publicvoidupdateModelFromJTable(){ClientesTableModeltbModel=view.getClientesTableModel();cliente=tbModel.getCliente(view.linhaSelecionadaTableClientes());if(cliente!=null){this.updateViewFromModel();view.enableBtnAlterar(true);view.enableBtnRemover(true);}else{view.enableBtnAlterar(false);view.enableBtnRemover(false);}this.updateViewFromModel();}publicvoidupdateViewFromModel(){if(cliente!=null){view.setId(cliente.getId());view.setNome(cliente.getNome());view.setTelefoneResidencial(cliente.getTelefoneResidencial());view.setTelefoneComercial(cliente.getTelefoneComercial());view.setTelefoneCelular(cliente.getTelefoneCelular());view.setEmail(cliente.getEmail());}else{view.clearFields();}}publicvoidnovoCliente(){cliente=newCliente();}publicvoidadicionarCliente(){this.updateModelFromView();this.model.add((Cliente)cliente.clone());view.refreshTableClientes();}publicvoidalterarCliente(){this.updateModelFromView();view.refreshTableClientes();}publicbooleanremoverCliente(){this.updateModelFromView();booleanb=this.model.remove(cliente);view.refreshTableClientes();returnb;}publicvoidhabilitarEdicao(booleanarg){view.enableTxtClienteId(arg);view.enableTxtClienteNome(arg);view.enableTxtClienteTelResidencial(arg);view.enableTxtClienteTelComercial(arg);view.enableTxtClienteTelCelular(arg);view.enableTxtClienteEmail(arg);}publicCadastroClienteViewgetView(){returnview;}publicvoidsetView(CadastroClienteViewview){this.view=(CadastroClienteView)view;}publicObjectgetModel(){returnmodel;}publicvoidsetModel(Objectmodel){if(modelinstanceofList){this.model=(List)model;}}}
Para definir os eventos disparados pela View, criaremos duas classes no pacote org.javafree.mvp.presenter.cadastro: ClientesWindowActionListeners eClientesWindowMouseListener.
A classe ClientesWindowActionListener contm todas as classes necessrias para tratar os eventos de ao na View, enquanto a classeClientesWindowMouseListenertrataoseventosdemouse.Segueabaixoocdigodasduasclasses:
ClientesWindowActionListeners.java
packageorg.javafree.mvp.presenter.cadastro;publicclassClientesWindowActionListeners{staticclassAlterarActionListenerimplementsActionListener{privateCadastroClientePresenterpresenter;publicAlterarActionListener(CadastroClientePresenterpresenter){this.presenter=presenter;}publicvoidactionPerformed(ActionEventevt){presenter.habilitarEdicao(true);CadastroClienteViewview=(CadastroClienteView)presenter.getView();view.enableBtnCancelar(true);view.enableBtnConfirmar(true);//...}}staticclassCancelarActionListenerimplementsActionListener{privateCadastroClientePresenterpresenter;publicCancelarActionListener(CadastroClientePresenterpresenter){this.presenter=presenter;}publicvoidactionPerformed(ActionEventevt){CadastroClienteViewview=(CadastroClienteView)presenter.getView();view.clearFields();presenter.habilitarEdicao(false);view.enableBtnCancelar(false);view.enableBtnConfirmar(false);//...}}staticclassConfirmarActionListenerimplementsActionListener{privateCadastroClientePresenterpresenter;publicConfirmarActionListener(CadastroClientePresenterpresenter){this.presenter=presenter;}publicvoidactionPerformed(ActionEventevt){presenter.habilitarEdicao(false);CadastroClienteViewview=(CadastroClienteView)presenter.getView();view.enableBtnCancelar(false);view.enableBtnConfirmar(false);//...}}staticclassInserirActionListenerimplementsActionListener{privateCadastroClientePresenterpresenter;publicInserirActionListener(CadastroClientePresenterpresenter){this.presenter=presenter;}publicvoidactionPerformed(ActionEventevt){presenter.novoCliente();CadastroClienteViewview=(CadastroClienteView)presenter.getView();presenter.habilitarEdicao(true);view.clearFields();view.enableBtnAlterar(false);view.enableBtnCancelar(true);//...}}staticclassRemoverActionListenerimplementsActionListener{privateCadastroClientePresenterpresenter;publicRemoverActionListener(CadastroClientePresenterpresenter){this.presenter=presenter;}publicvoidactionPerformed(ActionEventevt){
if(JOptionPane.showConfirmDialog((Component)presenter.getView(),"Desejaremover?","Pergunta",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE)==JOptionPane.YES_OPTION){CadastroClienteViewview=(CadastroClienteView)presenter.getView();presenter.removerCliente();view.clearFields();view.enableBtnAlterar(false);view.enableBtnRemover(false);}}}staticclassSairActionListenerimplementsActionListener{publicvoidactionPerformed(ActionEventevt){System.exit(0);}}}
ClientesWindowMouseListener.java
packageorg.javafree.mvp.presenter.cadastro;publicclassClientesWindowMouseListener{staticclassTableClientesMouseListenerextendsMouseAdapter{privateCadastroClientePresenterpresenter;publicTableClientesMouseListener(CadastroClientePresenterpresenter){this.presenter=presenter;}publicvoidmouseClicked(MouseEvente){presenter.updateModelFromJTable();CadastroClienteViewview=(CadastroClienteView)presenter.getView();view.enableBtnCancelar(false);view.enableBtnConfirmar(false);view.enableBtnInserir(true);}}}
A criao da camada de lgica de apresentao est quase concluda. Agora que temos as classes que tratam os eventos da View, vamos voltar classeCadastroClientePresentereimplementaromtodosetUpViewListeners()comomostradoabaixo:
publicvoidsetUpViewListeners(){view.setInserirActionListener(newInserirActionListener(this));view.setRemoverActionListener(newRemoverActionListener(this));view.setAlterarActionListener(newAlterarActionListener(this));view.setCancelarActionListener(newCancelarActionListener(this));view.setConfirmarActionListener(newConfirmarActionListener(this));view.setBtnSairActionListener(newSairActionListener());view.setTableClientesMouseListener(newTableClientesMouseListener(this));}
ParaconcluiroPresenter,nsprecisamosdefinirofluxodeoperaesdonossoaplicativo.Vejaaimagem
Figura9?FluxodoscomandosdeIncluireAlterarclientes
Comomostradonaimagem,aoperaodeconfirmao(quefeitaemumnicolocal),devesaberseousurioestinserindoumclientenosistemaouseestmodificandoumclientejcadastrado.
Paraevitartestesdotipo
if(operacao==inserir){//Faaumacoisa}elseif(operao==alterar){//Faaoutracoisa}elseif(etc...)
vamosimplementarestafuncionalidadeutilizandoopadroStrategy.Assim,quandoousuriopressionarobotoInserir,a?estratgia?executadapelobotoConfirmarinseriroclientenosistema.Damesmaforma,seousuriodecidiralterarumcliente,aestratgiadobotoConfirmarseralterarosdadosdocliente.
Paraconseguiresseefeito,vamoscriarumainterfacechamadaStrategy,dentrodopacoteorg.javafree.mvp.presenter.cadastro.
Strategy.java
packageorg.javafree.mvp.presenter.cadastro;publicinterfaceStrategy{publicvoidexecute();}
VolteclasseCadastroClientePresentereinsira,nofinaldocdigodaclasse,oseguintecdigo:
privateclassInsertStrategyimplementsStrategy{publicvoidexecute(){adicionarCliente();}}privateclassUpdateStrategyimplementsStrategy{publicvoidexecute(){alterarCliente();}}
Agora,vamoscriardoisobjetosquerepresentamessasoperaes.Estesdoisobjetosdevemestarvisveisparaasclassestratadorasdeeventos,permitindoadefiniodofluxodaaplicao.
//Constantesdeindicaodeoperao,paracontrolaroqueoboto'confirmar'fazpublicfinalStrategyINSERT_STRATEGY=newInsertStrategy();publicfinalStrategyUPDATE_STRATEGY=newUpdateStrategy();privateStrategyoperacao=INSERT_STRATEGY;//...publicvoidsetOperacao(Strategyoperacao){this.operacao=operacao;}publicStrategygetOperacao(){returnoperacao;}
EstamosquaseterminandoonossoPresenter.AbranovamenteaclasseClientesWindowActionListenersemodifiqueocdigo,comomostradonotrechoabaixo:
//?staticclassAlterarActionListenerimplementsActionListener{//...publicvoidactionPerformed(ActionEventevt){presenter.setOperacao(presenter.UPDATE_STRATEGY);//...}}//...staticclassConfirmarActionListenerimplementsActionListener{//...publicvoidactionPerformed(ActionEventevt){presenter.getOperacao().execute();//...}}//...staticclassInserirActionListenerimplementsActionListener{//...publicvoidactionPerformed(ActionEventevt){presenter.setOperacao(presenter.INSERT_STRATEGY);//...}}
Juntando as partesAnossaaplicaoestpraticamente finalizada.S restaagora criaroarquivode configuraodoSpring.Crieumarquivo chamadoapplicationContext.xmlnoCLASSPATHdaaplicao.Configureseucontedoparaalistagemexibidaabaixo:
comentrios:1
Parafinalizaracriaodonossoaplicativo,crieaclasseMain,nopacoteorg.javafree.mvp.application.
Main.java
packageorg.javafree.mvp.application;publicclassMain{publicstaticvoidmain(String[]args){getPresenter().createView();}publicstaticCadastroClientePresentergetPresenter(){BeanFactoryfactory=newClassPathXmlApplicationContext("./applicationContext.xml");return(CadastroClientePresenter)factory.getBean("presenter");}}
Aaplicaoestfinalizada.Snoseesqueadeadicionarosarquivosspring.jarecommonslogging.jarnasLibrariesdoprojeto.
ConclusoSeformoscompararduasaplicaes,umadelasseguindoopadroMVPeoutrano,inevitvelacomparaoentreocdigoescrito:aaplicaoqueutilizaoMVPtemmaislinhasdecdigoemaisclassesdoqueaplicaoquenoutilizaoMVP.Euparticularmentenoconsideroissoumadesvantagem,poisjustamenteaquebradocdigoemvriasclasses tornaaaplicaomais fcildeentenderedeexpandir,almdepermitirmaior reutilizaoemelhorara testabilidadedocdigo.
RefernciasMartinFowlerhttp://www.martinfowler.com/eaaDev/ModelViewPresenter.html
IBM?sTaligentftp://www6.software.ibm.com/software/developer/library/mvp.pdf
MVChttp://en.wikipedia.org/wiki/Modelviewcontroller
MVCMeetsSwinghttp://www.javaworld.com/javaworld/jw041998/jw04howto.html
LinksNetBeanshttp://www.netbeans.org
SpringFrameworkhttp://www.springframework.org
TpicosRelacionados[Resolvido]PassarvalorSpringMVC>funoJavascript?
UncaughtError:java.lang.reflect.InvocationTargetException
AjudacomPadroMVC
TeladeLoginnosistema
ImagemnoabrenapaginaJSP[RESOLVIDO]
DUVIDASCOM2COMBOBOXRELACIONADAS[]
Parametros[]
[Youtube]CanalVarallos
Erro:Onomedacolunaendereconaovalido
Dvidassobreimplementaoumnovoprojeto(MVC/MVPououtrasigla)
Dvidaemumcdigo.Precisandodeajuda!
ProblemaemsetarcamposformatadosemJava
NoCosigoresolveresteExercciojavasounovonarea.
TDDintroduo
ForumJavaFreecommensagensdesucessotodaszuadas
DvidaJavaBeans+JSP
ImagemnocarreganoJSP(ComSpring)[RESOLVIDO]
RSSNotciasRSSFrum
Jtablenocarrega[RESOLVIDO]
Problema:ButtonSomaButton=(Button)this.findViewById(R.id.SomaButton)
Grficonoaparece(jsf+primefaces)
PadrodeProjeto
ProblemacomSplashScreen
Home Sobre Anuncie
OJavaFree.orgumacomunidadejavaformadapelacoolaboraodosdesenvolvedoresdatecnologiajava.Apublicaodeartigosalmdeajudaracomunidadejava,ajudaadarmaiorvisibilidadeparaoautor.Contribuaconosco.