Méthode de modèle

La méthode des modèles fait référence aux modèles de conception comportementale. Le modèle décrit une manière de remplacer une partie de la logique d’une classe à la demande, laissant la partie globale inchangée pour les descendants.

Cuban Cars

Supposons que nous développions une banque client, considérons la tâche de développer un module d’autorisation – ; l’utilisateur doit pouvoir se connecter à l’application à l’aide de données de connexion abstraites.
Le module d’autorisation doit être multiplateforme, prenant en charge différentes technologies d’autorisation et stockant les données cryptées de différentes plateformes. Pour implémenter le module, nous choisissons le langage Kotlin multiplateforme, en utilisant la classe abstraite (protocole) du module d’autorisation, nous écrirons une implémentation pour le téléphone MyPhone :

class MyPhoneSuperDuperSecretMyPhoneAuthorizationStorage {
    fun loginAndPassword() : Pair {
        return Pair("admin", "qwerty65435")
    }
}

class ServerApiClient {
    fun authorize(authorizationData: AuthorizationData) : Unit {
        println(authorizationData.login)
        println(authorizationData.password)
        println("Authorized")
    }
}

class AuthorizationData {
    var login: String? = null
    var password: String? = null
}

interface AuthorizationModule {
    abstract fun fetchAuthorizationData() : AuthorizationData
    abstract fun authorize(authorizationData: AuthorizationData)
}

class MyPhoneAuthorizationModule: AuthorizationModule {
    
    override fun fetchAuthorizationData() : AuthorizationData {
        val loginAndPassword = MyPhoneSuperDuperSecretMyPhoneAuthorizationStorage().loginAndPassword()
        val authorizationData = AuthorizationData()
        authorizationData.login = loginAndPassword.first
        authorizationData.password = loginAndPassword.second
        
        return authorizationData
    }
    
    override fun authorize(authorizationData: AuthorizationData) {
        ServerApiClient().authorize(authorizationData)
    }
    
}

fun main() {
    val authorizationModule = MyPhoneAuthorizationModule()
    val authorizationData = authorizationModule.fetchAuthorizationData()
    authorizationModule.authorize(authorizationData)
}

Maintenant, pour chaque téléphone/plateforme, nous devrons dupliquer le code d’envoi de l’autorisation au serveur, c’est une violation du principe DRY. L’exemple ci-dessus est très simple ; dans les classes plus complexes, il y aura encore plus de duplications. Pour éliminer la duplication de code, vous devez utiliser le modèle de méthode modèle.
Déplaçons les parties communes du module vers des méthodes immuables et transférons la fonctionnalité de transfert de données cryptées vers des classes de plateforme spécifiques :

class MyPhoneSuperDuperSecretMyPhoneAuthorizationStorage {
    fun loginAndPassword() : Pair {
        return Pair("admin", "qwerty65435")
    }
}

class ServerApiClient {
    fun authorize(authorizationData: AuthorizationData) : Unit {
        println(authorizationData.login)
        println(authorizationData.password)
        println("Authorized")
    }
}

class AuthorizationData {
    var login: String? = null
    var password: String? = null
}

interface AuthorizationModule {
    abstract fun fetchAuthorizationData() : AuthorizationData
    
    fun authorize(authorizationData: AuthorizationData) {
        ServerApiClient().authorize(authorizationData)
    }
}

class MyPhoneAuthorizationModule: AuthorizationModule {
    
    override fun fetchAuthorizationData() : AuthorizationData {
        val loginAndPassword = MyPhoneSuperDuperSecretMyPhoneAuthorizationStorage().loginAndPassword()
        val authorizationData = AuthorizationData()
        authorizationData.login = loginAndPassword.first
        authorizationData.password = loginAndPassword.second
        
        return authorizationData
    }
    
}

fun main() {
    val authorizationModule = MyPhoneAuthorizationModule()
    val authorizationData = authorizationModule.fetchAuthorizationData()
    authorizationModule.authorize(authorizationData)
}

Sources

https://refactoring.guru/ru/design- modèles/méthode-modèle

Code source

https://gitlab.com/demensdeum/patterns/< /p>

Pont à motifs

Le modèle Bridge fait référence à des modèles de conception structurelle. Il vous permet d’abstraire l’implémentation de la logique de classe en déplaçant la logique dans une classe abstraite distincte. Cela semble simple, n’est-ce pas ?

Supposons que nous implémentions un robot spam capable d’envoyer des messages à différents types de messagers.
Nous l’implémentons en utilisant un protocole commun :

protocol User {
    let token: String
    let username: String
}

protocol Messenger {
    var authorize(login: String, password: String)
    var send(message: String, to user: User)
}

class iSeekUUser: User {
    let token: String
    let username: String
}

class iSeekU: Messenger {

    var authorizedUser: User?
    var requestSender: RequestSender?
    var requestFactory: RequestFactory?

    func authorize(login: String, password: String) {
        authorizedUser = requestSender?.perform(requestFactory.loginRequest(login: login, password: password))
    }
    
    func send(message: String, to user: User) {
        requestSender?.perform(requestFactory.messageRequest(message: message, to: user)
    }
}

class SpamBot {
    func start(usersList: [User]) {
        let iSeekUMessenger = iSeekU()
        iSeekUMessenger.authorize(login: "SpamBot", password: "SpamPassword")
        
        for user in usersList {
            iSeekUMessennger.send(message: "Hey checkout demensdeum blog! http://demensdeum.com", to: user)
        }
    }
}

Imaginons maintenant la sortie d’un nouveau protocole plus rapide pour envoyer des messages pour le messager iSekU. Pour ajouter un nouveau protocole, vous devrez dupliquer l’implémentation du bot iSekU, en n’en modifiant qu’une petite partie. On ne sait pas pourquoi faire cela si seulement une petite partie de la logique de classe a changé. Avec cette approche, le principe DRY est violé ; avec le développement ultérieur du produit, le manque de flexibilité se fera sentir par des erreurs et des retards dans la mise en œuvre de nouvelles fonctionnalités.
Déplaçons la logique du protocole dans une classe abstraite, implémentant ainsi le modèle Bridge :

protocol User {
    let token: String
    let username: String
}

protocol Messenger {
    var authorize(login: String, password: String)
    var send(message: String, to user: User)
}

protocol MessagesSender {
    func send(message: String, to user: User)
}

class iSeekUUser: User {
    let token: String
    let username: String
}

class iSeekUFastMessengerSender: MessagesSender {
    func send(message: String, to user: User) {
        requestSender?.perform(requestFactory.messageRequest(message: message, to: user)
    }
}

class iSeekU: Messenger {

    var authorizedUser: User?
    var requestSender: RequestSender?
    var requestFactory: RequestFactory?
    var messagesSender: MessengerMessagesSender?

    func authorize(login: String, password: String) {
        authorizedUser = requestSender?.perform(requestFactory.loginRequest(login: login, password: password))
    }
    
    func send(message: String, to user: User) {
        messagesSender?.send(message: message, to: user)
    }
}

class SpamBot {

    var messagesSender: MessagesSender?

    func start(usersList: [User]) {
        let iSeekUMessenger = iSeekU()
        iSeekUMessenger.authorize(login: "SpamBot", password: "SpamPassword")
        
        for user in usersList {
            messagesSender.send(message: "Hey checkout demensdeum blog! http://demensdeum.com", to: user)
        }
    }
}

L’un des avantages de cette approche est sans aucun doute la possibilité d’étendre les fonctionnalités de l’application en écrivant des plugins/bibliothèques qui implémentent une logique abstraite sans changer le code de l’application principale.
Quelle est la différence avec le modèle Stratégie ? Les deux modèles sont très similaires, cependant, Strategy décrit la commutation d’*algorithmes*, tandis que Bridge vous permet de changer de grandes parties de *n’importe quelle logique complexe*.

Sources

https://refactoring.guru/ru/design-patterns/bridge

Code source

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

Modèle de chaîne de responsabilité

La chaîne de responsabilité fait référence aux modèles de conception comportementale.


Ganna Dolbieva

La société cinématographique Jah-Pictures a réalisé un film documentaire sur les rastafariens communistes du Libéria intitulé « Red Dawn of Marley ». Le film est très long (8 heures), intéressant, mais avant sa sortie, il s’est avéré que dans certains pays, les plans et les phrases du film peuvent être considérés comme une hérésie et ne recevront pas de licence de distribution. Les producteurs du film décident de supprimer manuellement et automatiquement les moments contenant des phrases douteuses du film. Une double vérification est nécessaire afin que les représentants du distributeur ne soient pas simplement abattus dans certains pays en cas d’erreur lors de l’inspection manuelle et de l’installation.
Les pays sont divisés en quatre groupes : pays sans censure, avec une censure modérée, moyenne et très stricte. Il est décidé d’utiliser des réseaux de neurones pour classer le niveau d’hérésie dans le fragment regardé du film. Pour le projet, des neurones de pointe très coûteux sont achetés, entraînés pour différents niveaux de censure, la tâche du développeur – la tâche du développeur. divisez le film en fragments et transmettez-les à travers une chaîne de réseaux neuronaux, de libre à strict, jusqu’à ce que l’un d’eux détecte une hérésie, puis le fragment est transféré pour examen manuel en vue d’un montage ultérieur. Il est impossible de traverser tous les neurones, car leur travail nécessite trop de puissance de calcul (après tout, il faut quand même payer l’électricité), il suffit de s’arrêter au premier qui fonctionne.
Implémentation naïve du pseudocode :

import StateOfArtCensorshipHLNNClassifiers

protocol MovieCensorshipClassifier {
    func shouldBeCensored(movieChunk: MovieChunk) -> Bool
}

class CensorshipClassifier: MovieCensorshipClassifier {

    let hnnclassifier: StateOfArtCensorshipHLNNClassifier

    init(_ hnnclassifier: StateOfArtCensorshipHLNNClassifier) {
        self.hnnclassifier = hnnclassifier
    }
    
    func shouldBeCensored(_ movieChunk: MovieChunk) -> Bool {
        return hnnclassifier.shouldBeCensored(movieChunk)
    }
}

let lightCensorshipClassifier = CensorshipClassifier(StateOfArtCensorshipHLNNClassifier("light"))
let normalCensorshipClassifier = CensorshipClassifier(StateOfArtCensorshipHLNNClassifier("normal"))
let hardCensorshipClassifier = CensorshipClassifier(StateOfArtCensorshipHLNNClassifier("hard"))

let classifiers = [lightCensorshipClassifier, normalCensorshipClassifier, hardCensorshipClassifier]

let movie = Movie("Red Jah rising")
for chunk in movie.chunks {
    for classifier in classifiers {
        if classifier.shouldBeCensored(chunk) == true {
            print("Should censor movie chunk: \(chunk), reported by \(classifier)")
        }
   }
}

En général, la solution avec un tableau de classificateurs n’est pas si mauvaise, cependant ! Imaginons que nous ne puissions pas créer de tableau, nous avons la possibilité de créer une seule entité classificatrice, qui détermine déjà le type de censure pour un fragment de film. De telles restrictions sont possibles lors du développement d’une bibliothèque qui étend les fonctionnalités de l’application (plugin).
Utilisons le modèle de décorateur – Ajoutons une référence au classificateur suivant de la chaîne à la classe du classificateur et arrêtons le processus de vérification à la première classification réussie.
Ainsi, nous implémentons le modèle de chaîne de responsabilité :

import StateOfArtCensorshipHLNNClassifiers

protocol MovieCensorshipClassifier {
    func shouldBeCensored(movieChunk: MovieChunk) -> Bool
}

class CensorshipClassifier: MovieCensorshipClassifier {

    let nextClassifier: CensorshipClassifier?
    let hnnclassifier: StateOfArtCensorshipHLNNClassifier

    init(_ hnnclassifier: StateOfArtCensorshipHLNNClassifier, nextClassifier: CensorshipClassifiers?) {
            self.nextClassifier = nextClassifier
            self.hnnclassifier = hnnclassifier
    }
    
    func shouldBeCensored(_ movieChunk: MovieChunk) -> Bool {
        let result = hnnclassifier.shouldBeCensored(movieChunk)
        
        print("Should censor movie chunk: \(movieChunk), reported by \(self)")
        
        if result == true {
                return true
        }
        else {
                return nextClassifier?.shouldBeCensored(movieChunk) ?? false
        }
    }
}

let censorshipClassifier = CensorshipClassifier(StateOfArtCensorshipHLNNClassifier("light"), nextClassifier: CensorshipClassifier(StateOfArtCensorshipHLNNClassifier("normal", nextClassifier: CensorshipClassifier(StateOfArtCensorshipHLNNClassifier("hard")))))

let movie = Movie("Red Jah rising")
for chunk in movie.chunks {
    censorshipClassifier.shouldBeCensored(chunk)
}

Références

https://refactoring.guru/ru/ modèles de conception/chaîne de responsabilité

Code source

https://gitlab.com/demensdeum/patterns/< /p>

Décorateur de motifs

Le modèle Decorator fait référence à des modèles de conception structurelle.

Le décorateur est utilisé comme alternative à l’héritage pour étendre les fonctionnalités des classes.
Il existe une tâche consistant à étendre les fonctionnalités de l’application en fonction du type de produit. Le client a besoin de trois types de produits : Basique, Professionnel, Ultime.
De base– compte le nombre de caractères, Professionnel – capacités Basic + imprime le texte en majuscules, Ultimate – Basic + Professional + imprime le texte disant ULTIMATE.
Nous l’implémentons en utilisant l’héritage :

protocol Feature {
	func textOperation(text: String)
}

class BasicVersionFeature: Feature {
	func textOperation(text: String) {
		print("\(text.count)")
	}
}

class ProfessionalVersionFeature: BasicVersionFeature {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("\(text.uppercased())")
	}
}

class UltimateVersionFeature: ProfessionalVersionFeature {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("ULTIMATE: \(text)")
	}
}

let textToFormat = "Hello Decorator"

let basicProduct = BasicVersionFeature()
basicProduct.textOperation(text: textToFormat)

let professionalProduct = ProfessionalVersionFeature()
professionalProduct.textOperation(text: textToFormat)

let ultimateProduct = UltimateVersionFeature()
ultimateProduct.textOperation(text: textToFormat)

Il est désormais nécessaire de mettre en œuvre le produit « Ultimate Light » – 8211 ; Basic + Ultimate mais sans les capacités de la version Professionnelle. Le premier OH! arrive, parce que… vous devrez créer une classe distincte pour une tâche aussi simple et dupliquer le code.
Continuons l’implémentation en utilisant l’héritage :

protocol Feature {
	func textOperation(text: String)
}

class BasicVersionFeature: Feature {
	func textOperation(text: String) {
		print("\(text.count)")
	}
}

class ProfessionalVersionFeature: BasicVersionFeature {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("\(text.uppercased())")
	}
}

class UltimateVersionFeature: ProfessionalVersionFeature {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("ULTIMATE: \(text)")
	}
}

class UltimateLightVersionFeature: BasicVersionFeature {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("ULTIMATE: \(text)")	
	}
}

let textToFormat = "Hello Decorator"

let basicProduct = BasicVersionFeature()
basicProduct.textOperation(text: textToFormat)

let professionalProduct = ProfessionalVersionFeature()
professionalProduct.textOperation(text: textToFormat)

let ultimateProduct = UltimateVersionFeature()
ultimateProduct.textOperation(text: textToFormat)

let ultimateLightProduct = UltimateLightVersionFeature()
ultimateLightProduct.textOperation(text: textToFormat)

L’exemple peut être développé davantage pour plus de clarté, mais même maintenant, la complexité de la prise en charge d’un système basé sur une base d’héritage est visible – encombrant et manque de flexibilité.
Un décorateur est un ensemble de protocoles décrivant la fonctionnalité, une classe abstraite contenant une référence à une instance concrète enfant de la classe décorateur qui étend la fonctionnalité.
Réécrivons l’exemple ci-dessus en utilisant le modèle :

protocol Feature {
	func textOperation(text: String)
}

class FeatureDecorator: Feature {
	private var feature: Feature?
	
	init(feature: Feature? = nil) {
		self.feature = feature
	}
	
	func textOperation(text: String) {
		feature?.textOperation(text: text)
	}
}

class BasicVersionFeature: FeatureDecorator {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("\(text.count)")
	}
}

class ProfessionalVersionFeature: FeatureDecorator {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("\(text.uppercased())")
	}
}

class UltimateVersionFeature: FeatureDecorator {
	override func textOperation(text: String) {
		super.textOperation(text: text)
		print("ULTIMATE: \(text)")
	}
}

let textToFormat = "Hello Decorator"

let basicProduct = BasicVersionFeature(feature: UltimateVersionFeature())
basicProduct.textOperation(text: textToFormat)

let professionalProduct = ProfessionalVersionFeature(feature: UltimateVersionFeature())
professionalProduct.textOperation(text: textToFormat)

let ultimateProduct = BasicVersionFeature(feature: UltimateVersionFeature(feature: ProfessionalVersionFeature()))
ultimateProduct.textOperation(text: textToFormat)

let ultimateLightProduct = BasicVersionFeature(feature: UltimateVersionFeature())
ultimateLightProduct.textOperation(text: textToFormat)

Nous pouvons désormais créer des variantes de n’importe quel type de produit – il suffit d’initialiser les types combinés au stade du lancement de l’application ; l’exemple ci-dessous est la création de la version Ultimate + Professional :

ultimateProfessionalProduct.textOperation(text: textToFormat)

Sources

https://refactoring.guru/ru/design-patterns/decorator

Code source

https://gitlab.com/demensdeum/patterns

Médiateur de modèle

Le modèle Mediator fait référence à des modèles de conception comportementale.

Un jour, vous recevez une commande pour développer une application de blague – l’utilisateur appuie sur un bouton au milieu de l’écran et un drôle de bruit de canard cancan se fait entendre.
Une fois téléchargée sur l’App Store, l’application fait un carton : tout le monde cancane avec votre application, Elon Musk cancane sur son Instagram lors du prochain lancement d’un tunnel à très grande vitesse sur Mars, Hillary Clinton devance Donald Trump lors du débat. et remporte les élections en Ukraine, succès !
L’implémentation naïve de l’application ressemble à ceci :

class DuckButton {
    func didPress() {
        print("quack!")
    }
}

let duckButton = DuckButton()
duckButton.didPress()

Ensuite, vous décidez d’ajouter le son d’un chien qui aboie, pour cela, vous devez afficher deux boutons pour sélectionner le son – avec un canard et un chien. Créons deux classes de boutons : DuckButton et DogButton.
Changez le code :

class DuckButton {
    func didPress() {
        print("quack!")
    }
}

class DogButton {
    func didPress() {
        print("bark!")
    }
}

let duckButton = DuckButton()
duckButton.didPress()

let dogButton = DogButton()
dogButton.didPress()

Après un autre succès, nous ajoutons le son d’un cri de cochon, il y a maintenant trois classes de boutons :

class DuckButton {
    func didPress() {
        print("quack!")
    }
}

class DogButton {
    func didPress() {
        print("bark!")
    }
}

class PigButton {
    func didPress() {
        print("oink!")
    }
}

let duckButton = DuckButton()
duckButton.didPress()

let dogButton = DogButton()
dogButton.didPress()

let pigButton = PigButton()
pigButton.didPress()

Les utilisateurs se plaignent du fait que les sons se chevauchent.
Nous ajoutons une vérification pour éviter que cela ne se produise, et en même temps nous présentons les classes les unes aux autres :

class DuckButton {
    var isMakingSound = false
    var dogButton: DogButton?
    var pigButton: PigButton?
    func didPress() {
        guard dogButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false else { return }
        isMakingSound = true
        print("quack!")
        isMakingSound = false
    }
}

class DogButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var pigButton: PigButton?
    func didPress() {
        guard duckButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false else { return }
        isMakingSound = true
        print("bark!")
        isMakingSound = false
    }
}

class PigButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var dogButton: DogButton?
    func didPress() {
        guard duckButton?.isMakingSound ?? false == false && 
                dogButton?.isMakingSound ?? false == false else { return }
        isMakingSound = true
        print("oink!")
        isMakingSound = false
    }
}

let duckButton = DuckButton()
duckButton.didPress()

let dogButton = DogButton()
dogButton.didPress()

let pigButton = PigButton()
pigButton.didPress()

Suite au succès de votre candidature, le gouvernement décide de promulguer une loi selon laquelle les cancans, aboiements et grognements sur les appareils mobiles ne peuvent être effectués que de 9h00 à 15h00 le reste de la semaine ; Dans ce cas, l’utilisateur de votre application risque une peine de 5 ans de prison pour production sonore obscène à l’aide d’appareils électroniques personnels.
Changez le code :

import Foundation

extension Date {
    func mobileDeviceAllowedSoundTime() -> Bool {
        let hour = Calendar.current.component(.hour, from: self)
        let weekend = Calendar.current.isDateInWeekend(self)
        
        let result = hour >= 9 && hour <= 14 && weekend == false
        
        return result
    }
}

class DuckButton {
    var isMakingSound = false
    var dogButton: DogButton?
    var pigButton: PigButton?
    func didPress() {
        guard dogButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("quack!")
        isMakingSound = false
    }
}

class DogButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var pigButton: PigButton?
    func didPress() {
        guard duckButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("bark!")
        isMakingSound = false
    }
}

class PigButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var dogButton: DogButton?
    func didPress() {
        guard duckButton?.isMakingSound ?? false == false && 
                dogButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("oink!")
        isMakingSound = false
    }
}

let duckButton = DuckButton()
let dogButton = DogButton()
let pigButton = PigButton()

duckButton.dogButton = dogButton
duckButton.pigButton = pigButton

dogButton.duckButton = duckButton
dogButton.pigButton = pigButton

pigButton.duckButton = duckButton
pigButton.dogButton = dogButton

duckButton.didPress()
dogButton.didPress()
pigButton.didPress()

Soudain, l'application de lampe de poche commence à chasser la nôtre du marché, ne la laissons pas nous vaincre et ajoutez une lampe de poche en appuyant sur le bouton « oink-oink » et sur le reste des boutons :

import Foundation

extension Date {
    func mobileDeviceAllowedSoundTime() -> Bool {
        let hour = Calendar.current.component(.hour, from: self)
        let weekend = Calendar.current.isDateInWeekend(self)
        
        let result = hour >= 9 && hour <= 14 && weekend == false
        
        return result
    }
}

class Flashlight {

    var isOn = false

    func turn(on: Bool) {
        isOn = on
    }
}

class DuckButton {
    var isMakingSound = false
    var dogButton: DogButton?
    var pigButton: PigButton?
    var flashlight: Flashlight?
    func didPress() {
        flashlight?.turn(on: true)
        guard dogButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("quack!")
        isMakingSound = false
    }
}

class DogButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var pigButton: PigButton?
    var flashlight: Flashlight?
    func didPress() {
        flashlight?.turn(on: true)
        guard duckButton?.isMakingSound ?? false == false &&
                pigButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("bark!")
        isMakingSound = false
    }
}

class PigButton {
    var isMakingSound = false
    var duckButton: DuckButton?
    var dogButton: DogButton?
    var flashlight: Flashlight?
    func didPress() {
        flashlight?.turn(on: true)
        guard duckButton?.isMakingSound ?? false == false && 
                dogButton?.isMakingSound ?? false == false &&
                 Date().mobileDeviceAllowedSoundTime() == true else { return }
        isMakingSound = true
        print("oink!")
        isMakingSound = false
    }
}

let flashlight = Flashlight()
let duckButton = DuckButton()
let dogButton = DogButton()
let pigButton = PigButton()

duckButton.dogButton = dogButton
duckButton.pigButton = pigButton
duckButton.flashlight = flashlight

dogButton.duckButton = duckButton
dogButton.pigButton = pigButton
dogButton.flashlight = flashlight

pigButton.duckButton = duckButton
pigButton.dogButton = dogButton
pigButton.flashlight = flashlight

duckButton.didPress()
dogButton.didPress()
pigButton.didPress()

En conséquence, nous avons une énorme application qui contient beaucoup de code copié-collé, les classes à l'intérieur sont reliées entre elles par un lien mort - il n'y a pas de couplage faible, un tel miracle est très difficile à maintenir et changement dans le futur en raison des fortes chances de commettre une erreur.

Utiliser le médiateur

Ajoutons une classe de médiateur intermédiaire : ApplicationController. Cette classe fournira un couplage lâche des objets, garantira la séparation des responsabilités entre les classes et éliminera le code en double.
Réécrivons :

import Foundation

class ApplicationController {

    private var isMakingSound = false
    private let flashlight = Flashlight()
    private var soundButtons: [SoundButton] = []

    func add(soundButton: SoundButton) {
        soundButtons.append(soundButton)
    }
    
    func didPress(soundButton: SoundButton) {
        flashlight.turn(on: true)
        guard Date().mobileDeviceAllowedSoundTime() && 
                isMakingSound == false else { return }
        isMakingSound = true
        soundButton.didPress()
        isMakingSound = false
    }
}

class SoundButton {
    let soundText: String
    
    init(soundText: String) {
        self.soundText = soundText
    }
    
    func didPress() {
        print(soundText)
    }
}

class Flashlight {
    var isOn = false

    func turn(on: Bool) {
        isOn = on
    }
}

extension Date {
    func mobileDeviceAllowedSoundTime() -> Bool {
        let hour = Calendar.current.component(.hour, from: self)
        let weekend = Calendar.current.isDateInWeekend(self)
        
        let result = hour >= 9 && hour <= 14 && weekend == false
        
        return result
    }
}

let applicationController = ApplicationController()
let pigButton = SoundButton(soundText: "oink!")
let dogButton = SoundButton(soundText: "bark!")
let duckButton = SoundButton(soundText: "quack!")

applicationController.add(soundButton: pigButton)
applicationController.add(soundButton: dogButton)
applicationController.add(soundButton: duckButton)

pigButton.didPress()
dogButton.didPress()
duckButton.didPress()

De nombreux articles sur les architectures d'applications d'interface utilisateur décrivent le modèle MVC et ses dérivés. Le modèle est utilisé pour travailler avec des données de logique métier, la vue ou la représentation montre des informations à l'utilisateur dans l'interface/fournit une interaction avec l'utilisateur, le contrôleur est un médiateur qui assure l'interaction des composants du système.

Sources

https://refactoring.guru/ru/design-patterns/ médiateur

Code source

https://gitlab.com/demensdeum/patterns/< /p>

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/

Modèle “Instantané”

Dans cet article, je décrirai le modèle « Snapshot » ; ou “Memento”

Ce modèle fait référence au modèle « comportemental ». modèles de conception.

Supposons que nous développions un éditeur graphique et que nous devions ajouter la possibilité d’annuler des actions sur une commande utilisateur. Il est également très important que les composants du système n’aient pas accès à l’état interne des « actions » annulées lors de l’implémentation de ce modèle ; les autres composants du système n’ont accès qu’à l’objet instantané sans possibilité de le modifier. son état interne, fournissant une interface externe claire et simple. Pour résoudre ce problème, le modèle « Snapshot » est utilisé. ou “Gardien”.

Exemple de travail “Snapshot” présenté ci-dessous :


Lorsque vous cliquez dessus, un sprite apparaît, lorsque vous cliquez sur la flèche courbée, l’action est annulée – Le sprite disparaît. L’exemple se compose de trois classes :

  1. Toile sur laquelle les sprites et l’interface graphique sont affichés.
  2. Contrôleur d’écran, il traite les clics et contrôle la logique de l’écran.
  3. Les états du canevas qui persistent à chaque modification sont annulés si nécessaire à l’aide du contrôleur d’écran.

Dans le contexte du modèle “Snapshot” les cours sont :

  1. Toile – source, les états de cette classe sont enregistrés sous forme de « instantanés », pour une restauration ultérieure sur demande. De plus, la source doit être capable de restaurer l’état lors du transfert d’un « instantané » vers celle-ci.
  2. Contrôleur – dépositaire, cette classe sait comment et quand enregistrer/annuler les états.
  3. État – instantané, une classe qui stocke l’état de la source, ainsi que des informations sur la date ou un index à partir duquel l’ordre de restauration peut être établi avec précision.

Une caractéristique importante du modèle est que seule la source doit avoir accès aux champs internes de l’état enregistré dans l’instantané ; cela est nécessaire pour protéger les instantanés des modifications venant de l’extérieur (des développeurs expérimentés qui souhaitent modifier quelque chose en contournant l’encapsulation). , brisant la logique du système). Pour implémenter l’encapsulation, des classes intégrées sont utilisées et, en C++, elles utilisent la possibilité de spécifier des classes amies. Personnellement, j’ai implémenté une version simple sans encapsulation pour Rise et en utilisant Generic lors de l’implémentation pour Swift. Dans ma version – Memento donne son état interne uniquement aux entités du même état de classe :

Sources

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

Code source

https://gitlab.com/demensdeum/patterns/< /p>

Modèle de visiteur

Dans cet article, je décrirai un modèle de conception appelé « Visiteur » ; ou “Visiteur”
Ce modèle appartient au groupe des Modèles comportementaux.

Trouvons un problème

Ce modèle est principalement utilisé pour contourner la limitation de l’envoi unique dans les langues à liaison précoce.

Alice X par NFGPhoto (CC-2.0)
Créons une classe/protocole abstrait Band, créons une sous-classe de MurpleDeep, créons une classe Visitor avec deux méthodes – un pour afficher n’importe quel descendant de Band sur la console, le second pour afficher n’importe quel MurpleDeep, l’essentiel est que les noms (signatures) des méthodes soient les mêmes et que les arguments ne diffèrent que par classe. En utilisant la méthode d’impression intermédiaire avec l’argument Band, nous créons une instance de Visitor et appelons la méthode de visite pour MurpleDeep.
Vous trouverez ci-dessous le code en Kotlin :

Le résultat sera “Ceci est la classe Band

Comment est-ce possible ?!

La raison pour laquelle cela se produit est décrite avec des mots intelligents dans de nombreux articles, y compris en russe, mais je vous suggère d’imaginer comment le compilateur voit le code, peut-être que tout deviendra clair tout de suite :

Résoudre le problème

Il existe de nombreuses solutions pour résoudre ce problème. Nous examinerons ensuite une solution utilisant le modèle Visiteur.
Nous ajoutons la méthode accept avec l’argument Visitor à la classe/protocole abstrait, appelons Visitors.visit(this) à l’intérieur de la méthode, puis ajoutons une substitution/implémentation de la méthode accept à la classe MurpleDeep, violant de manière décisive et calme DRY, en écrivant à nouveau visiteur.visit(this).< br />Code final :

Sources

https://refactoring.guru/ru/ modèles de conception/double expédition des visiteurs

Code source

https://gitlab.com/demensdeum/patterns

Modèle de poids mouche

Dans cet article, je décrirai le modèle structurel « Léger » ; ou « Opportuniste » (Poids mouche)
Ce modèle appartient au groupe des Modèles structurels.

Regardons un exemple du fonctionnement du modèle ci-dessous :


Pourquoi est-ce nécessaire ? Pour économiser de la RAM. Je suis d’accord qu’à l’heure de l’utilisation généralisée de Java (qui consomme du CPU et de la mémoire pour rien), ce n’est plus si important, mais cela vaut la peine d’être utilisé.
Dans l’exemple ci-dessus, seuls 40 objets sont générés, mais si vous augmentez le nombre à 120 000, la consommation de mémoire augmentera en conséquence.
Examinons la consommation de mémoire sans utiliser le modèle flyweight dans le navigateur Chromium :

Sans utiliser de modèle, la consommation de mémoire est d’environ 300 Mo.

Ajoutons maintenant un modèle à l’application et voyons la consommation de mémoire :

En utilisant le modèle, la consommation de mémoire est d’environ 200 Mo, nous avons donc économisé 100 Mo de mémoire dans l’application de test ; dans les projets sérieux, la différence peut être beaucoup plus grande.

Comment ça marche ?

Dans l’exemple ci-dessus, nous dessinons 40 chats, ou pour plus de clarté, 120 000. Chaque chat est chargé en mémoire sous forme d’image png, puis dans la plupart des rendus, il est converti en bitmap pour le rendu (en fait bmp), ceci est fait pour la vitesse, car un png compressé prend très longtemps à rendre. Sans utiliser le modèle, nous chargeons 120 000 images de chats dans la RAM et dessinons, mais lorsque nous utilisons le modèle « léger » ; nous chargeons un chat en mémoire et le dessinons 120 000 fois avec différentes positions et transparence. Toute la magie réside dans le fait que nous implémentons les coordonnées et la transparence séparément de l’image du chat ; lors du rendu, le rendu prend un seul chat et utilise un objet avec des coordonnées et une transparence pour un rendu correct.

À quoi ça ressemble dans le code ?

Voici des exemples pour le langage Rise< /p>

Sans utiliser de modèle :


L’image du chat est chargée séparément pour chaque objet de la boucle – catImage.

Utilisation du modèle :

Une image d’un chat est utilisée par 120 000 objets.

Où est-il utilisé ?

Utilisé dans les frameworks GUI, par exemple la « réutilisation » d’Apple. (réutilisation) Cellules du tableau UITableViewCell, ce qui élève la barrière à l’entrée pour les débutants qui ne connaissent pas ce modèle. Également couramment utilisé dans le développement de jeux.

Code source

https://gitlab.com/demensdeum/patterns/< /p>

Sources

https://refactoring.guru/ru/design-patterns/ poids mouche
http://gameprogrammingpatterns.com/flyweight.html

С++ Application Plugins

In this post I will describe an example of adding functionality to a C ++ application using plugins. The practical part of the implementation for Linux is described; the theory can be found at the links at the end of the article.

Composition over inheritance!

To begin with, we will write a plugin – a function that we will call:

#include "iostream"

using namespace std;

extern "C" void extensionEntryPoint() {
	cout << "Extension entry point called" << endl;
};

Next, we will build the plugin as a dynamic library “extension.so”, which we will connect in the future:
clang++ -shared -fPIC extension.cpp -o extension.so

Next we write the main application that will load the file “extension.so”, look for a pointer to the function “extensionEntryPoint” there, and call it, typing errors if necessary:

#include "iostream"
#include "dlfcn.h"

using namespace std;

typedef void (*VoidFunctionPointer)();	

int main (int argc, char *argv[]) {

	cout << "C++ Plugins Example" << endl;

	auto extensionHandle = dlopen("./extension.so", RTLD_LAZY);
	if (!extensionHandle) {
		string errorString = dlerror();
		throw runtime_error(errorString);
	}

	auto functionPointer = VoidFunctionPointer();
	functionPointer = (VoidFunctionPointer) dlsym(extensionHandle, "extensionEntryPoint");
	auto dlsymError = dlerror();
 	if (dlsymError) {
		string errorString = dlerror();
		throw runtime_error(errorString);
 	}

	functionPointer();

	exit(0);
} 

The dlopen function returns a handler for working with a dynamic library; dlsym function returns a pointer to the required function by string; dlerror contains a pointer to the string with the error text, if any.

Next, build the main application, copy the file of the dynamic library in the folder with it and run. The output should be the “Extension entry point called”

Difficult moments include the lack of a single standard for working with dynamic libraries, because of this there is a need to export the function to a relatively global scope with extern C; the difference in working with different operating systems associated with this subtlety of work; the lack of a C ++ interface to implement OOP approach to working with dynamic libraries, however, there are open-source wrappers, for example m-renaud/libdlibxx

Example Source Code

https://gitlab.com/demensdeum/cpppluginsexample

Documents

http://man7.org/linux/man-pages/man3/dlopen.3.htm
https://gist.github.com/tailriver/30bf0c943325330b7b6a
https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work

Flutter comme Michelle

[Ressentez la puissance de l’intelligence artificielle]
Dans cet article, je vais vous expliquer comment prédire l’avenir.

Dans les statistiques, il existe une classe de problèmes : analyse de séries chronologiques. Ayant une date et la valeur d’une certaine variable, vous pouvez prédire la valeur de cette variable dans le futur.
Au début, je voulais implémenter une solution à ce problème en utilisant TensorFlow, mais j’ai trouvé la bibliothèque Prophète par Facebook.
Prophet vous permet de faire une prévision basée sur des données (csv) contenant des colonnes de date (ds) et de valeur variable (y). Vous pouvez découvrir comment l’utiliser dans la documentation du site officiel dans la section Démarrage rapide
En tant qu’ensemble de données, j’ai utilisé un téléchargement CSV depuis le site https://www.investing.com, lors de l’implémentation, j’ai utilisé Langage R et API Prophète pour lui. J’ai beaucoup aimé R, car sa syntaxe simplifie le travail avec de grandes quantités de données, permet d’écrire plus simplement et de faire moins d’erreurs qu’en travaillant avec des langages conventionnels (Python), puisqu’il faudrait travailler avec des expressions lambda, et dans R vous avez déjà toutes les expressions lambda .
Afin de ne pas préparer les données pour le traitement, j’ai utilisé le package anytime, qui peut convertir des chaînes en date, sans prétraitement. La conversion des chaînes monétaires en nombres s’effectue à l’aide du package readr .

En conséquence, j’ai reçu une prévision selon laquelle Bitcoin coûtera 8 400 $ d’ici la fin de 2019 et le taux de change du dollar sera de 61 roubles. Faut-il croire ces prévisions ? Personnellement, je pense que ça n’en vaut pas la peine, parce que… Vous ne pouvez pas utiliser de méthodes mathématiques sans en comprendre l’essence.

Sources

https:// facebook.github.io/prophet
https://habr.com/company/ods/blog/323730/
https://www.r-project.org/

Code source

https://gitlab.com/demensdeum/MachineLearning/tree/master/4prophet

Tesla parle

Dans cet article, je décrirai le processus de création d’un générateur de devis.

TL;DR

Pour la formation et la génération de texte &#8211 ; utilisez la bibliothèque textgenrnn, pour filtrer les phrases, vous devez utiliser la vérification orthographique à l’aide du hunspell et ses bibliothèques pour C/python. Après une formation à Colaboratory, vous pouvez commencer à générer du texte. Environ 90 % du texte sera complètement illisible, cependant, les 10 % restants contiendront un peu de sens, et avec une modification manuelle, les phrases auront l’air plutôt bien.
Le moyen le plus simple est de lancer un réseau neuronal prêt à l’emploi dans Colaboratory :
https://colab.research.google.com/drive/1-wbZMmxvsm3SoclJv11villo9VbUesbc(s’ouvre dans un nouvel onglet)”>https://colab.research.google.com/drive/1-wbZMmxvsm3SoclJv11villo9VbUesbc

Code source

https://gitlab.com/demensdeum/MachineLearning/tree/master/3quotesGenerator

Sources

https://karpathy.github.io/2015/05/21/rnn-effectiveness/https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5dhttps://minimaxir.com/2018/05/text-neural-networks/
https://github.com/wooorm/dictionaries (opens in a new tab)” href=”https://minimaxir.com/2018/05/text-neural-networks/” target=”_blank”>https://minimaxir.com/2018/05/text-neural-networks/
https://karpathy.github.io/2015/05/21/rnn-effectiveness/
https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5d
https://karpathy.github.io/2015/05/21/rnn-effectiveness/ (opens in a new tab)” href=”https://karpathy.github.io/2015/05/21/rnn-effectiveness/” target=”_blank”>https://karpathy.github.io/2015/05/21/rnn-effectiveness/
https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5d
https://karpathy.github.io/2015/05/21/rnn-effectiveness/https://medium.com/deep-learning-turkey/google-colab-free-gpu-tutorial-e113627b9f5dhttps://github.com/wooorm/dictionaries

” rel=”noopener” target=”_blank”>https://github.com/wooorm/dictionaries (opens in a new tab)”>https://github.com/wooorm/dictionaries

Combien d’erreurs as-tu là ?

Sur Hacker News, j’ai trouvé un article très intéressant dans lequel l’auteur suggère d’utiliser le Méthode Petersen-Lincoln, utilisée par les biologistes pour compter la population d’oiseaux, de singes et d’autres animaux, pour *roulement de tambour* compter les bugs dans l’application.

Insecte dans son habitat naturel &#8211 ; Observation de Bigfoot par Derek Hatfield

La méthode est très simple, on en prend deux ornithologues, ils trouvent les oiseaux d’une certaine espèce, leur tâche – déterminer la taille de la population de ces oiseaux. Les oiseaux trouvés sont marqués par les deux ornithologues, puis le nombre d’oiseaux communs est calculé, substitué dans la formule de l’indice de Lincoln et nous obtenons la taille approximative de la population.
Passons maintenant aux applications – la méthode est aussi très simple, on prend deux QA et ils trouvent des bugs dans l’application. Disons qu’un testeur a trouvé 10 bugs (E1) et que le deuxième testeur a trouvé 20 bugs (E2), maintenant nous prenons le nombre total de bugs – 3 (S), puis en utilisant la formule on obtient l’indice de Lincoln :

Il s’agit d’une prévision du nombre de bugs dans l’ensemble de l’application, dans l’exemple donné, il y a environ 66 bugs.

Exemple rapide

J’ai mis en place un banc de test pour tester la méthode, vous pouvez le voir ici :
https://paiza.io/projects/AY_9T3oaN9a-xICAx_H4qw?language=swift

Paramètres modifiables :

laissez aliceErrorFindProbability = 20 – pourcentage de bugs trouvés par QA Alice (20%)
laissez bobErrorFindProbability = 60 – pourcentage de bugs trouvés par QA Bob (60%)
soit actualBugsCount = 200 – combien de bugs y a-t-il réellement dans l’application

Lors de la dernière exécution, j’ai reçu les données suivantes :
Nombre de bugs d’estimation : 213
Nombre réel de bugs : 200

C’est-à-dire qu’il y a 200 bugs dans l’application, le Lincoln Index donne une prévision – 213 :
“Alice a trouvé 36 bugs”
“Bob a trouvé 89 bugs”
“Nombre de bogues courants : 15”

Nombre de bugs d’estimation : 213
Nombre réel de bugs : 200

Faiblesses

Cette méthode peut être utilisée pour évaluer le nombre d’erreurs dans l’application à toutes les étapes de développement ; idéalement, le nombre de bugs devrait diminuer. Les faiblesses de la méthode incluent le facteur humain, puisque le nombre de bugs trouvés par deux testeurs devrait être différent et que des bugs différents ont été trouvés, cependant il faut trouver les plus courants, sinon la méthode ne fonctionnera pas (zéro bug commun – division par zéro)< br />De plus, un concept tel que les bugs courants nécessite la présence d’un expert pour comprendre leurs points communs.

Sources

Combien d’erreurs reste-t-il à trouver ? – John D. Cook, PhD, président
The thrill of the chase – Brian Hayes

Code source

https://paiza.io/projects/AY_9T3oaN9a-xICAx_H4qw ?langue=rapide
https://gitlab.com/demensdeum/statistics/tree/master/1_BugsCountEstimation/src

Nous avons battu Malevich, carrés noirs OpenGL

Malevich arrive périodiquement à tout développeur sur OpenGL. Cela se produit de façon inattendue et hardiment, vous commencez simplement le projet et voyez un carré noir au lieu d’un merveilleux rendu:

Aujourd’hui, je décrirai la raison pour laquelle j’ai été visité par un carré noir, les problèmes trouvés à cause de quel OpenGL ne dessine rien à l’écran, et rend parfois même la fenêtre transparente.

Utilisez des outils

Pour le débogage OpenGL, deux outils m’ont aidé: renderdoc et et . RenderDoc & # 8211; Outil pour déboguer le processus de rendu OpenGL, vous pouvez tout voir & # 8211; Vertexes, shaders, textures, messages de la dette du pilote. Apitrace & # 8211; Un outil pour relever les défis d’une API graphique, fait des appels à vidage et affiche des arguments. Il y a aussi une excellente occasion de comparer deux décharges via Wdiff (ou sans, mais pas si pratique)

Vérifiez avec qui vous travaillez

J’ai un système d’exploitation Ubuntu 16.10 avec les anciennes dépendances SDL2, GLM, ASSIMM, GLEW. Dans la dernière version d’Ubuntu 18.04, j’obtiens l’assemblage du jeu Death-Mask qui ne montre rien à l’écran (seulement un carré noir). Lorsque vous utilisez le chroot et l’assemblage à 16.10 I , j’obtiens un assemblage de travail du jeu avec des graphiques .

Il semble que quelque chose soit cassé dans Ubuntu 18.04

LDD a montré le linkka aux bibliothèques identiques SDL2, GL. En conduisant une construction non travaillant dans RenderDoc, j’ai vu des ordures à l’entrée du vertex shader, mais j’avais besoin d’une confirmation plus solide. Afin de comprendre la différence entre les binares, je les ai conduits à tous les deux via apitrace . La comparaison des décharges m’a montré que l’assemblée sur un nouveau Ubunta rompt le programme des prospects dans OpenGL, en réellement des ordures là-bas:

Les matrices se rassemblent dans la bibliothèque GLM. Après avoir copier GLM à partir de 16,04 & # 8211; J’ai à nouveau obtenu la construction de travail du jeu. Le problème était la différence dans l’initialisation d’une seule matrice dans GLM 9.9.0, il est nécessaire d’indiquer clairement l’argument MAT4 (1.0f) dans le constructeur. Ayant changé l’initialisation et en rangeant l’auteur de la bibliothèque, j’ai commencé à faire Teste pour FSGL . Dans le processus d’écriture que j’ai trouvé des défauts dans FSGL, je les décrirai davantage.

Déterminez qui est dans la vie

Pour le bon travail avec OpenGL, vous devez volontairement de force demander le contexte d’une certaine version. Il recherche donc SDL2 (vous devez mettre la version strictement avant d’initialiser le contexte):


 sdl_gl_seettrtribute (sdl_gl_context_major_version,  3 );
Sdl_gl_settribute (sdl_gl_context_minor_version, 2 );
Sdl_gl_settribute (sdl_gl_context_profile_mask, sdl_gl_context_profile_core);

Par exemple, RenderDoc ne fonctionne pas avec des contextes inférieurs à 3.2. Je voudrais noter qu’après changer le contexte , il y a une forte probabilité de voir le même écran noir . Pourquoi?

Parce que le contexte d’OpenGL 3.2 doit nécessiter la présence de tampon VAO , sans lesquels 99% des conducteurs graphiques ne fonctionnent pas. Ajoutez-le facile:


 glGenverTexArrays ( 1 ,  &  vao);
Glbindvertexaray (vao);

Ne dormez pas, gel

J’ai également rencontré un problème intéressant sur Kubuntu, au lieu d’un carré noir, j’ai été montré transparent, et parfois tout a été rendu correctement. J’ai trouvé la solution à ce problème à Stack Overflow:

https://stackoverflow.com/questions/38411515/sdl2-opengl-window-appears-semi-transparent-sometimes

Le code de rendu de test FSGL était également présent sommeil (2s) ; Donc, sur le Xubuntu et Ubuntu, j’ai reçu le rendu correct et envoyé la demande endormie, mais sur Kubuntu, j’ai reçu un écran transparent dans 80% du lancement de Dolphin et 30% des lancements et du terminal. Pour résoudre ce problème, j’ai ajouté le rendu dans chaque trame, après une enquête SDlevent, comme recommandé dans la documentation.

Code de test:
https://gitlab.com/demensdeum/FSGLtests/blob/master/renderModelTest/

Parlez au conducteur

OpenGL prend en charge le canal de communication entre l’application et le pilote, pour l’activer, vous devez allumer les drapeaux GL_DEBUG_OUTPUT, GL_DEBUG_OUTPUT_SYNCHRONUS, APPIX L’ARTIPE GLDEBUGMESSAGECONTROL et TIE THE CALBACK AUX CLACK CLASS = “PL-PLE-C1” .
Un exemple d’initialisation peut être pris ici:
https://github.com/rock-core/gui-vizkit3d/blob/master/src/EnableGLDebugOperation.cpp

N’aie pas peur, regarde-le grandir

Dans cet article, je parlerai de mes mésaventures avec les pointeurs intelligents shared_ptr. Après avoir implémenté la génération de niveau supérieur dans mon jeu Death-Mask, j’ai remarqué un souvenir fuir . Chaque nouveau niveau donnait une augmentation de + 1 mégaoctet à la RAM consommée. Il est évidemment que certains objets sont restés en mémoire et ne l’ont pas libéré. Pour corriger ce fait, il a fallu mettre en œuvre la bonne mise en œuvre des ressources lorsque le niveau est surchargé, ce qui n’a apparemment pas été fait. Depuis que j’ai utilisé des pointeurs intelligents, il y avait plusieurs options pour résoudre ce problème, la première impliquait une révision manuelle du code (longue et ennuyeuse), tandis que la seconde impliquait de rechercher les capacités du débogueur lldb et le code source de libstdc++ pour la possibilité de suivre automatiquement changements de compteur.

Sur Internet, tous les conseils se résumaient à réviser manuellement le code, à le corriger et à se frapper avec des fouets après avoir trouvé la ligne de code problématique. Il a également été proposé d’implémenter son propre système de travail avec la mémoire, comme le font tous les grands projets développés depuis les années 90 et 2000, avant l’arrivée des pointeurs intelligents dans le standard C++11. J’ai essayé d’utiliser des points d’arrêt sur le constructeur d’une copie de tous les shared_ptrs, mais après plusieurs jours, rien d’utile ne s’est produit. Il y avait une idée pour ajouter la journalisation à la bibliothèque libstdc++, mais les coûts de main d’œuvre se sont avérés monstrueux.


Cowboy Bebop (1998)

La solution m’est soudainement venue à l’esprit : suivre les modifications apportées à la variable privée shared_ptr – use_count. Cela peut être fait en utilisant les points de surveillance intégrés dans lldb. Après avoir créé un shared_ptr via make_shared, les modifications apportées au compteur dans lldb peuvent être suivies à l’aide de la ligne :

regarder set var camera._M_refcount._M_pi->_M_use_count

Où « caméra » ; il s’agit d’un objet shared_ptr dont l’état du compteur doit être suivi. Bien sûr, les composants internes de shared_ptr varieront en fonction de la version de libstdc++, mais le principe général peut être compris. Après avoir installé le point de surveillance, nous lançons les applications et lisons le stacktrace de chaque changement de compteur, puis nous regardons le code (sic !), trouvons le problème et le réparons. Dans mon cas, les objets n’ont pas été libérés des tables de cache et des tables de logique de jeu. J’espère que cette méthode vous aidera à gérer les fuites lorsque vous travaillez avec shared_ptr et j’aimerai encore plus cet outil de mémoire. Bon débogage.

Exemple simple de TensorFlow

Je présente à votre attention un exemple simple de travail avec un framework pour travailler avec le Deep Learning – TensorFlow. Dans cet exemple, nous allons apprendre à un réseau de neurones à détecter les nombres positifs, négatifs et zéro. Installation de TensorFlow et CUDA Je vous informe, cette tâche n’est vraiment pas facile)

Pour résoudre les problèmes de classification, classificateurs. TensorFlow dispose de plusieurs classificateurs de haut niveau prêts à l’emploi qui nécessitent une configuration minimale pour fonctionner. Nous allons d’abord entraîner DNNClassifier en utilisant ensemble de données avec des nombres positifs, négatifs et zéro – avec les « étiquettes » correctes. Au niveau humain, un ensemble de données est un ensemble de nombres avec des résultats de classification (étiquettes) :

10 – positif
-22 – négatif
0 – zéro
42 – positif
… autres numéros avec classification

Ensuite, la formation commence, après quoi vous pouvez saisir des nombres qui n’étaient même pas inclus dans l’ensemble de données – c’est à dire. le réseau de neurones doit les identifier correctement.
Vous trouverez ci-dessous le code complet du classificateur avec un générateur d’ensembles de données pour la formation et les données d’entrée :

import tensorflowimporter itertoolsimporter aléatoirede heure heure d'importationclasse ClassifiedNumber :__number = 0__classifiedAs = 3def __init__(soi, nombre) :soi.__numéro =numéroif numéro == 0 :soi.__classifiedAs = 0 # zéroNuméro elif > 0 :soi.__classifiedAs = 1 # positifNuméro elif < 0 :soi.__classifiedAs = 2 # négatifdef number(soi) :return self.__numérodef classifiedAs(soi) :return self.__classifiedAsdef classifiedAsString(classifiedAs) :if classé comme == 0 :retour "Zéro"elif classéAs == 1 :retour "Positif"elif classéAs == 2 :return "Negative"def trainDatasetFunction() :trainNumbers = []trainNumberLabels = []pour i in plage(-1000, 1001) :numéro = ClassifiedNumber(i)trainNumbers.append(number.number())trainNumberLabels.append(number.classifiedAs())return ( {"number" : trainNumbers } , trainNumberÉtiquettes)def inputDatasetFunction() :global randomSeedrandom.seed(randomSeed) # pour obtenir le même résultatnombres = []pour i in plage(0, 4) :nombres.append(random.randint(-9999999, 9999999))return {"number" : nombres }def main() :print("Test de classificateur de nombres TensorFlow Positif-Négatif-Zéro par demensdeum 2017 (demensdeum@gmail. com)")maximalClassesCount = len(set< /span>(trainDatasetFunction()[1])) + 1numberFeature = tensorflow.feature_column. numeric_column("numéro")classificateur = tensorflow.estimateur. DNNClassifier(feature_columns = [numberFeature], Hidden_units = [10, 20, 10], n_classes = maximalClassesCount)générateur = classificateur.train(input_fn = trainDatasetFunction, étapes = 1000).predict(input_fn =  inputDatasetFunction)inputDataset = inputDatasetFunction()résultats = list(itertools. islice(générateur, len(inputDatasetFunction()["numéro"])))je = 0pour résultat dans résultats :imprimer("numéro : %d classé comme %s" % (inputDataset["number"][i], classifiéAsString(result["class_ids"][0 ])))je += 1randomSeed = time()principal()

Tout commence dans la méthode main(), nous définissons la colonne numérique avec laquelle le classificateur fonctionnera – tensorflow.feature_column.numeric_column(“number”) Ensuite, les paramètres du classificateur sont définis. Il est inutile de décrire les arguments d’initialisation actuels, car l’API change chaque jour, et vous devez absolument consulter la documentation de la version installée de TensorFlow et ne pas vous fier à des manuels obsolètes.

Ensuite, la formation est lancée en indiquant une fonction qui renvoie un ensemble de données de nombres de -1 000 à 1 000 (trainDatasetFunction), avec la classification correcte de ces nombres en fonction du positif, du négatif ou du zéro. Ensuite, nous soumettons en entrée les nombres qui ne figuraient pas dans l’ensemble de données de formation – ; aléatoire de -9999999 à 9999999 (inputDatasetFunction) pour les classer.

Enfin, nous lançons des itérations en fonction du nombre de données d’entrée (itertools.islice), imprimons le résultat, l’exécutons et sommes surpris :

numéro : 4063470 classé comme positifnuméro : 6006715 classé Positifnuméro : -5367127 classé comme négatifnuméro : -7834276 classé comme négatif

IL EST VIVANT

Pour être honnête, je suis toujours un peu surpris que le classificateur *comprenne* même les nombres que je ne lui ai pas enseignés. J’espère qu’à l’avenir je comprendrai le sujet de l’apprentissage automatique plus en détail et qu’il y aura plus de tutoriels.

GitLab :
https://gitlab.com/demensdeum/MachineLearning

Liens :
https://developers.googleblog.com/2017/09/introducing-tensorflow-datasets.html
https://www.tensorflow.org/versions/master/api_docs/python/tf/estimator/DNNClassifier

Briser le Bitcoin

Cette note n’est pas un appel à l’action ; je vais décrire ici les aspects faibles et potentiellement dangereux de la technologie Bitcoin et blockchain.

Centre vulnérable

Le principe de fonctionnement du Bitcoin et de la blockchain est de stocker et de modifier une base de données commune, dont une copie complète est stockée par chaque participant au réseau. Le système semble décentralisé parce que… il n’existe pas d’organisation/serveur unique sur lequel la base de données est stockée. Aussi, la décentralisation est présentée comme le principal avantage de la blockchain ; elle garantit que rien n’arrivera à vos bitcoins à votre insu.


Le principe du blocage de la peste de Elkin

Pour que la blockchain fonctionne, il est nécessaire de s’assurer que chaque utilisateur télécharge la dernière copie de la base de données blockchain et travaille avec elle selon certaines règles. Ces règles incluent la mise en œuvre du principe du minage de Bitcoin, consistant à recevoir un pourcentage de chaque transaction lors de la confirmation (frais de transaction) du transfert de fonds d’un portefeuille à un autre. L’utilisateur ne peut pas tirer 1 000 000 de bitcoins pour lui-même et acheter quelque chose avec, car… Pour les autres utilisateurs, le montant d’argent sur son compte restera inchangé. La possibilité de retirer des fonds du portefeuille de quelqu’un d’autre uniquement dans votre propre base de données est également exclue. ce changement ne sera pas reflété chez les autres utilisateurs de Bitcoin et sera ignoré.
La vulnérabilité de l’implémentation actuelle est que le portefeuille Bitcoin est situé sur le serveur github qui bloque complètement les slogans publicitaires sur la décentralisation. Pas de téléchargement de portefeuille à partir d’un seul centre – Sur le site Web du développeur, il est impossible de travailler avec Bitcoin, c’est-à-dire qu’à tout moment, les développeurs ont un contrôle total sur le réseau. Ainsi, la technologie blockchain elle-même est décentralisée, mais le client permettant de travailler avec le réseau est téléchargé à partir d’un centre unique.
Scénario d’attaque – Disons qu’un code a été ajouté au portefeuille pour retirer tous les fonds et les retirer sur le compte de tiers, après quoi tout utilisateur de la dernière version du portefeuille perdra automatiquement tous les bitcoins (sans possibilité de récupération). Je doute que de nombreux propriétaires de portefeuilles le vérifient et le construisent à partir du code source, donc les conséquences d’une telle attaque affecteront la plupart des utilisateurs.

La majorité décide

Blockchain est un réseau p2p décentralisé ; toutes les transactions sont automatiquement confirmées par les utilisateurs eux-mêmes. Scénario d’attaque – il est nécessaire d’obtenir 51 % du réseau pour ignorer les confirmations des 49 % restants, après quoi l’attaquant prend le contrôle total du Bitcoin/blockchain. Ceci peut être réalisé en connectant une puissance de calcul qui chevauche le reste. Ce scénario d’attaque est connu sous le nom d’attaque à 51 %.

Devinez-moi si vous pouvez

Lorsque vous lancez le portefeuille pour la première fois, l’ordinateur génère une paire – clé privée et publique pour assurer son bon fonctionnement. Le caractère unique de ces clés est extrêmement élevé, mais il existe une option permettant de générer des clés en utilisant le mot de code – le soi-disant “portefeuille cérébral“. Une personne stocke les clés dans sa tête ; elle n’a pas besoin de sauvegarder le fichier wallet.dat, car A tout moment, les clés peuvent être régénérées à l’aide de ce mot de code. Scénario d’attaque – l’attaquant sélectionne ou apprend le mot de code, génère une paire de clés privée-publique et prend le contrôle du portefeuille.

Copiez simplement

La paire de clés privée-publique est contenue dans le fichier wallet.dat. Tout logiciel ayant accès à ce fichier – a accès à un portefeuille Bitcoin. La protection contre une telle attaque consiste en l’ajout d’un mot de code que l’utilisateur devra mémoriser et saisir pour toutes les transactions avec le portefeuille. Après avoir ajouté le mot de passe, l’attaquant devra disposer de wallet.dat et du mot de passe pour prendre le contrôle total.
Il convient également d’ajouter que lorsque vous saisissez un mot de code, celui-ci entre dans la mémoire de l’ordinateur. Ainsi, toute vulnérabilité matérielle et/ou logicielle permettant de lire la mémoire de *quelqu’un d’autre* permettra à un logiciel antivirus de lire ce mot de code.

Erreur système

Le piratage des algorithmes de chiffrement de Bitcoin entraînera instantanément sa mort. Supposons qu’une erreur soit commise dans la mise en œuvre des algorithmes, l’attaquant qui la découvre prend le contrôle total ou partiel de la blockchain. De plus, les algorithmes de cryptage utilisés dans Bitcoin ne sont pas protégés contre le piratage par les futurs ordinateurs quantiques, leur apparition et leur mise en œuvre d’algorithmes quantiques – mettra fin à la mise en œuvre actuelle du Bitcoin. Cependant, ce problème peut être résolu en passant à des algorithmes de chiffrement post-quantique.

WebGL + SDL + Emscript

J’ai fini par porter Mika sur WebGL en utilisant SDL 1 et Emscripten.

Ensuite, je décrirai ce qui devait être modifié dans le code pour que la construction en JavaScript se termine avec succès.

  1. Utilisez SDL 1 au lieu de SDL 2. Il existe actuellement un portage SDL 2 pour emscripten, mais j’ai trouvé plus approprié d’utiliser le SDL 1 intégré à emscripten. Le contexte n’est pas initialisé dans la fenêtre, mais à l’aide de SDL_SetVideoMode et du flag SDL_OPENGL. Le tampon est dessiné à l’aide de la commande SDL_GL_SwapBuffers()
  2. En raison de la manière dont JavaScript boucle – le rendu est placé dans une fonction distincte et son appel périodique est effectué à l’aide de la fonction emscripten_set_main_loop
  3. L’assemblage doit également être effectué avec la clé “-s FULL_ES2=1
  4. J’ai dû abandonner la bibliothèque assimp, charger le modèle depuis le système de fichiers et charger la texture depuis le disque. Tous les tampons nécessaires ont été chargés sur la version de bureau et insérés dans le fichier c-header pour l’assemblage à l’aide d’emscripten.

Code :
https://github.com/demensdeum/OpenGLES3-Experiments/tree/master/9-sdl-gles-obj-textured-assimp-miku-webgl/mikuWebGL

Articles :
http://blog.scottlogic.com/2014/03/12/native-code-emscripten-webgl-simmer-gently.html
https://kripken.github.io/emscripten-site/docs/porting/multimedia_and_graphics/OpenGL-support.html

Modèle :
https://sketchfab.com/models/7310aaeb8370428e966bdcff414273e7

Il n’y a que Miku

Le résultat du travail sur la bibliothèque FSGL avec OpenGL ES et le code :

Ensuite, je décrirai comment tout a été programmé, divers problèmes intéressants ont été résolus.

Tout d’abord, nous allons initialiser le contexte OpenGL ES, comme je l’ai écrit dans le post précédent. De plus, nous ne considérerons que le rendu et une brève description du code.

La Matrix vous surveille

Cette figure de Miku dans la vidéo est constituée de triangles. Pour dessiner un triangle dans OpenGL, vous devez spécifier trois points avec les coordonnées x, y, z. en coordonnées 2D du contexte OpenGL.
Puisque nous devons dessiner une figure contenant des coordonnées 3D, nous devons utiliser une matrice de projection. Nous devons également faire pivoter, zoomer ou faire tout ce que nous voulons faire avec le modèle. À cette fin, la matrice de modèle est utilisée. Il n’y a pas de concept de caméra dans OpenGL ; en fait, les objets tournent autour d’une caméra statique. Pour cela, une matrice de vues est utilisée.

Pour simplifier la mise en œuvre d’OpenGL ES – il ne contient pas de données matricielles. Vous pouvez utiliser des bibliothèques qui ajoutent des fonctionnalités manquantes, par exemple GLM.

Shaders

Afin de permettre au développeur de dessiner n’importe quoi, et de quelque manière que ce soit, OpenGL ES doit implémenter des vertex et fragment shaders. Le vertex shader doit recevoir les coordonnées de rendu en entrée, effectuer des transformations à l’aide de matrices et transmettre les coordonnées à gl_Position. Fragment ou pixel shader – dessine déjà la couleur/texture, applique une superposition, etc.

J’ai écrit des shaders en GLSL. Dans mon implémentation actuelle, les shaders sont intégrés directement dans le code de l’application principale sous forme de chaînes C.

Tampons

Le vertex buffer contient les coordonnées des sommets (vertices) ; ce tampon contient également les coordonnées pour la texturation et d’autres données nécessaires aux shaders. Après avoir généré le vertex buffer, vous devez lier le pointeur aux données du vertex shader. Cela se fait avec la commande glVertexAttribPointer, où vous devez spécifier le nombre d’éléments, un pointeur vers le début des données et la taille du pas qui sera utilisé pour parcourir le tampon. Dans mon implémentation, la liaison des coordonnées de sommet et des coordonnées de texture pour le pixel shader est effectuée. Cependant, il convient de dire que le transfert des données (coordonnées de texture) vers le fragment shader s’effectue via le vertex shader. Pour y parvenir, les coordonnées sont déclarées en utilisant variant.

Pour qu’OpenGL sache dans quel ordre dessiner les points des triangles – vous aurez besoin d’un tampon d’index (index). Le tampon d’index contient le numéro de sommet dans le tableau ; en utilisant trois de ces indices, un triangle est obtenu.

Textures

Vous devez d’abord charger/générer une texture pour OpenGL. Pour cela j’ai utilisé SDL_LoadBMP, la texture est chargée depuis un fichier bmp. Cependant, il convient de noter que seuls les BMP 24 bits conviennent et que les couleurs qu’ils contiennent ne sont pas stockées dans l’ordre RVB habituel, mais en BGR. Autrement dit, après le chargement, vous devez remplacer le canal rouge par un bleu.
Les coordonnées de texture sont spécifiées au format UV< /a>, c’est-à-dire qu’il vous suffit de transférer deux coordonnées. La sortie de texture est effectuée dans le fragment shader. Pour ce faire, vous devez lier la texture dans un fragment shader.

Rien de plus

Puisque, selon nos instructions, OpenGL dessine de la 3D à la 2D – puis pour implémenter la profondeur et sélectionner des triangles invisibles – vous devez utiliser l’élimination et un tampon de profondeur (Z-Buffer). Dans mon implémentation, j’ai réussi à éviter la génération manuelle du tampon de profondeur à l’aide de deux commandes : glEnable(GL_DEPTH_TEST); et sélections glEnable(GL_CULL_FACE);
Assurez-vous également de vérifier que le plan proche de la matrice de projection est supérieur à zéro, car vérifier la profondeur avec un plan proche nul ne fonctionnera pas.

Rendu

Pour remplir le tampon de vertex, le tampon d’index avec quelque chose de conscient, par exemple le modèle Miku, vous devez charger ce modèle. Pour cela, j’ai utilisé la bibliothèque assimp. Miku a été placé dans un fichier au format Wavefront OBJ, chargé à l’aide de assimp, et la conversion des données d’assimp en tampons de sommets et d’index a été implémentée.

Le rendu s’effectue en plusieurs étapes :

  1. Faire pivoter Miku à l’aide de la rotation de la matrice du modèle
  2. Effacer l’écran et le tampon de profondeur
  3. Dessiner des triangles à l’aide de la commande glDrawElements.

Prochaine étape – Implémentation du rendu en WebGL à l’aide d’Emscripten.

Code source :
https://github.com/demensdeum/OpenGLES3-Experiments/tree/master/8-sdl-gles-obj-textured-assimp-miku
Modèle :
https://sketchfab.com/models/7310aaeb8370428e966bdcff414273e7

 

Projetez-le

Après avoir dessiné une théière rouge en 3D, je considère qu’il est de mon devoir de décrire brièvement comment cela se fait.

L’OpenGL moderne ne dessine pas en 3D, il dessine uniquement des triangles, des points, etc. en coordonnées d’écran 2D.
Pour générer au moins quelque chose en utilisant OpenGL, vous devez fournir un vertex buffer, écrire un vertex shader, ajouter toutes les matrices nécessaires (projection, modèle, vue) au vertex shader,associer toutes les données d’entrée à le shader, appelez la méthode de rendu en OpenGL. Est-ce que ça a l’air simple ?


Ok, qu’est-ce qu’un vertex buffer ? Liste des coordonnées à tracer (x, y, z)
Le vertex shader indique au GPU quelles coordonnées dessiner.
Le pixel shader indique quoi dessiner (couleur, texture, mélange, etc.)
Les matrices traduisent les coordonnées 3D en coordonnées OpenGL 2D qu’elles peuvent restituer

Dans les articles suivants, je fournirai des exemples de code et des résultats.

SDL2 – OpenGL ES

I love Panda3D game engine. But right now this engine is very hard to compile and debug on Microsoft Windows operation system. So as I said some time ago, I begin to develop my own graphics library. Right now it’s based on OpenGL ES and SDL2.
In this article I am going to tell how to initialize OpenGL ES context and how SDL2 helps in this task. We are going to show nothing.

King Nothing

First of all you need to install OpenGL ES3 – GLES 3 libraries. This operation is platform dependant, for Ubuntu Linux you can just type sudo apt-get install libgles2-mesa-dev. To work with OpenGL you need to initialize OpenGL context. There is many ways to do that, by using one of libraries – SDL2, GLFW, GLFM etc. Actually there is no one right way to initialize OpenGL context, but I chose SDL2 because it’s cross-platform solution, code will look same for Windows/*nix/HTML5/iOS/Android/etc.

To install sdl2 on Ubuntu use this command sudo apt-get install libsdl2-dev

So here is OpenGL context initialization code with SDL2:

    SDL_Window *window = SDL_CreateWindow(
            "SDL2 - OGLES",
            SDL_WINDOWPOS_UNDEFINED,
            SDL_WINDOWPOS_UNDEFINED,
            640,
            480,
            SDL_WINDOW_OPENGL
            );
	    

    SDL_GLContext glContext = SDL_GL_CreateContext(window);

After that, you can use any OpenGL calls in that context.

Here is example code for this article:
https://github.com/demensdeum/OpenGLES3-Experiments/tree/master/3sdl-gles
https://github.com/demensdeum/OpenGLES3-Experiments/blob/master/3sdl-gles/sdlgles.cpp

You can build and test it with command cmake . && make && ./SDLGles

Piratage quantique RSA

L’autre jour, j’ai écrit mon implémentation de l’algorithme de chiffrement à clé publique RSA. J’ai également fait un simple hack de cet algorithme, j’ai donc voulu écrire une courte note sur ce sujet. La résistance à la falsification de RSA repose sur un problème de factorisation. Factorisation… Quel mot terrible…

Ce n’est pas si effrayant que ça

En fait, lors de la première étape de création des clés, nous prenons deux nombres aléatoires, mais les nombres ne doivent être divisibles que par eux-mêmes et un – Nombres premiers.
Appelons-les p et q. Ensuite, nous devrions obtenir le nombre n = p *q. Il sera utilisé pour la génération ultérieure de clés, les clés seront à leur tour utilisées pour crypter et déchiffrer les messages. Dans la version finale de la clé privée et publique, le numéro n sera transféré sans modification.
Disons que nous avons entre nos mains l’une des clés RSA et un message crypté. Nous retirons le numéro n de la clé et commençons le piratage.

Factoriser n

Factorisation – décomposition d’un nombre en facteurs premiers. Tout d’abord, nous extrayons le nombre n de la clé (sur les vraies clés, vous pouvez le faire en utilisant openssl), disons n = 35. Ensuite, nous le prenons en compte en facteurs simples n = 35 = 5 * 7, c’est et il y a nos p et q. Vous pouvez désormais régénérer les clés en utilisant les p, q reçus, décrypter le message et le chiffrer tout en assurant la visibilité de l’auteur d’origine.

Les qubits ne sont pas si simples

Est-il vraiment possible de briser un RSA aussi facilement ? En fait non, les nombres p, q sont délibérément grands, de sorte que la tâche de factorisation sur les ordinateurs classiques prend très longtemps (10 ans dans une certaine mesure)< br/>Cependant, grâce à l’algorithme quantique de Shor, il est possible de factoriser un nombre en très peu de temps. À l’heure actuelle, des articles sur ce sujet indiquent le temps nécessaire pour multiplier un nombre donné, c’est-à-dire pratiquement instantanément. Pour que l’algorithme de Shor fonctionne, il est nécessaire de mettre en œuvre des ordinateurs quantiques dotés d’un grand nombre de qubits. En 2001, IBM a factorisé le nombre 15 en utilisant 7 qubits. Il faudra donc attendre longtemps pour ce moment, date à laquelle nous serons passés aux algorithmes de chiffrement post-quantique.

Touchez brièvement

Peter Shore parle de son algorithme de factorisation

Pour tester l’algorithme de Shor sur un simulateur quantique, vous pouvez installer ProjectQ, ses exemples incluent une implémentation de shor.py qui vous permet de factoriser un nombre saisi par l’utilisateur. Sur le simulateur, le temps d’exécution est déprimant, mais il semble s’agir d’une simulation amusante et ludique du fonctionnement d’un ordinateur quantique.

Articles :
http://www.pagedon.com/rsa-explained-simply/my_programming/
http://southernpacificreview.com/2014/01/06/rsa-key-generation-example/
https://0day.work/how-i-recovered-your-private-key-or-why-small-keys-are-bad/

Implémentation Python de RSA :
https://github.com/demensdeum/RSA-Python

Russian Quantum Hack and Number Generator

[Translation may be, some day]

Эта заметка увеличит длину вашего резюме на 5 см!

Без лишних слов о крутости квантовых компьютеров и всего такого, сегодня я покажу как сделать генератор чисел на реальном квантовом процессоре IBM.
Для этого мы будем использовать всего один кубит, фреймворк для разработки квантового ПО для python – ProjectQ, и 16 кубитовый процессор от IBM, онлайн доступ к которому открыт любому желающему по программе IBM Quantum Experience.

Установка ProjectQ

Для начала у вас должен быть Linux, Python и pip. Какие либо инструкции по установке этих базовых вещей приводить бесполезно, т.к. в любом случае инструкции устареют через неделю, поэтому просто найдите гайд по установке на официальном сайте. Далее устанавливаем ProjectQ, гайд по установке приведен в документации. На данный момент все свелось к установке пакета ProjectQ через pip, одной командой: python -m pip install –user projectq

Ставим кубит в суперпозицию

Создаем файл quantumNumberGenerator.py и берем пример генератора бинарного числа из документации ProjectQ, просто добавляем в него цикл на 32 шага, собираем бинарную строку и переводим в 32-битное число:

import projectq.setups.ibm
from projectq.ops import H, Measure
from projectq import MainEngine
from projectq.backends import IBMBackend

binaryString = ""

eng = MainEngine()

for i in range(1, 33):

 qubit = eng.allocate_qubit()

 H | qubit

 Measure | qubit

 eng.flush()

 binaryString = binaryString + str(int(qubit))

 print("Step " + str(i))

number = int(binaryString, 2)

print("\n--- Quantum 32-Bit Number Generator by demensdeum@gmail.com (2017) ---\n")
print("Binary: " + binaryString)
print("Number: " + str(number))
print("\n---")

Запускаем и получаем число из квантового симулятора с помощью команды python quantumNumberGenerator.py

Незнаю как вы, но я получил вывод и число 3974719468:

--- Quantum 32-Bit Number Generator by demensdeum@gmail.com (2017) ---

Binary: 11101100111010010110011111101100
Number: 3974719468

---

Хорошо, теперь мы запустим наш генератор на реальном квантовом процессоре IBM.

Хакаем IBM

Проходим регистрацию на сайте IBM Quantum Experience, подтверждаем email, в итоге должен остаться email и пароль для доступа.
Далее включаем айбиэмовский движок, меняем строку eng = MainEngine() -> eng = MainEngine(IBMBackend())
В теории после этого вы запускаете код снова и теперь он работает на реальном квантовом процессоре, используя один кубит. Однако после запуска вам придется 32 раза набрать свой email и пароль при каждой аллокации реального кубита. Обойти это можно прописав свой email и пароль прямо в библиотеки ProjectQ.

Заходим в папку где лежит фреймворк ProjectQ, ищем файл с помощью grep по строке IBM QE user (e-mail).
В итоге я исправил строки в файле projectq/backends/_ibm/_ibm_http_client.py:

email = input_fun('IBM QE user (e-mail) > ') -> email = "quantumPsycho@aport.ru"

password = getpass.getpass(prompt='IBM QE password > ') -> password = "ilovequbitsandicannotlie"

Напишите свой email и password со-но.

После этого IBM будет отправлять результаты работы с кубитом онлайн прямо в ваш скрипт, процесс генерации занимает около 20 секунд.

Возможно в дальнейшем я доберусь до работы квантового регистра, и возможно будет туториал, но это не обязательно.
Да прибудет с вами запутанность.

Статья на похожую тему:
Introducing the world’s first game for a quantum computer

Bad Robots sur WebGL basé sur ThreeJS

Aujourd’hui, une version du jeu Bad Robots est publiée sur un moteur de rendu WebGL expérimental basé sur la bibliothèque ThreeJS.
Il s’agit du premier jeu OpenGL (WebGL) sur Flame Steel Engine.
Vous pouvez y jouer ici :
http://demensdeum.com/games/BadRobotsGL/

Le code source d’IOSystem basé sur ThreeJS est disponible ici :
https://github.com/demensdeum/FlameSteelEngineGameToolkitWeb

Porting SDL C++ Game to HTML5 (Emscripten)

[Translation may be some day]

За последний год я написал простейший движок Flame Steel Engine и набор классов для игровой разработки Flame Steel Engine Game Toolkit. В данной статье я опишу как производил портирование движка и SDL игры Bad Robots на HTML 5, с использованием компилятора Emscripten.

Установка Hello World – Emscripten

Для начала нужно установить Emscripten. Простейшим вариантом оказалось использование скрипта emsdk для Linux. На официальном сайте данный тип установки называется как “Portable Emscripten SDK for Linux and OS X“. Внутри архива есть инструкция по установке с использованием скрипта. Я производил установку в директорию ~/emsdk/emsdk_portable.

После установки emscripten нужно проверить корректность работы компилятора, для этого создаем простейший hello_world.cpp и собираем его в hello_world.html с помощью команд:

source ~/emsdk/emsdk_portable/emsdk_env.sh
emcc hello_world.cpp -o hello_world.html

После компиляции в папке появится hello_world.html и вспомогательные файлы, откройте его в лучшем браузере Firefox, проверьте что все работает корректно.

Портирование кода игры

В javascript нежелательно вызывать бесконечный цикл – это приводит к зависанию браузера. На данный момент корректная стратегия – запрашивать один шаг цикла у браузера с помощью вызова window.requestAnimationFrame(callback)

В Emscripten данное обстоятельство решено с помощью вызова:

emscripten_set_main_loop(em_callback_func func, int fps, int simulate_infinite_loop);

Таким образом, нужно изменить код игры для корректного вызова метода emscripten. Для этого я сделал глобальный метод GLOBAL_fsegt_emscripten_gameLoop, в котором вызываю шаг цикла игрового контроллера. Главный игровой контроллер также вынесен в глобальную видимость:

#ifdef __EMSCRIPTEN__

void GLOBAL_fsegt_emscripten_gameLoop() {

GLOBAL_fsegt_emscripten_gameController->gameLoop();

}
#endif

Также для обработки специфических для Emscripten моментов, нужно использовать макрос __EMSCRIPTEN__.

Ресурсы и оптимизация

Emscripten поддерживает ресурсы и сборку с оптимизацией.

Для добавления изображений, музыки и прочего, положите все файлы в одну папку, например data. Далее в скрипт сборки добавьте:

emcc <файлы для сборки> –use-preload-plugins –preload-file data

Флаг –use-preload-plugins включает красивый прелоадер в углу экрана, –preload-file добавляет указанный ресурс в файл <имя проекта>.data
Код постоянно останавливался с ошибками доступа к ресурсам, пока я не включил оба этих флага. Также стоит заметить что для корректного доступа к ресурсам, желательно запускать игру на https (возможно и http) сервере, или отключить защиту локального доступа к файлам в вашем браузере.

Для включения оптимизации добавьте флаги:

-s TOTAL_MEMORY=67108864 -O3 -ffast-math

TOTAL_MEMORY – оперативная память в байтах(?) необходимая для корректной работы игры. Вы можете использовать флаг для динамического выделения памяти, но тогда часть оптимизаций работать не будет.

Производительность

Код javascript из C++ работает гораздо медленнее, даже со включенными оптимизациями. Поэтому если ваша цель это разработка для HTML5, то приготовьтесь к ручной оптимизации алгоритмов игры, паралелльному тестированию, также к написанию javascript кода вручную в особо узких местах. Для написания javascript кода используется макрос EM_ASM. Во время реализации рейкастера на emscripten, мне удалось добиться повышения fps с 2-4 до 30 с помощью прямого использования методов canvas.drawImage, в обход обертки SDL->Canvas, что почти приравнялось к написанию всего на javascript.

Поддержка SDL

На данный момент почти не работает SDL_TTF, поэтому отрисовка шрифта для Game Score в BadRobots очень проста. SDL_Image, SDL_Mixer работают корректно, в mixer я проверил только проигрывание музыки.

Исходный код Flame Steel Engine, Flame Steel Engine Game Toolkit, игры Bad Robots:

https://github.com/demensdeum/BadRobots
https://github.com/demensdeum/FlameSteelEngine
https://github.com/demensdeum/FlameSteelEngineGameToolkit

Статья на эту тему:

https://hacks.mozilla.org/2012/04/porting-me-my-shadow-to-the-web-c-to-javascriptcanvas-via-emscripten/

Diluer l’ECS


Commission: Mad Scientist by Culpeo-Fox on DeviantArt

Dans cet article, je décrirai grossièrement le modèle ECS et mon implémentation dans le Flame Steel Engine Game Toolkit. Le modèle Entity Component System est utilisé dans les jeux, incl. sur le moteur Unity. Chaque objet du jeu est une essence remplie de composants.  Pourquoi est-ce nécessaire s’il y a de la POO ?
Puis de modifier les propriétés, le comportement, l’affichage des objets directement pendant le déroulement du jeu. De telles choses ne se trouvent pas dans les applications du monde réel ; la dynamique de modification des paramètres, des propriétés des objets, de l’affichage et du son est plus caractéristique des jeux que des logiciels de comptabilité.


Nous n’avons pas passé de bananes

Disons que nous avons un cours de banane dans notre jeu. Et le concepteur du jeu voulait que les bananes soient utilisées comme armes. Disons que dans l’architecture actuelle, les bananes n’ont rien à voir avec les armes. Faire d’une banane une arme ? Fabriquer tous les objets comme des armes ?
ECS propose une solution à ce problème urgent : Tous les objets du jeu doivent être constitués de composants. Auparavant, Banana était la classe Banana, nous allons maintenant la créer, ainsi que tous les autres objets, la classe Entity, en leur ajoutant des composants. Disons qu’une banane se compose désormais des composants suivants :

  1. Composant de position (coordonnées dans le monde du jeu : x, y, z)
  2. Composant de rotation (coordonnées x, y, z)
  3. Composant calorique d’une banane (le personnage principal ne doit pas trop grossir)
  4. Composant image banane

Nous ajoutons maintenant un nouveau composant pour toutes les bananes, qui est un indicateur indiquant qu’elles peuvent être utilisées comme une arme – ; Composant d’arme. Désormais, lorsque le système de jeu voit qu’un joueur s’est approché d’une banane, il vérifie la présence d’un composant d’arme sur la banane, et si c’est le cas, il arme le joueur avec une banane.
Dans mon jeu Flame Steel Call Of The Death Mask, le motif ECS est utilisé partout. Les objets sont constitués de composants et les composants eux-mêmes peuvent contenir des composants. En général, diviser l’objet < – > le composant manque dans mon implémentation, mais c’est même un plus.

screenshot_2016-09-24_14-33-43

Le fusil de chasse dans cette capture d’écran est un composant du joueur, et en même temps, le deuxième fusil de chasse est simplement suspendu sur la carte du jeu comme un objet ordinaire.
Dans cette capture d’écran, deux systèmes sont en cours d’exécution : moteur de rendu de scène et moteur de rendu d’interface. Le moteur de rendu de scène fonctionne avec le composant d’image de fusil de chasse sur la carte, le moteur de rendu d’interface fonctionne avec le composant d’image de fusil de chasse dans les mains du joueur.

Liens connexes :
https://habrahabr.ru/post/197920/
https://www.youtube.com/watch?v=NTWSeQtHZ9M

Architecture de la boîte à outils du jeu Flame Steel Engine

Aujourd’hui, je vais parler de l’architecture de la boîte à outils pour le développement de jeux Flame Steel Engine Game Toolkit.
Flame Steel Engine Game Toolkit vous permet de créer des jeux basés sur Flame Steel Engine :
flamesteelgametoolkitschematics

Toutes les classes du Flame Steel Engine commencent par le préfixe FSE (Flame Steel Moteur ), etFSEGT (Fà flammeSmoteurEmoteurG strong>ame Ttoolkit) pour la boîte à outils.
La scène de jeu, les objets et les boutons sont tous des sous-classes de FSEObject et doivent se trouver dans la classe FSEGTGameData. Chaque FSEObject doitimplémenter l’interface FSESerialize, cela vous permettra de sauvegarder/charger les données du jeu et de fournir un mécanisme de sauvegarde.
La classe FSEController fonctionne avec les objets de la classe FSEObject. La boîte à outils comporte une classe de contrôleur de scène de jeu de base : FSEGTGameSceneController, vous pouvez hériter de cette classe pour implémenter votre logique de jeu.
IOSystem est un objet de l’interface FSEGTIOSystem, cette interface contient FSEGTRenderer, FSEGTInputController, FSEGTUIRenderer. fort>.FSEGTIOSystem doit implémenter un moteur de rendu, recevoir les données du clavier, des joysticks (périphériques d’entrée) et fournir le rendu des éléments d’interface pour systèmes d’entrées/sorties de cette plateforme.
Actuellement, un moteur de rendu a été implémenté, un contrôleur de clavier basé sur la bibliothèque SDL, il est disponible dans le FSEGTIOSDLSystem

Flame Steel Engine Raycaster Demo
Flame Steel Engine Raycaster Demo

Il est prévu à l’avenir de créer un IOSystem basé sur OpenGL, la classe s’appellera FSEGTIOGLSystem . Si vous souhaitez créer un IOSystem basé sur n’importe quelle plate-forme, vous devez utiliser l’interface FSEGTIOSystem et implémenter le moteur de rendu FSEGTRenderer, FSEGTInputController pour cela. plateforme .

Code source de Flame Steel Engine, boîte à outils, jeu :
https://github.com/demensdeum/FlameSteelCallOfTheDeathMask

Unity, pourquoi Wasteland 2 ne fonctionne-t-il pas sur mon Ubuntu ?

Je suis fier de soutenir le jeu Wasteland 2. Aujourd’hui, je voulais le lancer sur Ubuntu, mais je n’ai pas pu. Cependant, après une heure de recherche sur Google, tout s’est bien passé. Il s’avère que Unity a de sérieux problèmes avec Linux, mais en utilisant certaines béquilles, le jeu peut être lancé :

ulimit -Sn 65536~/.local/share/Steam/steamapps/common/Wasteland\ 2\ Director\'s\ Cut/Linux/WL2

Recette d’ici :
https://forums.inxile-entertainment.com/viewtopic.php?t=15505