{"id":2087,"date":"2019-06-25T22:00:52","date_gmt":"2019-06-25T19:00:52","guid":{"rendered":"http:\/\/demensdeum.com\/blog\/?p=2087"},"modified":"2024-12-16T22:32:36","modified_gmt":"2024-12-16T19:32:36","slug":"%d0%a1%d1%82%d0%b5%d0%b9%d1%82-%d0%bc%d0%b0%d1%88%d0%b8%d0%bd%d0%b0-%d0%b8-%d0%bf%d0%b0%d1%82%d1%82%d0%b5%d1%80%d0%bd-%d0%a1%d0%be%d1%81%d1%82%d0%be%d1%8f%d0%bd%d0%b8%d0%b5","status":"publish","type":"post","link":"https:\/\/demensdeum.com\/blog\/pt\/2019\/06\/25\/%d0%a1%d1%82%d0%b5%d0%b9%d1%82-%d0%bc%d0%b0%d1%88%d0%b8%d0%bd%d0%b0-%d0%b8-%d0%bf%d0%b0%d1%82%d1%82%d0%b5%d1%80%d0%bd-%d0%a1%d0%be%d1%81%d1%82%d0%be%d1%8f%d0%bd%d0%b8%d0%b5\/","title":{"rendered":"M\u00e1quina de estado e padr\u00e3o Condi\u00e7\u00e3o"},"content":{"rendered":"<p>Neste artigo irei descrever o uso da m\u00e1quina de estados (State Machine), mostrar uma implementa\u00e7\u00e3o simples, uma implementa\u00e7\u00e3o utilizando o padr\u00e3o State. Vale ressaltar que n\u00e3o \u00e9 desej\u00e1vel utilizar o padr\u00e3o Estado se houver menos de tr\u00eas estados, pois isso geralmente leva a uma complexidade desnecess\u00e1ria na legibilidade do c\u00f3digo e a problemas de suporte associados. tudo deve ser feito com modera\u00e7\u00e3o.<\/p>\n<figure id=\"attachment_2088\" aria-describedby=\"caption-attachment-2088\" style=\"width: 510px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-2088\" src=\"https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2019\/06\/flags.jpg\" alt=\"\" width=\"510\" height=\"190\" \/><figcaption id=\"caption-attachment-2088\" class=\"wp-caption-text\">MEAACT PHOTO \/ STUART PRICE.<\/figcaption><\/figure>\n<h3>Senhor das Bandeiras<\/h3>\n<p>Suponha que estejamos desenvolvendo uma tela de player de v\u00eddeo para o sistema de m\u00eddia de uma aeronave civil, o player deve ser capaz de carregar um stream de v\u00eddeo, reproduzi-lo, permitir ao usu\u00e1rio interromper o processo de download, retroceder e realizar outras opera\u00e7\u00f5es usuais para um jogador.<br \/>Digamos que o player armazenou em cache o pr\u00f3ximo peda\u00e7o do stream de v\u00eddeo, verificou se h\u00e1 peda\u00e7os suficientes para reprodu\u00e7\u00e3o, come\u00e7ou a reproduzir o fragmento para o usu\u00e1rio e ao mesmo tempo continua baixando o pr\u00f3ximo.<br \/>Neste momento, o usu\u00e1rio retrocede at\u00e9 o meio do v\u00eddeo, ou seja, agora \u00e9 necess\u00e1rio interromper a reprodu\u00e7\u00e3o do fragmento atual e iniciar o carregamento a partir de uma nova posi\u00e7\u00e3o. Por\u00e9m, existem situa\u00e7\u00f5es em que isso n\u00e3o pode ser feito &#8211; o usu\u00e1rio n\u00e3o pode controlar a reprodu\u00e7\u00e3o do fluxo de v\u00eddeo enquanto est\u00e1 vendo um v\u00eddeo sobre seguran\u00e7a a\u00e9rea. Vamos verificar o sinalizador isSafetyVideoPlaying para verificar esta situa\u00e7\u00e3o.<br \/>O sistema tamb\u00e9m deve ser capaz de pausar o v\u00eddeo atual e transmitir um alerta do capit\u00e3o e da tripula\u00e7\u00e3o do navio atrav\u00e9s do player. Vamos adicionar outro sinalizador isAnnouncementPlaying. Al\u00e9m disso, h\u00e1 um requisito para n\u00e3o pausar a reprodu\u00e7\u00e3o enquanto exibe ajuda sobre como trabalhar com o player. Outro sinalizador \u00e9HelpPresenting.<\/p>\n<p>Pseudoc\u00f3digo de exemplo do reprodutor de m\u00eddia:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>class MediaPlayer {\n\n    public var isHelpPresenting = false\n    public var isCaching = false\n    public var isMediaPlaying: Bool = false\n    public var isAnnouncementPlaying = false\n    public var isSafetyVideoPlaying = false\n\n    public var currentMedia: Media = null\n\n    fun play(media: Media) {\n\n        if isMediaPlaying == false, isAnnouncementPlaying == false, isSafetyVideoPlaying == false {\n\n            if isCaching == false {\n                if isHelpPresenting == false {\n                    media.playAfterHelpClosed()\n                }\n                else {\n                    media.playAfterCaching()\n                }\n            }\n    }\n\n    fun pause() {\n        if isAnnouncementPlaying == false, isSafetyVideoPlaying == false {\n            currentMedia.pause()\n        }\n    }\n}\n<\/code><\/pre>\n<\/div>\n<p>O exemplo acima \u00e9 dif\u00edcil de ler e manter devido \u00e0 alta variabilidade (entropia). Este exemplo \u00e9 baseado em minha experi\u00eancia trabalhando com a base de c\u00f3digo de *muitos* projetos que n\u00e3o usavam uma m\u00e1quina de estado.<br \/ >Cada checkbox deve \u201ccontrolar\u201d especificamente os elementos da interface e da l\u00f3gica de neg\u00f3cio da aplica\u00e7\u00e3o, o desenvolvedor, adicionando outro checkbox, deve ser capaz de fazer malabarismos com eles, verificando e verificando tudo v\u00e1rias vezes com todas as op\u00e7\u00f5es poss\u00edveis.<br \/>Substituindo na f\u00f3rmula \u201c2 ^ n\u00famero de caixas de sele\u00e7\u00e3o\u201d voc\u00ea pode obter 2 ^ 6 = 64 op\u00e7\u00f5es de comportamento do aplicativo para apenas 6 caixas de sele\u00e7\u00e3o, todas essas combina\u00e7\u00f5es de caixas de sele\u00e7\u00e3o precisar\u00e3o ser marcadas e mantidas manualmente.<br \/>Do lado do desenvolvedor, adicionar novas funcionalidades a esse sistema \u00e9 assim:<br \/>&#8211; Precisamos adicionar a capacidade de mostrar a p\u00e1gina do navegador da companhia a\u00e9rea, e isso deve minimizar a semelhan\u00e7a com os filmes se os membros da tripula\u00e7\u00e3o anunciarem algo.<br \/>&#8211; Ok, eu farei isso. (Ah, droga, terei que adicionar outra bandeira e verificar novamente todos os locais onde as bandeiras se cruzam, h\u00e1 muitas coisas que precisam ser alteradas!)<\/p>\n<p>Tamb\u00e9m \u00e9 um ponto fraco do sistema de bandeiras &#8211; fazer altera\u00e7\u00f5es no comportamento do aplicativo. \u00c9 muito dif\u00edcil imaginar como alterar o comportamento de forma r\u00e1pida\/flex\u00edvel com base em sinalizadores, se depois de alterar apenas um sinalizador voc\u00ea tiver que verificar tudo novamente. Esta abordagem ao desenvolvimento leva a muitos problemas, perda de tempo e dinheiro.<\/p>\n<h3>Entre na m\u00e1quina<\/h3>\n<p>Se voc\u00ea observar atentamente os sinalizadores, poder\u00e1 entender que, na verdade, estamos tentando processar processos espec\u00edficos que ocorrem no mundo real. N\u00f3s os listamos: modo normal, exibi\u00e7\u00e3o de v\u00eddeo de seguran\u00e7a, transmiss\u00e3o de mensagem do capit\u00e3o ou tripulantes. Para cada processo \u00e9 conhecido um conjunto de regras que alteram o comportamento da aplica\u00e7\u00e3o.<br \/>De acordo com as regras do padr\u00e3o de m\u00e1quina de estado (m\u00e1quina de estado), listaremos todos os processos como estados no enum, adicionaremos um conceito como estado ao c\u00f3digo do jogador, implementaremos comportamento baseado em estado removendo combina\u00e7\u00f5es nos sinalizadores. Assim, reduziremos as op\u00e7\u00f5es de testes exatamente ao n\u00famero de estados.<\/p>\n<p>Pseudoc\u00f3digo:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>enum MediaPlayerState {\n\tmediaPlaying,\n\tmediaCaching,\n\tcrewSpeaking,\n\tsafetyVideoPlaying,\n\tpresentingHelp\n}\n\nclass MediaPlayer {\n\tfun play(media: Media) {\n\t\tmedia.play()\n\t}\n\n\tfunc pause() {\n\t\tmedia.pause()\n\t}\n}\n\nclass MediaPlayerStateMachine {\n\tpublic state: MediaPlayerState\n\tpublic mediaPlayer: MediaPlayer\n\tpublic currentMedia: Media\n\n\t\/\/.. init (mediaPlayer) etc\n\n\tpublic fun set(state: MediaPlayerState) {\n\t\tswitch state {\n\t\t\tcase mediaPlaying:\n\t\t\t\tmediaPlayer.play(currentMedia)\n\t\t\tcase mediaCaching, crewSpeaking,\n\t\t\tsafetyVideoPlaying, presentingHelp:\n\t\t\t\tmediaPlayer.pause()\n\t\t}\n\t}\n}\n<\/code><\/pre>\n<\/div>\n<p>A grande diferen\u00e7a entre um sistema de sinalizadores e uma m\u00e1quina de estado \u00e9 o funil l\u00f3gico de comuta\u00e7\u00e3o de estado no m\u00e9todo set(state: ..), que permite traduzir a compreens\u00e3o humana do estado em c\u00f3digo de programa, sem ter que jogar l\u00f3gica jogos de convers\u00e3o de bandeiras em estados quando houver suporte adicional ao c\u00f3digo.<\/p>\n<h3>Estado do padr\u00e3o<\/h3>\n<p>A seguir mostrarei a diferen\u00e7a entre a implementa\u00e7\u00e3o ing\u00eanua da m\u00e1quina de estados e o padr\u00e3o de estados. Vamos imaginar que precis\u00e1vamos adicionar 10 estados; como resultado, a classe da m\u00e1quina de estados crescer\u00e1 at\u00e9 o tamanho de um objeto divino, o que ser\u00e1 dif\u00edcil e caro de manter. Claro, esta implementa\u00e7\u00e3o \u00e9 melhor do que a implementa\u00e7\u00e3o de flag (com o sistema de flag, o desenvolvedor ir\u00e1 atirar em si mesmo primeiro, e se n\u00e3o, ent\u00e3o vendo 2 ^ 10 = 1024 varia\u00e7\u00f5es, o QA se enforcar\u00e1, mas se ambos *n\u00e3o notar* a complexidade da tarefa, ent\u00e3o o usu\u00e1rio cuja aplica\u00e7\u00e3o \u00e9 simples perceber\u00e1 que se recusar\u00e1 a trabalhar com uma determinada combina\u00e7\u00e3o de flags)<br \/>Se houver um grande n\u00famero de estados, \u00e9 necess\u00e1rio utilizar o padr\u00e3o State.<br \/>Vamos adicionar um conjunto de regras ao protocolo do Estado:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>protocol State {\n    func playMedia(media: Media, context: MediaPlayerContext)\n    func shouldCacheMedia(context: MediaPlayerContext)\n    func crewSpeaking(context: MediaPlayerContext)\n    func safetyVideoPlaying(context:MediaPlayerContext)\n    func presentHelp(context: MediaPlayerContext)\n}\n<\/code><\/pre>\n<\/div>\n<p>Vamos mover a implementa\u00e7\u00e3o do conjunto de regras para estados separados, por exemplo, o c\u00f3digo para um estado:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>class CrewSpeakingState: State {\n\tfunc playMedia(context: MediaPlayerContext) {\n\t\tshowWarning(\u201cCan\u2019 t play media - listen to announce!\u201d)\n\t}\n\n\tfunc mediaCaching(context: MediaPlayerContext) {\n\t\tshowActivityIndicator()\n\t}\n\n\tfunc crewSpeaking(context: MediaPlayerContext) {\n\t\tset(volume: 100)\n\t}\n\n\tfunc safetyVideoPlaying(context: MediaPlayerContext) {\n\t\tset(volume: 100)\n\t}\n\n\tfunc presentHelp(context: MediaPlayerContext) {\n\t\tshowWarning(\u201cCan\u2019 t present help - listen to announce!\u201d)\n\t}\n}\n<\/code><\/pre>\n<\/div>\n<p>A seguir, vamos criar um contexto com o qual cada estado funcionar\u00e1 e integrar a m\u00e1quina de estados:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>final class MediaPlayerContext {\n\tprivate\n\tvar state: State\n\n\tpublic fun set(state: State) {\n\t\tself.state = state\n\t}\n\n\tpublic fun play(media: Media) {\n\t\tstate.play(media: media, context: this)\n\t}\n\n\t\u2026\n\t\u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\n}\n<\/code><\/pre>\n<\/div>\n<p>Os componentes da aplica\u00e7\u00e3o trabalham com o contexto atrav\u00e9s de m\u00e9todos p\u00fablicos; os pr\u00f3prios objetos de estado decidem de qual estado fazer a transi\u00e7\u00e3o usando a m\u00e1quina de estado dentro do contexto.<br \/>Assim, implementamos a decomposi\u00e7\u00e3o do Objeto Deus, manter um estado de mudan\u00e7a ser\u00e1 muito mais f\u00e1cil, gra\u00e7as ao compilador rastreando as altera\u00e7\u00f5es no protocolo, reduzindo a complexidade de compreens\u00e3o dos estados devido \u00e0 redu\u00e7\u00e3o no n\u00famero de linhas de c\u00f3digo, e focando em resolver um problema de estado espec\u00edfico. Agora voc\u00ea tamb\u00e9m pode compartilhar o trabalho em equipe, entregando a implementa\u00e7\u00e3o de um estado espec\u00edfico aos membros da equipe, sem se preocupar com a necessidade de \u201cresolver\u201d conflitos, o que acontece quando se trabalha com uma grande classe de m\u00e1quina de estado.<\/p>\n<h3>Fontes<\/h3>\n<p><a href=\"https:\/\/refactoring.guru\/ru\/design-patterns\/state\" target=\"_blank\" rel=\"noopener\">https:\/\/refactoring.guru\/ru\/design-patterns\/state <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Neste artigo irei descrever o uso da m\u00e1quina de estados (State Machine), mostrar uma implementa\u00e7\u00e3o simples, uma implementa\u00e7\u00e3o utilizando o padr\u00e3o State. Vale ressaltar que n\u00e3o \u00e9 desej\u00e1vel utilizar o padr\u00e3o Estado se houver menos de tr\u00eas estados, pois isso geralmente leva a uma complexidade desnecess\u00e1ria na legibilidade do c\u00f3digo e a problemas de suporte<a class=\"more-link\" href=\"https:\/\/demensdeum.com\/blog\/pt\/2019\/06\/25\/%d0%a1%d1%82%d0%b5%d0%b9%d1%82-%d0%bc%d0%b0%d1%88%d0%b8%d0%bd%d0%b0-%d0%b8-%d0%bf%d0%b0%d1%82%d1%82%d0%b5%d1%80%d0%bd-%d0%a1%d0%be%d1%81%d1%82%d0%be%d1%8f%d0%bd%d0%b8%d0%b5\/\">Continue reading <span class=\"screen-reader-text\">&#8220;M\u00e1quina de estado e padr\u00e3o Condi\u00e7\u00e3o&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[61,52],"tags":[95,109],"class_list":["post-2087","post","type-post","status-publish","format-standard","hentry","category-techie","category-tutorials","tag-patterns","tag-state","entry"],"translation":{"provider":"WPGlobus","version":"3.0.2","language":"pt","enabled_languages":["en","ru","zh","de","fr","ja","pt","hi"],"languages":{"en":{"title":true,"content":true,"excerpt":false},"ru":{"title":true,"content":true,"excerpt":false},"zh":{"title":true,"content":true,"excerpt":false},"de":{"title":true,"content":true,"excerpt":false},"fr":{"title":true,"content":true,"excerpt":false},"ja":{"title":true,"content":true,"excerpt":false},"pt":{"title":true,"content":true,"excerpt":false},"hi":{"title":false,"content":false,"excerpt":false}}},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/2087","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/comments?post=2087"}],"version-history":[{"count":14,"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/2087\/revisions"}],"predecessor-version":[{"id":3952,"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/2087\/revisions\/3952"}],"wp:attachment":[{"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/media?parent=2087"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/categories?post=2087"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/tags?post=2087"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}