{"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\/fr\/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":"Machine \u00e0 \u00e9tats et mod\u00e8le Condition"},"content":{"rendered":"<p>Dans cet article, je vais d\u00e9crire l&#8217;utilisation de la machine \u00e0 \u00e9tats (State Machine), montrer une impl\u00e9mentation simple, une impl\u00e9mentation utilisant le mod\u00e8le State. Il convient de mentionner qu&#8217;il n&#8217;est pas souhaitable d&#8217;utiliser le mod\u00e8le d&#8217;\u00c9tat s&#8217;il y a moins de trois \u00c9tats, car cela conduit g\u00e9n\u00e9ralement \u00e0 une complexit\u00e9 inutile dans la lisibilit\u00e9 du code et \u00e0 des probl\u00e8mes de support associ\u00e9s &#8211; tout doit \u00eatre mod\u00e9r\u00e9.<\/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>Seigneur des drapeaux<\/h3>\n<p>Supposons que nous d\u00e9veloppions un \u00e9cran de lecteur vid\u00e9o pour le syst\u00e8me multim\u00e9dia d&#8217;un avion civil, le lecteur doit \u00eatre capable de charger un flux vid\u00e9o, de le lire, de permettre \u00e0 l&#8217;utilisateur d&#8217;arr\u00eater le processus de t\u00e9l\u00e9chargement, de rembobiner et d&#8217;effectuer d&#8217;autres op\u00e9rations habituelles pour un joueur.<br \/>Supposons que le lecteur ait mis en cache le morceau suivant du flux vid\u00e9o, v\u00e9rifi\u00e9 qu&#8217;il y a suffisamment de morceaux pour la lecture, commenc\u00e9 \u00e0 lire le fragment \u00e0 l&#8217;utilisateur et continue en m\u00eame temps \u00e0 t\u00e9l\u00e9charger le suivant.<br \/>\u00c0 ce stade, l&#8217;utilisateur revient au milieu de la vid\u00e9o, c&#8217;est-\u00e0-dire qu&#8217;il doit maintenant arr\u00eater de lire le fragment en cours et commencer le chargement \u00e0 partir d&#8217;une nouvelle position. Cependant, il existe des situations dans lesquelles cela ne peut pas \u00eatre fait\u00a0: l&#8217;utilisateur ne peut pas contr\u00f4ler la lecture du flux vid\u00e9o pendant qu&#8217;on lui montre une vid\u00e9o sur la s\u00e9curit\u00e9 a\u00e9rienne. V\u00e9rifions l&#8217;indicateur isSafetyVideoPlaying pour v\u00e9rifier cette situation.<br \/>Le syst\u00e8me doit \u00e9galement \u00eatre capable de mettre en pause la vid\u00e9o en cours et de diffuser une alerte du capitaine et de l\u2019\u00e9quipage du navire via le lecteur. Ajoutons un autre indicateur isAnnouncementPlaying. De plus, il est obligatoire de ne pas mettre la lecture en pause lors de l&#8217;affichage de l&#8217;aide sur l&#8217;utilisation du lecteur, un autre indicateur est HelpPresenting.<\/p>\n<p>Exemple de pseudocode du lecteur multim\u00e9dia\u00a0:<\/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>L&#8217;exemple ci-dessus est difficile \u00e0 lire et \u00e0 maintenir en raison d&#8217;une grande variabilit\u00e9 (entropie). Cet exemple est bas\u00e9 sur mon exp\u00e9rience de travail avec la base de code de *de nombreux* projets qui n&#8217;utilisaient pas de machine \u00e0 \u00e9tats.<br \/ >Chaque case \u00e0 cocher doit sp\u00e9cifiquement \u00ab contr\u00f4ler \u00bb les \u00e9l\u00e9ments de l&#8217;interface et la logique m\u00e9tier de l&#8217;application ; le d\u00e9veloppeur, en ajoutant une autre case \u00e0 cocher, doit \u00eatre capable de jongler avec eux, en v\u00e9rifiant et rev\u00e9rifiant le tout plusieurs fois avec toutes les options possibles.<br \/>En rempla\u00e7ant dans la formule \u00ab 2 ^ nombre de cases \u00e0 cocher \u00bb, vous pouvez obtenir 2 ^ 6 = 64 options pour le comportement de l&#8217;application pour seulement 6 cases \u00e0 cocher, toutes ces combinaisons de cases \u00e0 cocher devront \u00eatre coch\u00e9es et g\u00e9r\u00e9es manuellement.<br \/>Du c\u00f4t\u00e9 du d\u00e9veloppeur, l&#8217;ajout de nouvelles fonctionnalit\u00e9s avec un tel syst\u00e8me ressemble \u00e0 ceci\u00a0:<br \/>&#8211; Nous devons ajouter la possibilit\u00e9 d&#8217;afficher la page du navigateur de la compagnie a\u00e9rienne, et elle devrait \u00eatre r\u00e9duite comme pour les films si les membres de l&#8217;\u00e9quipage annoncent quelque chose.<br \/>&#8211; D&#8217;accord, je vais le faire. (Oh putain, je vais devoir ajouter un autre drapeau et rev\u00e9rifier tous les endroits o\u00f9 les drapeaux se croisent, \u00e7a fait beaucoup de choses \u00e0 changer\u00a0!)<\/p>\n<p>C&#8217;est aussi un point faible du syst\u00e8me de drapeaux\u00a0:\u00a0; apporter des modifications au comportement de l&#8217;application. Il est tr\u00e8s difficile d&#8217;imaginer comment modifier rapidement et de mani\u00e8re flexible un comportement en fonction des indicateurs, si apr\u00e8s avoir modifi\u00e9 un seul indicateur, vous devez tout rev\u00e9rifier. Cette approche du d\u00e9veloppement entra\u00eene beaucoup de probl\u00e8mes, une perte de temps et d&#8217;argent.<\/p>\n<h3>Entrez dans la machine<\/h3>\n<p>Si vous regardez attentivement les drapeaux, vous comprendrez qu&#8217;en fait nous essayons de traiter des processus sp\u00e9cifiques se produisant dans le monde r\u00e9el. Nous les listons : mode normal, affichage d&#8217;une vid\u00e9o de s\u00e9curit\u00e9, diffusion d&#8217;un message du capitaine ou des membres d&#8217;\u00e9quipage. Pour chaque processus, un ensemble de r\u00e8gles est connu qui modifie le comportement de l&#8217;application.<br \/>Selon les r\u00e8gles du mod\u00e8le de machine \u00e0 \u00e9tats (machine \u00e0 \u00e9tats), nous listerons tous les processus en tant qu&#8217;\u00e9tats dans l&#8217;\u00e9num\u00e9ration, ajouterons un concept tel qu&#8217;un \u00e9tat au code du joueur, impl\u00e9menterons un comportement bas\u00e9 sur l&#8217;\u00e9tat en supprimant les combinaisons sur les drapeaux. Ainsi, nous r\u00e9duirons les options de test exactement au nombre d&#8217;\u00e9tats.<\/p>\n<p>Pseudocode\u00a0:<\/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>L&#8217;\u00e9norme diff\u00e9rence entre un syst\u00e8me de drapeaux et une machine \u00e0 \u00e9tats r\u00e9side dans l&#8217;entonnoir de commutation d&#8217;\u00e9tat logique dans la m\u00e9thode set(state: ..), il vous permet de traduire la compr\u00e9hension humaine de l&#8217;\u00e9tat en code de programme, sans avoir \u00e0 jouer \u00e0 la logique. jeux de conversion de drapeaux en \u00e9tats avec prise en charge ult\u00e9rieure du code.<\/p>\n<h3>\u00c9tat du mod\u00e8le<\/h3>\n<p>Ensuite, je montrerai la diff\u00e9rence entre l&#8217;impl\u00e9mentation na\u00efve de la machine \u00e0 \u00e9tats et le mod\u00e8le d&#8217;\u00e9tat. Imaginons que nous devions ajouter 10 \u00e9tats ; en cons\u00e9quence, la classe des machines \u00e0 \u00e9tats atteindrait la taille d&#8217;un objet divin, ce qui serait difficile et co\u00fbteux \u00e0 maintenir. Bien s\u00fbr, cette impl\u00e9mentation est meilleure que l&#8217;impl\u00e9mentation du flag (avec le syst\u00e8me de flag, le d\u00e9veloppeur se tirera une balle en premier, et sinon, voyant 2 ^ 10 = 1024 variations, QA se pendra, mais si les deux *ne le font pas remarquerez* la complexit\u00e9 de la t\u00e2che, alors l&#8217;utilisateur dont l&#8217;application est simple remarquera qu&#8217;il refusera de travailler avec une certaine combinaison de drapeaux)<br \/>S&#8217;il y a un grand nombre d&#8217;\u00e9tats, il est n\u00e9cessaire d&#8217;utiliser le mod\u00e8le State.<br \/>Ajoutons un ensemble de r\u00e8gles au protocole d&#8217;\u00c9tat\u00a0:<\/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>D\u00e9pla\u00e7ons l&#8217;impl\u00e9mentation de l&#8217;ensemble de r\u00e8gles dans des \u00e9tats distincts, par exemple, le code d&#8217;un \u00e9tat\u00a0:<\/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>Ensuite, cr\u00e9ons un contexte avec lequel chaque \u00e9tat fonctionnera et int\u00e9grons la machine \u00e0 \u00e9tats\u00a0:<\/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>Les composants d&#8217;application fonctionnent avec le contexte via des m\u00e9thodes publiques\u00a0; les objets d&#8217;\u00e9tat d\u00e9cident eux-m\u00eames de l&#8217;\u00e9tat vers lequel effectuer la transition \u00e0 l&#8217;aide de la machine \u00e0 \u00e9tats \u00e0 l&#8217;int\u00e9rieur du contexte.<br \/>Ainsi, nous avons impl\u00e9ment\u00e9 la d\u00e9composition God Object, maintenir un \u00e9tat changeant sera beaucoup plus facile, gr\u00e2ce au compilateur qui suit les modifications du protocole, r\u00e9duisant la complexit\u00e9 de compr\u00e9hension des \u00e9tats en raison de la r\u00e9duction du nombre de lignes de code, et se concentrant sur r\u00e9soudre un probl\u00e8me d\u2019\u00c9tat sp\u00e9cifique. Vous pouvez \u00e9galement d\u00e9sormais partager le travail en \u00e9quipe, en confiant la mise en \u0153uvre d&#8217;un \u00e9tat sp\u00e9cifique aux membres de l&#8217;\u00e9quipe, sans vous soucier de la n\u00e9cessit\u00e9 de \u00ab r\u00e9soudre \u00bb les conflits, ce qui se produit lorsque vous travaillez avec une grande classe de machines \u00e0 \u00e9tats.<\/p>\n<h3>Sources<\/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>Dans cet article, je vais d\u00e9crire l&#8217;utilisation de la machine \u00e0 \u00e9tats (State Machine), montrer une impl\u00e9mentation simple, une impl\u00e9mentation utilisant le mod\u00e8le State. Il convient de mentionner qu&#8217;il n&#8217;est pas souhaitable d&#8217;utiliser le mod\u00e8le d&#8217;\u00c9tat s&#8217;il y a moins de trois \u00c9tats, car cela conduit g\u00e9n\u00e9ralement \u00e0 une complexit\u00e9 inutile dans la lisibilit\u00e9 du<a class=\"more-link\" href=\"https:\/\/demensdeum.com\/blog\/fr\/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;Machine \u00e0 \u00e9tats et mod\u00e8le Condition&#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":"fr","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\/fr\/wp-json\/wp\/v2\/posts\/2087","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/comments?post=2087"}],"version-history":[{"count":14,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/posts\/2087\/revisions"}],"predecessor-version":[{"id":3952,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/posts\/2087\/revisions\/3952"}],"wp:attachment":[{"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/media?parent=2087"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/categories?post=2087"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/fr\/wp-json\/wp\/v2\/tags?post=2087"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}