How I didn’t hit the guy on the pole or the story of amazing ingenuity

In this post I will write about the importance of architectural decisions in development, application support, in a team development environment.

Self-Operating Napkin – Rube Goldberg

During my youth, I worked on an app for ordering a taxi. In the program, you could choose a pickup point, a drop point, calculate the cost of the trip, the type of tariff, and, in fact, order a taxi. I got the application at the last stage of the pre-launch, after adding several fixes, the application was released in the AppStore. Already at that stage, the whole team understood that it was implemented very poorly, design patterns were not used, all components of the system were tightly connected, in general, it could be written into one large continuous class (God object), nothing would have changed, so how classes mixed their boundaries of responsibility and, in their mass, overlapped each other in a dead cohesion. Later, the management decided to write the application from scratch, using the correct architecture, which was done and the final product was implemented by several dozen B2B clients.

However, I will describe a curious incident from past architecture, from which I sometimes wake up in a cold sweat in the middle of the night, or suddenly remember in the middle of the day and start laughing hysterically. The thing is that I could not hit the guy on the pole the first time, and this brought down most of the application, but first things first.

It was an ordinary working day, one of the customers received a task to slightly modify the application design – it is banal to move a few pixels up the icon in the center of the screen on the pickup address selection screen. Well, having professionally evaluated the task in 10 minutes, I raised the icon 20 pixels up, suspecting nothing at all, I decided to check the taxi order.

What? Does the app no ​​longer show the order button? How did it happen?

I could not believe my eyes, after raising the icon by 20 pixels, the application stopped showing the continue ordering button. Rolling back the change, I saw the button again. Something was wrong here. After spending 20 minutes in the debugger, I got a little tired of unwinding spaghetti from calls to overlapping classes, but I found that * moving the picture really changes the logic of the application *

It was all about the icon in the center – the man on the pole, when he moved the map, he jumped to animate the camera movement, this animation was followed by the disappearance of the button below. Apparently the program thought that the man shifted by 20 pixels was in a jump, so, according to internal logic, it hid the confirmation button.

How can this happen? Does * the state * of the screen depend not on the pattern of the state machine, but on the * representation * of the guy’s position on the pole?

Everything turned out to be so, every time the map was drawn, the application * visually pick * in the middle of the screen and checked what was there, if there was a man on a pole, then this means that the animation of the map shift was over and the button had to be shown. In the case when the man is not there, it means that the map is shifting, and the button must be hidden.

In the example above, everything is fine, firstly, this is an example of the Goldberg Machine (abstruse machines), secondly, an example of the developer’s unwillingness to somehow interact with other developers in the team (try to figure it out without me), thirdly, you can list all the problems by SOLID, patterns (code smell), MVC violation, and more.

Try not to do this, develop in all possible directions, help your colleagues in their work. Happy new year everyone)

Links

https://en.wikipedia.org/wiki/Rube_Goldberg_machine

https://en.wikipedia.org/wiki/SOLID

https://refactoring.guru/refactoring/smells

https://en.wikipedia.org/wiki/Model-view-controller

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

Facade Pattern

Facade belongs to structural design patterns. It provides a single interface that provides interaction between client and complex systems. GoF has a good example of the Facade – a compiler of programming languages ​​that provides different clients with different goals, the ability to build code through a single interface of the compiler facade.

References

https://refactoring.guru/design-patterns/facade
https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

Abstract Factory Pattern

Abstract factory – provides an interface for creating the corresponding objects, without specifying specific classes.

I really like the alternative name for this pattern – Kit

It is very similar to the Factory Method, however, Abstract Factories should describe the relationship between the created objects, otherwise it is already just the antipattern God Object, creating unsystematically everything.

Imagine the development of an AR framework for glasses, we display on the screen arrows for indoor navigation, store icons, interesting places, windows and buttons with information about any place where the user is now located.

At the same time, we need the ability to customize the appearance and behavior of the controls of the AR environment. That’s it for this case, you need to use the Set pattern.

We’ll write the interface of Abstract Factory and Products – parent protocols, elements of the AR environment:

 
protocol ARFactory {
    func arrow() -> ARArrow
    func icon() -> ARIcon
    func button() -> ARButton
    func window() -> ARWindow
}

protocol ARArrow {
    var image: { get }
    func handleSelection()
}

protocol ARIcon {
    var image: { get }
    var title: String
}

protocol ARButton {
    var title: String
    func handleSelection()
}

protocol ARWindow {
    var title: String
    var draw(canvas: Canvas)
}
 

Now the kit developers will need to implement the Concrete Factory on the basis of the Abstract Factory interface, and all the elements will have to be implemented together, the rest of the application will be able to work with the factory without changing its code.

References

https://refactoring.guru/design-patterns/abstract-factory
https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

Factory Method Pattern

Pattern Factory Method refers to creational design patterns.
This pattern describes the creation of an interface for creating an object of a particular class. Sounds simple yeah?

Theory

Suppose we are developing a framework for working with AR glasses, when you tilt your head to the side, a menu of available applications should appear in front of the user’s eyes. Applications will be developed by third-party companies, clients of our framework. Naturally, we don’t know which applications, icons, names should appear, so we must provide an interface for implementing the icon and related information about the application. Call it Product:

 
protocol Product {
 var name: String { get }
 var image: Image { get }
 var executablePath: String { get }
}
 

Next, we need to provide an interface so that our customers implement the application array of their Specific Product – an array of application icons with names that we will already draw in the framework.

We’ll write this interface – the Creator interface, containing the Factory Method, which returns an array of Products.

 
protocol Creator {
 func factoryMethod() -> [Product]
}
 

In practice

The first customer of our AR framework was 7B, a leading provider of coffee maker software in Honduras. They want to sell augmented reality glasses with the ability to brew coffee, check the fullness of the water / beans, point the way to their nearest coffee maker in indoor map mode.

They undertake the development of software, we only need to provide documentation on the interfaces of Creator and Product, for the correct listing of applications and their further launch.

After the documentation is transferred, 7B using the Creator interface implements the Concrete Creator – a class that returns an array of icon applications. Icon applications themselves are the Concrete Product classes implementing the Product interface.

Concrete Product code example:

 
class CoffeeMachineLocator: implements Product {
 let name = “7B Coffee Machine Locator v.3000”
 let image = Image.atPath(“images/locator.tga”)
 let executablePath = “CoffeeMachineLocator.wasm”
}

class iPuchinno: implements Product {
 let name = “iPuchinno 1.0.3”
 let image = Image.atPath(“images/puchino.pvrtc”)
 let executablePath = “neutron/ipuchBugFixFinalNoFreezeFixAlpha4.js”
}
 

Concrete Creator class, returning array with two applications:

 
class 7BAppsCreator: implements Creator {
 func factoryMethod() -> [Product] {
  return [CoffeeMachineLocator(), iPuchinno()]
 }
}
 

After that, 7B compiles the library of Concrete Products, Concrete Creator and combines it with our framework, starts selling AR glasses for its coffee makers, no source code modifications on our side is required.

References

https://refactoring.guru/design-patterns/command
https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

Command Pattern

Pattern Command refers to behavioral design patterns.

This is the pattern with which I sit longer than the rest, it is so simple that it is very complicated. But personally, I find the charm of self-learning in that you have all the time in the world to explore a specific issue from all angles.

So, in GoF, applicability is described rather concisely and clearly:
Encapsulates the request as an object, allowing you to configure (parameterize) clients with different requests, use queues, log requests and perform cancellation operations.

Now we implement a simple version of the command from the description:

 
string fakeTrumpsRequest = “SELECT * from Users where name beginsWith DonaldTrump”
 

We encapsulated the request in a string class object, it can configure clients, add commands to the queue, log, cancel (using the “Snapshot” pattern)

It seems to me that this is quite enough to implement SQL queries and the like, but then the implementation details begin, various applications, the code base of the pattern, the roles of the clients vary greatly, auxiliary classes are added.

The Math Part

The command pattern begins with the Command protocol, which contains a single execute() method. Next comes the Concrete Command and Receiver, QC implements the operation on the Receiver, describes the relationship between the Receiver and the action. Nothing is clear? Me too, but drove on. Client creates an instance of the Specific Command, associates it with the Receiver. Invoker – an object that implements the process of launching Command.

Now let’s try to figure out an example, let’s say we want to update myOS on myPhone, for this we launch the application myOS_Update!, in it we press the Update Now! Button, after 10 seconds the system will inform you of a successful update.

The Client in the example above is the application myOS_Update!, Invoker is the “Update Now!” button, it launches the Specific Command of the system update using the execute(), which refers to the Receiver – the daemon for updating the operating system.

Case Study

Let’s say the application UI is myOS_Update! so good that they decided to sell it as a separate product to provide an update interface for other operating systems. In this case, we will implement an application with support for extension through libraries, in the libraries there will be implementations of Specific Commands, Receivers, we will leave static/unchanged Invoker, Client, protocol Command.

Thus, there is no need to provide support for mutable code, since our code will remain unchanged, problems can arise only when implemented on the client side, due to errors in the code of their Concrete Commands and Receivers. Also, in such an implementation, there is no need to transfer the source code of the main application, that is, we encapsulated commands and UI interactions using the Command pattern.

References

https://refactoring.guru/design-patterns/command
https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

Builder Pattern

The Builder pattern belongs to a group of patterns whose existence is not particularly clear to me, I note a clear redundancy of this. Refers to a group of generative design patterns. Used to implement a simple interface for creating complex objects.

Usage

Simplification of the interface. It can facilitate the creation of an object in constructors with a large number of arguments, objectively improve the readability of the code.

C ++ example without a builder:


auto monster = new Monster();
auto weapon = new Weapon(“Claws”);
monster->weapon = weapon;
auto health = new MonsterHealth(100);
monster->health = health;

C ++ example with a builder:


auto monster = new MonsterBuilder()
              .addWeapon(“Claws”)
              .addHealth(100)
              .build();

However, in languages ​​that support named arguments, there is no need to use it for this particular case.

Swift example using named arguments:


let monster = Monster(weapon: “Claws”, health: 100)

Immutability. Using the builder, you can ensure the encapsulation of the created object, until the final stage of assembly. Here you need to think carefully about whether the use of the pattern will save you from the high dynamics of the environment in which you work, perhaps the use of the pattern will not work, due to the simple lack of culture of using encapsulation by the development team.

Interaction with components at different stages of creating an object. Also, using the pattern, it is possible to provide step-by-step creation of an object when interacting with other components of the system. Most likely it is very useful (?)

Critics

Of course you need to * thoroughly * think about whether it is necessary to establish the widespread use of the pattern in your project. Languages ​​with modern syntax and an advanced IDE eliminate the need to use the Builder in terms of improving the readability of the code (see paragraph on named arguments)
Was it necessary to use this pattern in 1994, at the time of the release of the GoF book? Most likely yes, however, judging by the Open source code base of those years, few people used it.

References

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

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

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