Strategy pattern

The Strategy pattern allows you to select the type of algorithm that implements a common interface, right while the application is running. This pattern refers to the behavioral design patterns.

Sun Tzu

Suppose we are developing a music player with embedded codecs. The built-in codecs imply reading music formats without using external sources of the operating system (codecs), the player should be able to read tracks of different formats and play them. VLC player has such capabilities, it supports various types of video and audio formats, it runs on popular and not very operating systems.

Imagine what a naive player implementation looks like:

var player: MusicPlayer?

func play(filePath: String) {
    let extension = filePath.pathExtension

    if extension == "mp3" {
        playMp3(filePath)
    }
    else if extension == "ogg" {
        playOgg(filePath)
    }
}

func playMp3(_ filePath: String) {
    player = MpegPlayer()
    player?.playMp3(filePath)
}

func playOgg(_ filePath: String) {
    player = VorbisPlayer()
    player?.playMusic(filePath)
}

Next, we add several formats, which leads to the need to write additional methods. Plus, the player must support plug-in libraries, with new audio formats that will appear later. There is a need to switch the music playback algorithm, the Strategy pattern is used to solve this problem.

Let’s create a common protocol MusicPlayerCodecAlgorithm, write the implementation of the protocol in two classes MpegMusicPlayerCodecAlgorithm and VorbisMusicPlayerCodecAlgorithm, to play mp3 and ogg files with-but. Create a class MusicPlayer, which will contain a reference for the algorithm that needs to be switched, then by the file extension we implement codec type switching:

import Foundation

class MusicPlayer {
    var playerCodecAlgorithm: MusicPlayerCodecAlgorithm?
    
	func play(_ filePath: String) {
            playerCodecAlgorithm?.play(filePath)
	}
}

protocol MusicPlayerCodecAlgorithm {
    func play(_ filePath: String)
}

class MpegMusicPlayerCodecAlgorithm: MusicPlayerCodecAlgorithm {
	func play(_ filePath: String) {
		debugPrint("mpeg codec - play")
	}
}

class VorbisMusicPlayerCodecAlgorithm: MusicPlayerCodecAlgorithm {
	func play(_ filePath: String) {
		debugPrint("vorbis codec - play")	
	}
}

func play(fileAtPath path: String) {
	guard let url = URL(string: path) else { return }
	let fileExtension = url.pathExtension
		
	let musicPlayer = MusicPlayer()
	var playerCodecAlgorithm: MusicPlayerCodecAlgorithm? 
		
	if fileExtension == "mp3" {
                playerCodecAlgorithm = MpegMusicPlayerCodecAlgorithm()
	}
	else if fileExtension == "ogg" {
                playerCodecAlgorithm = VorbisMusicPlayerCodecAlgorithm()
	}
		
	musicPlayer.playerCodecAlgorithm = playerCodecAlgorithm
	musicPlayer.playerCodecAlgorithm?.play(path)
}

play(fileAtPath: "Djentuggah.mp3")
play(fileAtPath: "Procrastinallica.ogg")

The above example also shows the simplest example of a factory (switching the codec type from the file extension) It is important to note that the Strategy strategy does not create objects, it only describes how to create a common interface for switching the family of algorithms.

Documentation

https://refactoring.guru/en/design-patterns/strategy

Source code

https://gitlab.com/demensdeum/patterns/

Iterator pattern

In this article I will describe the Iterator pattern.
This pattern refers to the behavioral design patterns.

Print it

Suppose we need to print a list of tracks from the album “Procrastinate them all” of the group “Procrastinallica”.
The naive implementation (Swift) looks like this:

for i=0; i < tracks.count; i++ {
    print(tracks[i].title)
}

Suddenly during compilation, it is detected that the class of the tracks object does not give the number of tracks in the count call, and moreover, its elements cannot be accessed by index. Oh…

Filter it

Suppose we are writing an article for the magazine “Wacky Hammer”, we need a list of tracks of the group “Djentuggah” in which bpm exceeds 140 beats per minute. An interesting feature of this group is that its records are stored in a huge collection of underground groups, not sorted by albums, or for any other grounds. Let’s imagine that we work with a language without functionality:

var djentuggahFastTracks = [Track]()

for track in undergroundCollectionTracks {
    if track.band.title == "Djentuggah" && track.info.bpm == 140 {
        djentuggahFastTracks.append(track)
    }
}

Suddenly, a couple of tracks of the group are found in the collection of digitized tapes, and the editor of the magazine suggests finding tracks in this collection and writing about them. A Data Scientist friend suggests to use the Djentuggah track classification algorithm, so you don’t need to listen to a collection of 200 thousand tapes manually. Try:

var djentuggahFastTracks = [Track]()

for track in undergroundCollectionTracks {
    if track.band.title == "Djentuggah" && track.info.bpm == 140 {
        djentuggahFastTracks.append(track)
    }
}

let tracksClassifier = TracksClassifier()
let bpmClassifier = BPMClassifier()

for track in cassetsTracks {
    if tracksClassifier.classify(track).band.title == "Djentuggah" && bpmClassifier.classify(track).bpm == 140 {
        djentuggahFastTracks.append(track)
    }
}

Mistakes

Now, just before sending to print, the editor reports that 140 beats per minute are out of fashion, people are more interested in 160, so the article should be rewritten by adding the necessary tracks.
Apply changes:

var djentuggahFastTracks = [Track]()

for track in undergroundCollectionTracks {
    if track.band.title == "Djentuggah" && track.info.bpm == 160 {
        djentuggahFastTracks.append(track)
    }
}

let tracksClassifier = TracksClassifier()
let bpmClassifier = BPMClassifier()

for track in cassetsTracks {
    if tracksClassifier.classify(track).band.title == "Djentuggah" && bpmClassifier.classify(track).bpm == 140 {
        djentuggahFastTracks.append(track)
    }
}

The most attentive ones noticed an error; the bpm parameter was changed only for the first pass through the list. If there were more passes through the collections, then the chance of a mistake would be higher, that is why the DRY principle should be used. The above example can be developed further, for example, by adding the condition that you need to find several groups with different bpm, by the names of vocalists, guitarists, this will increase the chance of error due to duplication of code.

Behold the Iterator!

In the literature, an iterator is described as a combination of two protocols / interfaces, the first is an iterator interface consisting of two methods – next(), hasNext(), next() returns an object from the collection, and hasNext() reports that there is an object and the list is not over. However in practice, I observed iterators with one method – next(), when the list ended, null was returned from this object. The second is a collection that should have an interface that provides an iterator – the iterator() method, there are variations with the collection interface that returns an iterator in the initial position and in end – the begin() and end() methods are used in C ++ std.
Using the iterator in the example above will remove duplicate code, eliminate the chance of mistake due to duplicate filtering conditions. It will also be easier to work with the collection of tracks on a single interface – if you change the internal structure of the collection, the interface will remain old and the external code will not be affected.
Wow!

let bandFilter = Filter(key: "band", value: "Djentuggah")
let bpmFilter = Filter(key: "bpm", value: 140)
let iterator = tracksCollection.filterableIterator(filters: [bandFilter, bpmFilter])

while let track = iterator.next() {
    print("\(track.band) - \(track.title)")
}

Changes

While the iterator is running, the collection may change, thus causing the iterator’s internal counter to be invalid, and generally breaking such a thing as “next object”. Many frameworks contain a check for changing the state of the collection, and in case of changes they return an error / exception. Some implementations allow you to remove objects from the collection while the iterator is running, by providing the remove() method in the iterator.

Documentation

https://refactoring.guru/en/design-patterns/iterator

Source code

https://gitlab.com/demensdeum/patterns/

Memento pattern

In this note, I will describe the “Snapshot” or “Memento” pattern.
This pattern refers to the “Behavioral” design patterns.

Suppose we are developing a graphics editor, and we need to add the ability to roll back actions at the user’s command. It is also very important that the system components do not have access to the internal state of the rollback “actions”. When implementing this pattern, the other system components have access only to the object snapshot without the ability to change its internal state, providing a clear, simple external interface. To solve this problem, use the “Snapshot” or “Memento” pattern.

Memento pattern example:

When you click a sprite appears, when you click on a undo button, the action is canceled – the sprite disappears. The example consists of three classes:

  1. Canvas that shows sprites, user interface.
  2. Screen controller, it handles input and controls screen logic.
  3. Canvas states that are saved with each change, and could be are reverted.

In terms of the Snapshot pattern, classes are:

  1. Canvas – originator, which stated are saved as “mementos”, to revert changes if needed. Originator must revert his state, from memento object if necessary.
  2. Screen controller – caretaker, this class controls all screen, and know how and when to revert changes.
  3. Canvas state – memento, which contains state, and some kind of index to track changes correctly.

An important feature of the pattern is that only the Originator should have access to the internal fields of the saved state in the snapshot. Embedded classes are used to implement encapsulation, and in C ++, the ability to specify friend classes is used. Personally, I implemented a simple version without encapsulation for Rise, and using Generic when implementing for Swift. In my version, Memento gives its inner state only to entities of the same class state:

Documentation

https://refactoring.guru/design-patterns/memento

Source code

https://gitlab.com/demensdeum/patterns/

Visitor pattern

In this article I will describe a design pattern called “Visitor”
This pattern refers to the group Behavioral patterns.

Think up a problem

Basically, this pattern is used to bypass the restriction of a single dispatch (“single dispatch”), in languages ​​with early binding.

Alice X by NFGPhoto (CC-2.0)
Create an abstract class/protocol Band, make a subclass of MurpleDeep, create a class Visitor with two methods – one to output any inheritor to the console, the second to output any MurpleDeep, the main thing is that the names (signatures) of the methods are the same, and the arguments differ only in class. Through the intermediate printout method with the Band argument, create an instance of Visitor and call the visit method for MurpleDeep.
Next code on Kotlin:

The output will be “This is Band class

WTF (World Taekwondo Federation)

Why this happens is described in buzzwords in many articles, including in Russian, I suggest you present how the compiler sees the code, maybe everything will become clear right away:

Silver bullet

To solve this problem, there are many solutions, then consider the solution using the Visitor pattern.
We add the accept method with the Visitor argument to the abstract class/protocol, call visitor.visit(this) inside the method, then add the override/implementation of the accept method to the MurpleDeep class, break DRY decisively and quietly, write visitor.visit(this).
The resulting code:

Documentation

https://refactoring.guru/en/design-patterns/visitor-double-dispatch

Source code

https://gitlab.com/demensdeum/patterns

Flyweight pattern

In this article I will describe the structural pattern “Flyweight”
This pattern refers to the group Structural Patterns.

Example of the pattern below:

Why is it needed? To save RAM memory. I agree that in times of widespread use of Java (which consumes cpu and memory just like that), this is not so important, but it’s worth it.
In the example above, only 40 objects are displayed, but if you raise their number to 120,000, the memory consumption will increase accordingly.
Let’s look at the memory consumption without using the flyweight pattern in the Chromium browser:

Without the use of the pattern, the memory consumption is ~ 300 megabytes.

Now add a pattern to the application and see the memory consumption:

With the use of the pattern, the memory consumption is ~ 200 megabytes, so we saved 100 megabytes of memory in the test application, in serious projects the difference can be much larger.

Wie funktioniert das?

In the example above, we draw 40 cats or, for clarity, 120 thousand. Each cat is loaded into memory as a png image, then in most renders it is converted into a bitmap for rendering (actually bmp), this is done for speed, since a compressed png is drawn for a very long time. Without using a pattern, we load 120 thousand pictures of cats into RAM and draw, but when using the lightweight pattern, we load one cat into memory and draw it 120 thousand times with different positions and transparency. All the magic lies in the fact that we implement the coordinates and transparency separately from the cat image, when rendering the render takes just one cat and uses an object with coordinates and transparency to correctly draw.

Show me the code

The following are examples for the Rise language.

Without a pattern:


The cat image is loaded for each object in the loop separately – catImage.

Using the pattern:

One cat picture is used by 120 thousand objects.

Real life example

It is used in GUI frameworks, for example, in Apple, in the “reuse” system of the cells of the UITableViewCell tables, which raise the entry threshold for beginners who do not know about this pattern.

Source code

https://gitlab.com/demensdeum/patterns/

Documents

https://refactoring.guru/en/design-patterns/flyweight
http://gameprogrammingpatterns.com/flyweight.html

The Good, the Bad and the Ugly Singleton

In this article I will describe my experience and the experience of my colleagues when working with the Singleton pattern, when working on different (successful and not so successful) projects. I will describe why I personally consider this pattern to not be used anywhere, I will also describe what psychological factors in the team influence the integration of this anti-pattern. Dedicated to all fallen and crippled developers who tried to understand why it all started with one of the team members who brought a cute little puppy, easy to use, does not require special care and knowledge to care for him, and ended up raising the beast, it requires more and more man-hours and eats the man-nerves of users, your money and heavily slow down new features implementation.


Wolf in sheep’s clothing by SarahRichterArt

The story takes place in an alternate universe, all coincidences is purely coincidental…

Pet a cat at home with Cat@Home

Every person sometimes has an overwhelming desire to pet a cat at some point in life. Analysts all over the world predict that the first startup that created the application for the delivery and rental of cats will be extremely popular, in the near future will be bought by Moogle for trillions of dollars. Soon it happens – the guy from Tyumen creates the Cat@Home application, and soon becomes a trillionaire, the Moogle company gets itself a new source of profit, and millions of people who are stressed out can order a cat on home.

Attack of the Clones

Extremely rich dentist from Murmansk Alexey Goloborodko, impressed with the article about Cat@Home from Фorbes, decides that he also wants to be astronomically rich. To achieve this goal, through his friends, he finds a company from Goldfield – Wakeboard DevPops that provides software development services, he orders the development of a Cat@Home clone from them.

The Dream Team

The project is called Fur&Pure, it is assigned to a talented development team of 20 people; further focus on the mobile development group of 5 people. Each team member gets his part of the work, armed with agile and scrum, the team completes the development on time (six months), without bugs, releases the application in the iStore, where it is rated by 100.000 users for 5 stars, many comments about how great the application and excellent service (alternative universe, after all). The cats are ironed, the application is released, everything seems to be going well. However, Moogle is not in a hurry to buy a startup for trillions of dollars, because not only cats, but also dogs have already appeared in Cat@Home.

The dogs bark, but the caravan moves

The owner of the application decides that it’s time to add dogs to the application, applies for an assessment to the company and receives estimation at least six months to add dogs to the application. In fact, the application will be written from scratch again. During this time, Moogle will add snakes, spiders and guinea pigs to the application, and Fur&Pur will only receive dogs.
Why did this happen? The lack of a flexible application architecture is to blame for everything, one of the most common factors is the design pattern anti-pattern Singleton.

Nah, can’t be

In order to order a cat at home, the customer needs to create an request and send it to the office, where they will process it at the office and send the courier with the cat, the courier will already receive payment for the service.
One of the programmers decides to create a class “RequestCat” with the required fields, and brings this class into the global application space through singleton. Why does he do it? To save time (penny saving for half an hour), it’s easier to make an global object than to think about the application architecture and use dependency injection. Then the rest of the developers pick up this global object and couple their classes to it. For example, all the screens themselves refer to the global object “RequestCat” and show the data on the application. As a result, such a monolithic application is being tested and released.
Everything seems to be fine, but suddenly a customer appears with the requirement to add an requests for dogs to the application. The team frantically begins to check how many components in the system will affect this change. At the end of the analysis, it turns out that 60 to 90% of the code needs to be altered to teach the application to take in the global singleton object not only the RequestCat but also the RequestDog, it is already useless to estimate the addition of other animals, it’s already difficult with two.

How to avoid singleton

First, during the requirements gathering phase, clearly indicate the need to create a flexible, extensible application architecture. Secondly, it is necessary to conduct an independent examination of the product code on the side, with the mandatory study of weak points. If you are a developer and you like singletons, then I suggest you think again before it’s too late, otherwise sleepless nights and scorched nerves are 100% guaranteed. If you work with a project, in which there are many singletons, then try to get rid of them as soon as possible, or from the project.
You need to switch from the antipattern of singleton-global objects / variables to dependency injection – the simplest design pattern in which all the necessary data is set to an instance of the class at the initialization stage without further need to be tied to global space.

Documents

https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons
http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/
https://blog.ndepend.com/singleton-pattern-costs/

Death Mask Dev Diary 1

The development of the game Death-Mask is in full swing, the logo of the Flame Steel Engine game engine has been added, the initial map selection screen for islands (green, red, black, white) has been added textures for walls, ceiling, maze floor, increased the size of the playing area.


Map of Red Zone City

Next, we plan to add 3D models for the environment, instead of Doom-style sprites, add models for weapons, boxes, enemies, friends. In the gameplay, it is planned to add currency, stores, the ability to buy parts of the game map indicating interesting places with loot, and the possible finding of the “Death Mask”. I also want to add the ability to hire companions for traveling through the cyber-labyrinth. Follow the news.

Swift 4.2.3 – Ubuntu 18.10

Here you can download Swift with libraries to run on Ubuntu 18.10. The latest available version on the Apple site is for Ubuntu 18.04. Based on the build from the official site with libraries from Ubuntu 18.04. +Example with PATH and LD_LIBRARY_PATH for the bash terminal:
http://www.mediafire.com/file/lrs74mvoj3fti03/swift4.2.3.ubuntu.18.10.x86_64.tar.gz/file