{"id":1966,"date":"2019-05-26T15:02:25","date_gmt":"2019-05-26T15:02:25","guid":{"rendered":"http:\/\/demensdeum.com\/blog\/?p=1966"},"modified":"2024-12-16T22:32:37","modified_gmt":"2024-12-16T19:32:37","slug":"mediator-pattern","status":"publish","type":"post","link":"https:\/\/demensdeum.com\/blog\/hi\/2019\/05\/26\/mediator-pattern\/","title":{"rendered":"Pattern Mediator"},"content":{"rendered":"<p>The Mediator pattern is a behavioral design pattern.<br \/><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1995\" src=\"https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2019\/05\/guitarPick.jpg\" alt=\"\" width=\"320\" height=\"213\" \/><br \/>\nOne day you receive an order to develop a joke app &#8211; the user presses a button in the middle of the screen and a funny duck quacking sound is heard.<br \/>After uploading to the app store, the app becomes a hit: everyone quacks through your app, Elon Musk quacks on his Instagram at the latest launch of a super-high-speed tunnel on Mars, Hillary Clinton quacks Donald Trump at the debates and wins the elections in Ukraine, success!<br \/>A naive implementation of the application looks like this:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>class DuckButton {\n    func didPress() {\n        print(\"quack!\")\n    }\n}\n\nlet duckButton = DuckButton()\nduckButton.didPress()\n<\/code><\/pre>\n<\/div>\n<p>Next you decide to add the sound of a dog barking, for this you need to show two buttons for selecting the sound &#8211; with a duck and a dog. Create two button classes DuckButton and DogButton.<br \/>Change the code:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>class DuckButton {\n    func didPress() {\n        print(\"quack!\")\n    }\n}\n\nclass DogButton {\n    func didPress() {\n        print(\"bark!\")\n    }\n}\n\nlet duckButton = DuckButton()\nduckButton.didPress()\n\nlet dogButton = DogButton()\ndogButton.didPress()\n<\/code><\/pre>\n<\/div>\n<p>After another success, we add the sound of a pig squeal, now three classes of buttons:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>class DuckButton {\n    func didPress() {\n        print(\"quack!\")\n    }\n}\n\nclass DogButton {\n    func didPress() {\n        print(\"bark!\")\n    }\n}\n\nclass PigButton {\n    func didPress() {\n        print(\"oink!\")\n    }\n}\n\nlet duckButton = DuckButton()\nduckButton.didPress()\n\nlet dogButton = DogButton()\ndogButton.didPress()\n\nlet pigButton = PigButton()\npigButton.didPress()\n<\/code><\/pre>\n<\/div>\n<p>Users complain that sounds overlap each other.<br \/>We add a check to prevent this from happening, and at the same time introduce the classes to each other:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>class DuckButton {\n    var isMakingSound = false\n    var dogButton: DogButton?\n    var pigButton: PigButton?\n    func didPress() {\n        guard dogButton?.isMakingSound ?? false == false &&\n                pigButton?.isMakingSound ?? false == false else { return }\n        isMakingSound = true\n        print(\"quack!\")\n        isMakingSound = false\n    }\n}\n\nclass DogButton {\n    var isMakingSound = false\n    var duckButton: DuckButton?\n    var pigButton: PigButton?\n    func didPress() {\n        guard duckButton?.isMakingSound ?? false == false &&\n                pigButton?.isMakingSound ?? false == false else { return }\n        isMakingSound = true\n        print(\"bark!\")\n        isMakingSound = false\n    }\n}\n\nclass PigButton {\n    var isMakingSound = false\n    var duckButton: DuckButton?\n    var dogButton: DogButton?\n    func didPress() {\n        guard duckButton?.isMakingSound ?? false == false && \n                dogButton?.isMakingSound ?? false == false else { return }\n        isMakingSound = true\n        print(\"oink!\")\n        isMakingSound = false\n    }\n}\n\nlet duckButton = DuckButton()\nduckButton.didPress()\n\nlet dogButton = DogButton()\ndogButton.didPress()\n\nlet pigButton = PigButton()\npigButton.didPress()\n<\/code><\/pre>\n<\/div>\n<p>Based on the success of your app, the government decides to make a law that allows quack, bark and grunt on mobile devices only from 9:00 am to 3:00 pm on weekdays; at other times, the user of your app risks going to prison for 5 years for indecent sound production using personal electronic devices.<br \/>Change the code:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>import Foundation\n\nextension Date {\n    func mobileDeviceAllowedSoundTime() -> Bool {\n        let hour = Calendar.current.component(.hour, from: self)\n        let weekend = Calendar.current.isDateInWeekend(self)\n        \n        let result = hour >= 9 && hour <= 14 &#038;&#038; weekend == false\n        \n        return result\n    }\n}\n\nclass DuckButton {\n    var isMakingSound = false\n    var dogButton: DogButton?\n    var pigButton: PigButton?\n    func didPress() {\n        guard dogButton?.isMakingSound ?? false == false &#038;&#038;\n                pigButton?.isMakingSound ?? false == false &#038;&#038;\n                 Date().mobileDeviceAllowedSoundTime() == true else { return }\n        isMakingSound = true\n        print(\"quack!\")\n        isMakingSound = false\n    }\n}\n\nclass DogButton {\n    var isMakingSound = false\n    var duckButton: DuckButton?\n    var pigButton: PigButton?\n    func didPress() {\n        guard duckButton?.isMakingSound ?? false == false &#038;&#038;\n                pigButton?.isMakingSound ?? false == false &#038;&#038;\n                 Date().mobileDeviceAllowedSoundTime() == true else { return }\n        isMakingSound = true\n        print(\"bark!\")\n        isMakingSound = false\n    }\n}\n\nclass PigButton {\n    var isMakingSound = false\n    var duckButton: DuckButton?\n    var dogButton: DogButton?\n    func didPress() {\n        guard duckButton?.isMakingSound ?? false == false &#038;&#038; \n                dogButton?.isMakingSound ?? false == false &#038;&#038;\n                 Date().mobileDeviceAllowedSoundTime() == true else { return }\n        isMakingSound = true\n        print(\"oink!\")\n        isMakingSound = false\n    }\n}\n\nlet duckButton = DuckButton()\nlet dogButton = DogButton()\nlet pigButton = PigButton()\n\nduckButton.dogButton = dogButton\nduckButton.pigButton = pigButton\n\ndogButton.duckButton = duckButton\ndogButton.pigButton = pigButton\n\npigButton.duckButton = duckButton\npigButton.dogButton = dogButton\n\nduckButton.didPress()\ndogButton.didPress()\npigButton.didPress()\n<\/code><\/pre>\n<\/div>\n<p>Suddenly a flashlight app starts to push ours out of the market, let's not let it defeat us and add a flashlight by pressing the \"oink-oink\" button, and so-no to the other buttons:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>import Foundation\n\nextension Date {\n    func mobileDeviceAllowedSoundTime() -> Bool {\n        let hour = Calendar.current.component(.hour, from: self)\n        let weekend = Calendar.current.isDateInWeekend(self)\n        \n        let result = hour >= 9 && hour <= 14 &#038;&#038; weekend == false\n        \n        return result\n    }\n}\n\nclass Flashlight {\n\n    var isOn = false\n\n    func turn(on: Bool) {\n        isOn = on\n    }\n}\n\nclass DuckButton {\n    var isMakingSound = false\n    var dogButton: DogButton?\n    var pigButton: PigButton?\n    var flashlight: Flashlight?\n    func didPress() {\n        flashlight?.turn(on: true)\n        guard dogButton?.isMakingSound ?? false == false &#038;&#038;\n                pigButton?.isMakingSound ?? false == false &#038;&#038;\n                 Date().mobileDeviceAllowedSoundTime() == true else { return }\n        isMakingSound = true\n        print(\"quack!\")\n        isMakingSound = false\n    }\n}\n\nclass DogButton {\n    var isMakingSound = false\n    var duckButton: DuckButton?\n    var pigButton: PigButton?\n    var flashlight: Flashlight?\n    func didPress() {\n        flashlight?.turn(on: true)\n        guard duckButton?.isMakingSound ?? false == false &#038;&#038;\n                pigButton?.isMakingSound ?? false == false &#038;&#038;\n                 Date().mobileDeviceAllowedSoundTime() == true else { return }\n        isMakingSound = true\n        print(\"bark!\")\n        isMakingSound = false\n    }\n}\n\nclass PigButton {\n    var isMakingSound = false\n    var duckButton: DuckButton?\n    var dogButton: DogButton?\n    var flashlight: Flashlight?\n    func didPress() {\n        flashlight?.turn(on: true)\n        guard duckButton?.isMakingSound ?? false == false &#038;&#038; \n                dogButton?.isMakingSound ?? false == false &#038;&#038;\n                 Date().mobileDeviceAllowedSoundTime() == true else { return }\n        isMakingSound = true\n        print(\"oink!\")\n        isMakingSound = false\n    }\n}\n\nlet flashlight = Flashlight()\nlet duckButton = DuckButton()\nlet dogButton = DogButton()\nlet pigButton = PigButton()\n\nduckButton.dogButton = dogButton\nduckButton.pigButton = pigButton\nduckButton.flashlight = flashlight\n\ndogButton.duckButton = duckButton\ndogButton.pigButton = pigButton\ndogButton.flashlight = flashlight\n\npigButton.duckButton = duckButton\npigButton.dogButton = dogButton\npigButton.flashlight = flashlight\n\nduckButton.didPress()\ndogButton.didPress()\npigButton.didPress()\n<\/code><\/pre>\n<\/div>\n<p>As a result, we have a huge application that contains a lot of copy-paste code, the classes inside are connected to each other by a dead link - there is no weak coupling, such a miracle is very difficult to maintain and change in the future due to the high chances of making a mistake.<\/p>\n<h3>Using Mediator<\/h3>\n<p>Let's add an intermediate class mediator - ApplicationController. This class will provide weak coupling of objects, ensure separation of class responsibilities, and eliminate duplicate code.<br \/>Let's rewrite:<\/p>\n<div class=\"hcb_wrap\">\n<pre class=\"prism line-numbers lang-unknown\" data-lang=\"unknown\"><code>import Foundation\n\nclass ApplicationController {\n\n    private var isMakingSound = false\n    private let flashlight = Flashlight()\n    private var soundButtons: [SoundButton] = []\n\n    func add(soundButton: SoundButton) {\n        soundButtons.append(soundButton)\n    }\n    \n    func didPress(soundButton: SoundButton) {\n        flashlight.turn(on: true)\n        guard Date().mobileDeviceAllowedSoundTime() && \n                isMakingSound == false else { return }\n        isMakingSound = true\n        soundButton.didPress()\n        isMakingSound = false\n    }\n}\n\nclass SoundButton {\n    let soundText: String\n    \n    init(soundText: String) {\n        self.soundText = soundText\n    }\n    \n    func didPress() {\n        print(soundText)\n    }\n}\n\nclass Flashlight {\n    var isOn = false\n\n    func turn(on: Bool) {\n        isOn = on\n    }\n}\n\nextension Date {\n    func mobileDeviceAllowedSoundTime() -> Bool {\n        let hour = Calendar.current.component(.hour, from: self)\n        let weekend = Calendar.current.isDateInWeekend(self)\n        \n        let result = hour >= 9 && hour <= 14 &#038;&#038; weekend == false\n        \n        return result\n    }\n}\n\nlet applicationController = ApplicationController()\nlet pigButton = SoundButton(soundText: \"oink!\")\nlet dogButton = SoundButton(soundText: \"bark!\")\nlet duckButton = SoundButton(soundText: \"quack!\")\n\napplicationController.add(soundButton: pigButton)\napplicationController.add(soundButton: dogButton)\napplicationController.add(soundButton: duckButton)\n\npigButton.didPress()\ndogButton.didPress()\nduckButton.didPress()\n<\/code><\/pre>\n<\/div>\n<p>Many articles devoted to the architecture of applications with a user interface describe the MVC pattern and its derivatives. The model is used to work with business logic data, the view or presentation shows information to the user in the interface\/provides interaction with the user, the controller is a mediator providing interaction between system components.<\/p>\n<h3>Sources<\/h3>\n<p><a href=\"https:\/\/refactoring.guru\/ru\/design-patterns\/mediator\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/refactoring.guru\/ru\/design-patterns\/ mediator<\/a><\/p>\n<h3>Source code<\/h3>\n<p><a href=\"https:\/\/gitlab.com\/demensdeum\/patterns\/\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/gitlab.com\/demensdeum\/patterns\/<\/a>< \/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Mediator pattern is a behavioral design pattern. One day you receive an order to develop a joke app &#8211; the user presses a button in the middle of the screen and a funny duck quacking sound is heard.After uploading to the app store, the app becomes a hit: everyone quacks through your app, Elon<a class=\"more-link\" href=\"https:\/\/demensdeum.com\/blog\/hi\/2019\/05\/26\/mediator-pattern\/\">Continue reading <span class=\"screen-reader-text\">&#8220;Pattern Mediator&#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":[103,95],"class_list":["post-1966","post","type-post","status-publish","format-standard","hentry","category-techie","category-tutorials","tag-mediator","tag-patterns","entry"],"translation":{"provider":"WPGlobus","version":"3.0.2","language":"hi","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\/hi\/wp-json\/wp\/v2\/posts\/1966","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/comments?post=1966"}],"version-history":[{"count":31,"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/posts\/1966\/revisions"}],"predecessor-version":[{"id":3958,"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/posts\/1966\/revisions\/3958"}],"wp:attachment":[{"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/media?parent=1966"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/categories?post=1966"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/hi\/wp-json\/wp\/v2\/tags?post=1966"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}