Composite Pattern

The Composite pattern refers to structural design patterns.
Suppose we are developing an application – a photo album. The user can create folders, add photos there, and perform other manipulations. You definitely need the ability to show the number of files in folders, the total number of all files and folders.
Obviously, you need to use a tree, but how to implement the architecture of the tree, with a simple and convenient interface? The Composite pattern comes to the rescue.

Sheila in Moonducks

Next, in the Directory, we implement the dataCount () method – by passing through all the elements lying in the component array, adding all their dataCount’s.
Done!
Go example:

package main

import "fmt"

type component interface {
    
    dataCount() int
    
}

type file struct {
    
}

type directory struct {

    c []component

}

func (f file) dataCount() int {

    return 1

}

func (d directory) dataCount() int {
    
    var outputDataCount int = 0
    
    for _, v := range d.c {
        outputDataCount += v.dataCount()
    }
    
    return outputDataCount
    
}

func (d *directory) addComponent(c component) {

    d.c = append(d.c, c)

}

func main() {
    
    var f file
    var rd directory
    rd.addComponent(f)
    rd.addComponent(f)
    rd.addComponent(f)
    rd.addComponent(f)

    fmt.Println(rd.dataCount())
    
    var sd directory
    sd.addComponent(f)
 
    rd.addComponent(sd)
    rd.addComponent(sd)
    rd.addComponent(sd)
    
    fmt.Println(sd.dataCount())
    fmt.Println(rd.dataCount())

}

References

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

0

Adapter Pattern

Benjamín Núñez González

An adapter pattern refers to structural design patterns.

The adapter provides data / interface conversion between two classes / interfaces.

Suppose we are developing a system for determining the goal of a buyer in a store based on neural networks. The system receives a video stream from the store’s camera, determines customers according to their behavior, classifies them into groups. Types of groups – came to buy (potential buyer), just look (onlooker), came to steal something (thief), came to hand over goods (disgruntled buyer), came drunk / high (potential rowdy).

Like all experienced developers, we find a ready-made neural network that can classify species of monkeys in a cage according to the video stream, which the zoological institute of the Berlin zoo has kindly laid out for free access, retrain it on the video stream from the store and get a working state-of-the-art system.

There is only a small problem – the video stream is encoded in mpeg2 format, and our system only supports OGG Theora. We do not have the source code of the system, the only thing we can do is change the dataset and train the neural network. What to do? Write an adapter class that will translate the stream from mpeg2 -> OGG Theora and give into the neural network.

According to the classical scheme, client, target, adaptee and adapter are involved in the pattern. In this case, the client is a neural network that receives a video stream in Theora OGG, the target is the interface with which it interacts, adaptee is the interface that sends the video stream to mpeg2, the adapter converts mpeg2 to Theora OGG and sends via the target interface.

Sounds easy, right?

References

https://en.wikipedia.org/wiki/Adapter_pattern
https://refactoring.guru/design-patterns/adapter

1+

Delegate Pattern

The delegate pattern refers to the basic design patterns.
Suppose we are developing a barbershop application. The application has a calendar for choosing the day for recording, tap on the date should open a list of barbers with the option of choice.
We implement naive binding of system components, combine the calendar and the screen using pointers to each other, to implement the list output:



// pseudocode

class BarbershopScreen {
   let calendar: Calendar

   func showBarbersList(date: Date) {
      showSelectionSheet(barbers(forDate: date))
   }
}

class Calendar {
    let screen: BarbershopScreen

    func handleTap(on date: Date) {
        screen.showBarbersList(date: date)
    }
}

After a few days, the requirements change, before the list is displayed, you need to show offers with a choice of services (beard haircuts, etc.) but not always, on all days except Saturday.
We add the check Saturday today or not to the calendar, depending on it we call the method of the list of barbers or the list of services, for clarity I will demonstrate:



// pseudocode

class BarbershopScreen {
   let calendar: Calendar

   func showBarbersList(date: Date) {
      showSelectionSheet(barbers(forDate: date))
   }

   func showOffersList() {
      showSelectionSheet(offers)
   }
}

class Calendar {
    let screen: BarbershopScreen

    func handleTap(on date: Date)  {
        if date.day != .saturday {
             screen.showOffersList()
        }
        else {
             screen.showBarbersList(date: date)
        }
    }
}

After a week, we are asked to add a calendar to the feedback screen, and at this moment the first architectural oh happens!
What to do? After all, the calendar is tightly connected to the haircut recording screen.
wow! phew! Oh oh
If you continue to work with such a crazy application architecture, you should make a copy of the entire calendar class and associate this copy with the feedback screen.
Ok, it seems good, then we added a few more screens and several copies of the calendar, and then the X moment came. We were asked to change the design of the calendar, that is, now we need to find all copies of the calendar and add the same changes to all. This “approach” greatly affects the speed of development, increases the chance to make a mistake. As a result, such projects find themselves in a trough, when even the author of the original architecture does not understand how copies of his classes work, other hacks added along the way fall apart on the fly.
What had to be done, but it is better that it’s not too late to start doing? Use the delegation pattern!
Delegation is a way to pass class events through a common interface. The following is an example delegate for a calendar:


protocol CalendarDelegate {
   func calendar(_ calendar: Calendar, didSelect date: Date)
}

Now add the work code with the delegate to the sample code:



// pseudocode

class BarbershopScreen: CalendarDelegate {
   let calendar: Calendar

   init() {
       calendar.delegate = self
   }

   func calendar(_ calendar: Calendar, didSelect date: Date) {
        if date.day != .saturday {
            showOffersList()
        }
        else {
             showBarbersList(date: date)
        }
   }

   func showBarbersList(date: Date) {
      showSelectionSheet(barbers(forDate: date))
   }

   func showOffersList() {
      showSelectionSheet(offers)
   }
}

class Calendar {
    weak var delegate: CalendarDelegate

    func handleTap(on date: Date)  {
        delegate?.calendar(self, didSelect: date)
    }
}

As a result, we untied the calendar from the screen at all, when selecting a date from the calendar, it sends a date selection event – * delegates * the processing of the event to the subscriber; the subscriber is the screen.
What benefits do we get in this approach? Now we can change the calendar and the logic of the screens independently of each other, without duplicating classes, simplifying further support; Thus, the “sole responsibility principle” of the implementation of the system components is implemented, the DRY principle is respected.
When using delegation, you can add, change the logic for displaying windows, the order of anything on the screen, and this will absolutely not affect the calendar and other classes, which objectively and should not participate in processes not directly related to them.
Alternatively, programmers who are not particularly bothered use sending messages via a common bus, without writing a separate protocol / interface for the delegate, where it would be better to use delegation. I wrote about the shortcomings of this approach in the last post – “Pattern Observer”.

References

https://refactoring.guru/replace-inheritance-with-delegation

1+

Observer Pattern

The Observer pattern refers to behavioral patterns of design.
The pattern allows you to send an object state change to subscribers using a common interface.
Suppose we are developing a messenger for programmers, we have a chat screen in the application. When you receive a message with the text “problem” and “error” or “something is wrong”, you need to paint the error list screen and the settings screen in red.
Next, I will describe 2 options for solving the problem, the first is simple but extremely difficult to support, and the second is much more stable in support, but it requires skills! during initial implementation.

Common Bus

All implementations of the pattern contain sending messages when data changes, subscribing to messages, and further processing in methods. The shared bus option contains a single object (a singleton is usually used) that provides message dispatch to recipients.
Ease of implementation is as follows:

  1. The object sends an abstract message to the shared bus
  2. Another object subscribed to the shared bus catches the message and decides to process it or not.

One of the implementation options available from Apple (NSNotificationCenter subsystem), message header matching is added to the name of the method that is called by the recipient upon delivery.
The biggest drawback of this approach is that with a further change in the message, you will first need to remember and then manually edit all the places where it is processed, sent. There is a case of quick initial implementation, further long, complex support, requiring a knowledge base for correct operation.

Multicast Delegate

In this implementation, we will make the final class of the multicast delegate, as well as in the case with the common bus, objects can be subscribed to receive “messages” or “events”, however, the tasks of parsing and filtering messages are not assigned to the shoulders of objects. Instead, subscriber classes should implement the delegate’s multicast methods by which it notifies them.
This is realized by using the interfaces / protocols of the delegate, when changing the general interface, the application will stop compiling, at this point it will be necessary to change all the places of processing this message, without having to keep a separate knowledge base for remembering these places. Compiler is your friend.
In this approach, the team’s productivity is increased, since there is no need to write, store documentation, there is no need for a new developer to try to understand how a message is processed, its arguments, instead it works with a convenient and intuitive interface, so the documentation paradigm is implemented through code.
The multicast delegate itself is based on the delegate pattern, which I will write about in the next note.

References

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

0

Proxy Pattern

Proxy pattern refers to structural design patterns.
The pattern describes the technique of working with the class through the class layer – proxy. The proxy allows you to change the functionality of the original class, with the ability to preserve the original behavior, while maintaining the original class interface.
Imagine the situation – in 2015, one of the countries of Western Europe decides to record all requests to the websites of users of the country, in order to improve statistics and an in-depth understanding of the political view of citizens.
Imagine a pseudo-code of naive gateway implementation used by citizens to access the Internet:


class InternetRouter {

    private let internet: Internet

    init(internet: Internet) {
        self.internet = internet
    }

    func handle(request: Request, from client: Client) -> Data {
        return self.internet.handle(request)
    }

}

In the above code, we create an Internet router class, with a pointer to an object that provides Internet access. When a client requests a site, we will return a response from the Internet.

Using the Proxy pattern and the singleton antipattern, add the logging functionality of the client name and URL:


class InternetRouterProxy {

    private let internetRouter: InternetRouter

    init(internet: Internet) {
        self.internetRouter = InternetRouter(internet: internet)
    }

    func handle(request: Request, from client: Client) -> Data {

        Logger.shared.log(“Client name: \(client.name), requested URL: \(request.URL)”)

        return self.internetRouter.handle(request: request, from: client)
    }

}

Due to saving the original InternetRouter interface in the InternetRouterProxy proxy class, it is enough to replace the initialization class from InternerRouter to its proxy, no more changes to the code base are required.

References

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

0

Prototype Pattern

The pattern “Prototype” refers to the group of creational patterns.
Suppose we are developing applications for dating Tender, according to the business model, we have a paid opportunity to make copies of our own profile by changing the name automatically, and the order of photos in places. This is done in order for the user to be able to conduct several profiles at once with a different set of friends in the application.
By clicking on the button to create a copy of the profile, we need to implement a profile copy, auto name generation and re-sort the photos.
Pseudocode:


fun didPressOnCopyProfileButton() {
    let profileCopy = new Profile()
    profileCopy.name = generateRandomName()
    profileCopy.age = profile.age
    profileCopy.photos = profile.photos.randomize()
    storage.save(profileCopy)
}

Now imagine that other team members made a copy-paste copy code or invented it from scratch, and after that a new field was added – likes. This field stores the number of profile likes, now you need to update * all * of the place where copying takes place manually, adding a new field. It is very long and difficult to maintain the code, as in testing.
To solve this problem, a prototype design pattern was invented. Let’s create a common copying protocol, with the copy () method which returns a copy of the object with the required fields. After changing the entity fields, you will need to update only one copy() method, instead of manually searching and updating all places containing the copy code.

References

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

0

State machine and State pattern

In this article I will describe the use of the state machine, I will show a simple implementation, an implementation using the State pattern. It is worth mentioning that the use of the State pattern is undesirable in the presence of fewer than three states, since this usually leads to an excessive complication of the readability of the code, accompanying support problems – everything should be in moderation.

MEAACT PHOTO / STUART PRICE.

Flags Ruler

Suppose we are developing a video player screen for a civil aircraft media system, the player must be able to download the video stream, play it, allow the user to stop the download process, rewind and perform other ordinary operations for the player.
Suppose the player has cached the next chunk of the video stream, checked that there were enough chunks to play, began to play the fragment to the user and at the same time continues to download the next one.
At this point, the user rewinds to the middle of the video, so now you need to stop playing the current track, and start loading from a new position. However, there are situations in which this can not be done – the user can not control the playback of the video stream, while he is shown a video about safety in the air. Get the isSafetyVideoPlaying flag to check this situation.
Also, the system should be able to pause the current video and broadcast an alert from the captain of the vessel and crew through the player. Let’s get another flag isAnnouncementPlaying. Plus, there was a requirement not to pause the playback while displaying help on working with the player, one more flag isHelpPresenting.

Pseudocode:


class MediaPlayer {

    public var isHelpPresenting = false
    public var isCaching = false
    public var isMediaPlaying: Bool = false
    public var isAnnouncementPlaying = false
    public var isSafetyVideoPlaying = false

    public var currentMedia: Media = null

    fun play(media: Media) {

        if isMediaPlaying == false, isAnnouncementPlaying == false, isSafetyVideoPlaying == false {

            if isCaching == false {
                if isHelpPresenting == false {
                    media.playAfterHelpClosed()
                }
                else {
                    media.playAfterCaching()
                }
            }
    }

    fun pause() {
        if isAnnouncementPlaying == false, isSafetyVideoPlaying == false {
            currentMedia.pause()
        }
    }
}

The above example is poorly readable, such code is difficult to maintain due to the large variability (entropy) This example is based on my experience with the code base * of many * projects where the state machine was not used.
Each flag should “manage” the interface elements in a special way; the application’s business logic, the developer, adding the next flag, should be able to juggle them, checking and re-checking everything several times with all possible options.
Substituting into the formula “2 ^ number of flags” you can get 2 ^ 6 = 64 variants of application behavior for a total of 6 flags, all these combinations of flags will need to be checked and maintained manually.
From the developer’s side, adding a new functionality with this system looks like this:
– You need to add the ability to show the browser page of the airline, while it should be minimized as with movies, if the crew members announce something.
– Ok, I will. (Oh, damn, you’ll have to add one more flag, and double-check all the places where the flags intersect, that’s how much you need to change!)

Also a weak point of the flag system is making changes to the behavior of the application. It is very difficult to imagine how to quickly / flexibly change the behavior based on the checkboxes, if, after changing only one flag, you have to double-check everything. This approach to development leads to a lot of problems, loss of time and money.

Enter The Machine

If you take a good look at the flags, you can understand that in fact we are trying to handle specific processes taking place in the real world. We list them: the usual mode, the display of video security, broadcast messages from the captain or crew. For each process, a set of rules is known that change the behavior of the application.
According to the rules of the state machine, we will list all the processes as states in enum, add such a concept as state to the player code, implement the behavior based on the state, removing the combinations on the checkboxes. Thus, we reduce the options for testing to exactly the number of states.

Pseudocode:


enum MediaPlayerState {
	mediaPlaying,
	mediaCaching,
	crewSpeaking,
	safetyVideoPlaying,
	presentingHelp
}

class MediaPlayer {
	fun play(media: Media) {
		media.play()
	}

	func pause() {
		media.pause()
	}
}

class MediaPlayerStateMachine {
	public state: MediaPlayerState
	public mediaPlayer: MediaPlayer
	public currentMedia: Media

	//.. init (mediaPlayer) etc

	public fun set(state: MediaPlayerState) {
		switch state {
			case mediaPlaying:
				mediaPlayer.play(currentMedia)
			case mediaCaching, crewSpeaking,
			safetyVideoPlaying, presentingHelp:
				mediaPlayer.pause()
		}
	}
}

The huge difference between the flag system and the state machine lies in the logic state switching funnel in the set (state: ..) method, it allows you to translate the human understanding of the state into the program code, without having to play logic games to convert the flags to states with further support of the code .

State pattern

Next, I will show the difference between the naive implementation of the state machine and the state pattern. Imagine that you need to add 10 steits, as a result, the state machine class will grow to the size of a godobject, it will be difficult and expensive to maintain. Of course, this implementation is better than the flag, (if the flag system is shot first by the developer, and if not, after seeing 2 ^ 10 = 1024 variations, QA hangs, but if both of them * do not notice the complexity of the task, then the user who has the application just refuses to work with a certain combination of flags)
With a large number of states, it is necessary to use the State pattern.
We will issue a set of rules in the protocol of the State:


protocol State {
    func playMedia(media: Media, context: MediaPlayerContext)
    func shouldCacheMedia(context: MediaPlayerContext)
    func crewSpeaking(context: MediaPlayerContext)
    func safetyVideoPlaying(context:MediaPlayerContext)
    func presentHelp(context: MediaPlayerContext)
}

We will carry out the implementation of the set of rules in separate states, for example, the code of one state:


class CrewSpeakingState: State {
	func playMedia(context: MediaPlayerContext) {
		showWarning(“Can’ t play media - listen to announce!”)
	}

	func mediaCaching(context: MediaPlayerContext) {
		showActivityIndicator()
	}

	func crewSpeaking(context: MediaPlayerContext) {
		set(volume: 100)
	}

	func safetyVideoPlaying(context: MediaPlayerContext) {
		set(volume: 100)
	}

	func presentHelp(context: MediaPlayerContext) {
		showWarning(“Can’ t present help - listen to announce!”)
	}
}

Next, create a context with which each state will work, integrate the state machine:


final class MediaPlayerContext {
	private
	var state: State

	public fun set(state: State) {
		self.state = state
	}

	public fun play(media: Media) {
		state.play(media: media, context: this)
	}

	…
	Other events
}

Application components work with the context through public methods, the state objects themselves decide from which state to which to make the transition using the state machine inside the context.
Thus, we have implemented the God Object decomposition, and it will be much easier to maintain a changing state by tracking the changes in the protocol by the compiler, reducing the complexity of understanding the states due to reducing the number of lines of code and focusing on solving a specific state task. Also, you can now share teamwork, giving the implementation of a particular state to team members, without worrying about the need to “resolve” conflicts, which happens when working with one large class of state machines.

References

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

0

Skeletal Animation (part 1 – shader)

In this article, I will describe my understanding of skeletal animation, which is used in all modern 3D engines to animate characters, game environments, etc.
I will start the description from the most simple part – the vertex shader, because the whole way of the calculations, no matter how complicated it was, ends with the transfer of the prepared data to render in the vertex shader.

Skeletal animation after calculating the CPU enters the vertex shader.
I recall the vertex formula without skeletal animation:
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vertex;
For those who do not understand how this formula originated, you can read my article describing the principle of working with matrices to display 3D content in the context of OpenGL.
For the rest – the formula for the implementation of skeletal animation:
” vec4 animatedVertex = bone0matrix * vertex * bone0weight +”
“bone1matrix * vertex * bone1weight +”
“bone2matrix * vertex * bone2weight +”
“bone3matrix * vertex * bone3weight;\n”
” gl_Position = projectionMatrix * viewMatrix * modelMatrix * animatedVertex;\n”

Ie the final matrix of bone transformation is multiplied by the vertex and the weight of this matrix relative to the vertex. Each vertex can be animated with 4 bones, the force of the impact is regulated by the bone weight parameter, the sum of the effects should be equal to one.
What to do if the vertex is affected by less than 4 bones? It is necessary to divide the weight between them, the effect of the rest to be equal to zero.
Mathematically multiplying a weight by a matrix is ​​called “Multiplying a matrix by a scalar”. Multiplication by a scalar allows you to sum up the effects of the matrices on the final vertex.

The bone transformation matrixes themselves are passed in an array. Moreover, the array contains the matrix for the entire model as a whole, and not for each mesh separately.

But for each vertex the following information is transmitted separately:
– Matrix index that affects vertex
– The weight of the matrix that affects the vertex
More than one bone is transmitted, usually 4 bones per vertex are used.
Also the sum of the weights of the 4 bones should always be equal to one.
Next, consider how it looks in the shader.
Array of matrices:
“uniform mat4 bonesMatrices[kMaxBones];”

Information on the effect of 4 bones on each vertex:
“attribute vec2 bone0info;”
“attribute vec2 bone1info;”
“attribute vec2 bone2info;”
“attribute vec2 bone3info;”

vec2 – in the X coordinate, store the bone index (and translate to int in the shader), in the Y coordinate, the weight of the impact of the bone on the vertex. Why do you have to transfer this data in a two-dimensional vector? Because GLSL does not support the transfer of readable C structures with correct fields to the shader.

Below is an example of obtaining the necessary information from a vector, for further substitution in the formula animatedVertex:

“int bone0Index = int(bone0info.x);”
“float bone0weight = bone0info.y;”
“mat4 bone0matrix = bonesMatrices[bone0Index];”

“int bone1Index = int(bone1info.x);”
“float bone1weight = bone1info.y;”
“mat4 bone1matrix = bonesMatrices[bone1Index];”

“int bone2Index = int(bone2info.x);”
“float bone2weight = bone2info.y;”
“mat4 bone2matrix = bonesMatrices[bone2Index];”

“int bone3Index = int(bone3info.x);”
“float bone3weight = bone3info.y;”
“mat4 bone3matrix = bonesMatrices[bone3Index];”

Now the vertex structure that fills up on the CPU should look like this:
x, y, z, u, v, bone0index, bone0weight, bone1index, bone1weight, bone2index, bone2weight, bone3index, bone3weight

The structure of the vertex buffer is filled once at the time of loading the model, but the transformation matrices are transferred from the CPU to the shader at each rendering frame.

In the remaining parts, I will describe the principle of calculating animation on the CPU, before passing to the vertex shader, I will describe the tree of bones, the passage through the hierarchy of animation-model-node-mesh, matrix interpolation.

References

http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html

Source Code

https://gitlab.com/demensdeum/skeletal-animation

0