Maison > développement back-end > tutoriel php > Poka Yoke - Sauver les projets avec une programmation hyper-défensive

Poka Yoke - Sauver les projets avec une programmation hyper-défensive

尊渡假赌尊渡假赌尊渡假赌
Libérer: 2025-02-09 11:13:12
original
216 Les gens l'ont consulté

Poka Yoke - Saving Projects with Hyper-Defensive Programming

Cet article a été évalué par des pairs par Deji Akala et Marco Pivetta. Merci à tous les pairs examinateurs de SitePoint pour obtenir le meilleur contenu dans SitePoint!


comprendre le code de l'autre et comment l'utiliser peut parfois devenir difficile lorsque des équipes moyennes et grandes collaborent sur la même base de code. À cette fin, il existe plusieurs solutions. Par exemple, il peut être convenu de suivre un ensemble de normes de codage pour améliorer la lisibilité de votre code, ou utiliser un cadre familier à tous les membres de l'équipe (l'excellent cours d'entrée de gamme Laravel est disponible ici).

Cependant, cela ne suffit généralement pas, surtout lorsqu'il est nécessaire de plonger dans la section d'application écrite il y a un certain temps pour corriger les bogues ou ajouter de nouvelles fonctionnalités. À ce stade, il est difficile de se rappeler comment une classe particulière devrait fonctionner, y compris elles-mêmes et comment elles sont combinées les unes avec les autres. À ce stade, il est facile d'introduire accidentellement des effets secondaires ou des erreurs sans les connaître.

Poka Yoke - Saving Projects with Hyper-Defensive Programming

Ces erreurs peuvent être trouvées dans l'assurance qualité, mais peuvent également être vraiment ignorées. Même s'il est découvert, renvoyer le code et la fixation peut prendre beaucoup de temps.

Alors, comment éviter cela? La réponse est "Poka Yoke" .

points clés

  • Poka Yoke Définition: Poka Yoke est un terme japonais qui signifie "résistant aux erreurs".
  • Prévention et détection des erreurs: Cette méthode est divisée en prévention des erreurs (assurez-vous que le code est utilisé correctement) et la détection des erreurs (impliquant la surveillance de l'application pour les erreurs potentielles).
  • Exemple d'application pratique: implémentation de la déclaration de type dans PHP pour éviter les erreurs de type de paramètre, et utiliser des objets de valeur pour éviter les paramètres de fonction d'obscusance est un exemple clé de la prévention des erreurs.
  • Imutabilité et objets vides: mettre l'accent sur l'immuabilité des objets pour prévenir les effets secondaires dans les applications et utiliser des objets vides pour éviter les contrôles vides est une stratégie avancée de l'empelet Poka.
  • Gestion des erreurs et journalisation: défendre la gestion stricte des erreurs, comme l'activation des types stricts en PHP et le non-supprimer les erreurs, ainsi que les applications de journalisation et de surveillance proactivement.
  • Applicabilité plus large: le joug Poka ne se limite pas à la programmation, il peut également améliorer la disponibilité de l'API, améliorer la configuration de l'application et empêcher les erreurs de l'utilisateur dans diverses interfaces, démontrant son universalité dans différents domaines.

Qu'est-ce que Poka Yoke?

Poka Yoke est un terme japonais qui se traduit grossièrement par "anti-erre". Le terme provenait de la fabrication Lean et fait référence à tout mécanisme qui aide l'opérateur de la machine à éviter les erreurs.

En plus de la fabrication, Poka Yoke est également fréquemment utilisé dans l'électronique grand public. Par exemple, les cartes SIM ne peuvent être insérées que dans le plateau SIM d'une manière en raison de leur forme asymétrique.

Poka Yoke - Saving Projects with Hyper-Defensive Programming

L'exemple matériel manquant de joug Poka est le port PS / 2, qui a exactement la même forme pour le connecteur du clavier et le connecteur de la souris. Ils ne peuvent être distingués qu'en utilisant des codes de couleur, il est donc facile d'échanger accidentellement des connecteurs et de les insérer dans les mauvais ports, car ils s'adaptent tous de la même manière.

Poka Yoke - Saving Projects with Hyper-Defensive Programming

En plus d'être utilisé dans le matériel, le concept de Poka Yoke peut également être appliqué à la programmation. L'idée est de rendre l'interface publique de notre code aussi facile à comprendre que possible et de lancer immédiatement une erreur lorsque le code est mal utilisé. Cela semble évident, mais nous rencontrons souvent du code qui a des défauts à cet égard.

Cependant, veuillez noter que Poka Yoke n'est pas destiné à prévenir les abus intentionnels. Son objectif est d'empêcher les erreurs inattendues, et non de protéger le code contre une utilisation malveillante. Tant que quelqu'un a accès à votre code, il est toujours en mesure de contourner vos mesures de sécurité s'il le souhaite vraiment.

Avant de discuter de quelles mesures spécifiques peuvent être prises pour rendre le code plus résistant aux erreurs, il est important de savoir que le mécanisme de joug Poka peut généralement être divisé en deux catégories:

  • Prévention des erreurs
  • Détection d'erreur

Les techniques de prévention des erreurs aident à détecter les erreurs le plus tôt possible. Ils sont conçus pour s'assurer que personne ne peut utiliser accidentellement notre code incorrectement en rendant l'interface et le comportement aussi simples et simples que possible. Pensez à l'exemple d'une carte SIM, qui ne peut être insérée que dans le plateau SIM d'une manière.

En revanche, le mécanisme de détection des erreurs existe en dehors de notre code. Ils surveillent nos applications pour des erreurs potentielles et nous avertissent. Un exemple est de détecter si un périphérique connecté à un port PS / 2 est le bon type de logiciel, et sinon, un avertissement s'affiche à l'utilisateur de la raison pour laquelle il ne fonctionne pas. Ce logiciel particulier ne peut pas empêcher les erreurs car le connecteur est interchangeable lorsqu'il est branché, mais il peut détecter les erreurs et nous avertir afin que l'erreur puisse être corrigée.

Dans le reste de cet article, nous explorerons plusieurs méthodes qui peuvent être utilisées pour implémenter la prévention des erreurs et la détection des erreurs dans nos applications. Mais rappelez-vous que cette liste n'est qu'un point de départ. Selon votre application spécifique, il peut y avoir des mesures supplémentaires qui peuvent rendre votre code plus résistant aux erreurs. En outre, il est important de se souvenir du coût initial du joug Poka et de vous assurer qu'il en vaut la peine pour votre projet spécifique. Selon la complexité et la taille de l'application, certaines mesures peuvent être trop coûteuses par rapport aux coûts d'erreur potentiels. Par conséquent, vous et votre équipe devez décider quelles mesures vous conviennent le mieux.

Exemple de prévention des erreurs

Poka Yoke - Saving Projects with Hyper-Defensive Programming

(Scalar) Type Declaration

Auparavant appelé type indique dans PHP 5, la déclaration de type est un moyen facile de démarrer la fonction et la signature de la méthode d'erreur dans PHP 7.

En affectant un type spécifique aux paramètres de fonction, l'obscurcissement de l'ordre du paramètre devient plus difficile lors de l'appel d'une fonction.

Par exemple, jetons un coup d'œil à cette notification que nous pourrions vouloir envoyer à l'utilisateur:

<?php
class Notification {
    private $userId;
    private $subject;
    private $message;

    public function __construct(
        $userId,
        $subject,
        $message
    ) {
        $this->userId = $userId;
        $this->subject = $subject;
        $this->message = $message;
    }

    public function getUserId()
    {
        return $this->userId;
    }

    public function getSubject()
    {
        return $this->subject;
    }

    public function getMessage()
    {
        return $this->message;
    }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Sans déclaration de type, nous pouvons facilement injecter des variables du mauvais type, ce qui peut briser notre application. Par exemple, nous pouvons supposer que $ userId devrait être une chaîne, et il peut en fait devoir être un entier.

Si nous injectons le mauvais type, l'erreur ne peut pas être détectée tant que l'application essaie réellement de traiter la notification. D'ici là, nous pourrions obtenir des messages d'erreur incompréhensibles sur des types inattendus, mais rien ne pointe immédiatement de notre code qui injecte des chaînes au lieu des entiers.

Par conséquent, il est généralement plus intéressant de forcer l'application à s'écraser le plus tôt possible afin que de telles erreurs puissent être détectées le plus tôt possible pendant le développement.

Dans ce cas, nous pouvons simplement ajouter une déclaration de type, et lorsque nous obscurons le type de paramètre, PHP s'arrêtera immédiatement et nous avertira d'une erreur fatale:

<?php
declare(strict_types=1);

class Notification {
    private $userId;
    private $subject;
    private $message;

    public function __construct(
        int $userId,
        string $subject,
        string $message
    ) {
        $this->userId = $userId;
        $this->subject = $subject;
        $this->message = $message;
    }

    public function getUserId() : int
    {
        return $this->userId;
    }

    public function getSubject() : string
    {
        return $this->subject;
    }

    public function getMessage() : string
    {
        return $this->message;
    }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

mais notez que par défaut, PHP essaiera de lancer des paramètres incorrects à leur type attendu. Pour éviter cela, il est important que nous activions strict_types afin que nous recevons réellement une erreur fatale lorsqu'une erreur se produit. Par conséquent, les déclarations de type scalaire ne sont pas des formes de joug Poka idéales, mais elles sont un bon début pour réduire les erreurs. Même si strict_types est désactivé, ils peuvent toujours servir d'indication du type attendu du paramètre.

De plus, nous déclarons le type de retour pour la méthode. Ceux-ci facilitent la détermination de la valeur que vous pouvez vous attendre lors de l'appel d'une fonction spécifique.

Les types de retour définis de manière opérationnelle aident également à éviter d'utiliser un grand nombre d'instructions de commutation lors du traitement des valeurs de retour, car notre méthode peut renvoyer divers types sans type de retour explicitement déclaré. Ainsi, ceux qui utilisent notre méthode doivent vérifier quel type est réellement renvoyé dans un cas spécifique. Ces instructions de commutation sont évidemment oubliées et conduisent à des erreurs indétectables. En utilisant les types de retour, ces erreurs seront considérablement réduites.

Value Object

Un problème que le type scalaire indique ne peut pas être facilement résolu pour nous est que le fait d'avoir plusieurs paramètres de fonction permet de confondre l'ordre desdits paramètres.

Poka Yoke - Saving Projects with Hyper-Defensive Programming

PHP peut nous avertir lorsque nous obscurcissons l'ordre des paramètres lorsque tous les paramètres ont des types scalaires différents, mais dans la plupart des cas, nous pouvons avoir certains paramètres avec le même type.

Pour résoudre ce problème, nous pouvons envelopper nos paramètres dans un objet de valeur comme celui-ci:

class UserId {
    private $userId;

    public function __construct(int $userId) {
        $this->userId = $userId;
    }

    public function getValue() : int
    {
        return $this->userId;
    }
}

class Subject {
    private $subject;

    public function __construct(string $subject) {
        $this->subject = $subject;
    }

    public function getValue() : string
    {
        return $this->subject;
    }
}

class Message {
    private $message;

    public function __construct(string $message) {
        $this->message = $message;
    }

    public function getMessage() : string
    {
        return $this->message;
    }
}

class Notification {
    /* ... */

    public function __construct(
        UserId $userId,
        Subject $subject,
        Message $message
    ) {
        $this->userId = $userId;
        $this->subject = $subject;
        $this->message = $message;
    }

    public function getUserId() : UserId { /* ... */ }

    public function getSubject() : Subject { /* ... */ }

    public function getMessage() : Message { /* ... */ }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Parce que nos paramètres ont désormais un type très spécifique, il est presque impossible de les confondre.

Un autre avantage de l'utilisation d'objets de valeur au lieu de la déclaration de type scalaire est que nous n'avons plus besoin d'activer strict_types dans chaque fichier. Si nous n'avons pas à nous en souvenir, nous ne pouvons pas l'oublier accidentellement.

Vérification

Poka Yoke - Saving Projects with Hyper-Defensive Programming

Lorsque vous utilisez des objets de valeur, nous pouvons encapsuler leur logique de vérification des données dans l'objet lui-même. Ce faisant, nous pouvons empêcher la création d'objets de valeur avec un état non valide, ce qui peut entraîner des problèmes dans d'autres couches de l'application.

Par exemple, nous pourrions avoir une règle selon laquelle un utilisateur donné doit toujours être positif.

Nous pouvons évidemment vérifier cette règle à chaque fois que nous obtenons un utilisateur en entrée, mais en revanche, il peut également être facilement oublié à un endroit ou à un autre.

Même si cette erreur entraîne une erreur réelle dans une autre couche de l'application, le message d'erreur peut ne pas indiquer clairement quelle erreur s'est réellement produite et est difficile à déboguer.

Pour éviter de telles erreurs, nous pouvons ajouter une certaine validation au constructeur utilisateur:

<?php
class Notification {
    private $userId;
    private $subject;
    private $message;

    public function __construct(
        $userId,
        $subject,
        $message
    ) {
        $this->userId = $userId;
        $this->subject = $subject;
        $this->message = $message;
    }

    public function getUserId()
    {
        return $this->userId;
    }

    public function getSubject()
    {
        return $this->subject;
    }

    public function getMessage()
    {
        return $this->message;
    }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

De cette façon, nous pouvons toujours nous assurer que lorsque vous utilisez l'objet utilisateur, il a un état valide. Cela nous empêche de vénérifier constamment nos données à tous les niveaux de notre application.

Notez que nous pouvons ajouter des déclarations de type scalaire au lieu d'utiliser IS_INT, mais cela nous obligera à activer strict_types partout où nous utilisons l'utilisateur.

Si nous n'activons pas strict_types, PHP essaiera automatiquement de lancer d'autres types à INT lors de la transmission à UserId. Cela peut être problématique, par exemple, nous pourrions injecter un numéro de point flottant, qui peut en fait être une variable incorrecte car l'ID utilisateur n'est pas un numéro de point flottant.

Dans d'autres cas, comme lorsque nous pouvons utiliser un objet de valeur de prix, la désactivation de strict_types peut entraîner une erreur d'arrondi, car PHP convertira automatiquement les variables de points flottants en int.

Imutabilité

Par défaut, les objets sont passés par référence dans PHP. Cela signifie que lorsque nous apportons des modifications à l'objet, elle change immédiatement tout au long de l'application.

Poka Yoke - Saving Projects with Hyper-Defensive Programming

Bien que cette méthode ait ses avantages, elle a également quelques inconvénients. Jetons un coup d'œil à un exemple d'envoi de notifications aux utilisateurs via des messages texte et des e-mails:

<?php
declare(strict_types=1);

class Notification {
    private $userId;
    private $subject;
    private $message;

    public function __construct(
        int $userId,
        string $subject,
        string $message
    ) {
        $this->userId = $userId;
        $this->subject = $subject;
        $this->message = $message;
    }

    public function getUserId() : int
    {
        return $this->userId;
    }

    public function getSubject() : string
    {
        return $this->subject;
    }

    public function getMessage() : string
    {
        return $this->message;
    }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Nous provoquons des effets secondaires inattendus car l'objet de notification est passé par référence. En raccourcissant la durée du message dans SMSNotificationsSend, l'objet de notification référencé est mis à jour dans toute la demande, ce qui signifie qu'il est également raccourci lorsqu'il est envoyé plus tard par le StravofificationSender.

Pour résoudre ce problème, nous pouvons rendre notre objet de notification immuable. Au lieu de fournir une méthode définie pour le modifier, nous pouvons en ajouter quelques méthodes pour créer une copie de la notification d'origine, puis appliquer les modifications:

class UserId {
    private $userId;

    public function __construct(int $userId) {
        $this->userId = $userId;
    }

    public function getValue() : int
    {
        return $this->userId;
    }
}

class Subject {
    private $subject;

    public function __construct(string $subject) {
        $this->subject = $subject;
    }

    public function getValue() : string
    {
        return $this->subject;
    }
}

class Message {
    private $message;

    public function __construct(string $message) {
        $this->message = $message;
    }

    public function getMessage() : string
    {
        return $this->message;
    }
}

class Notification {
    /* ... */

    public function __construct(
        UserId $userId,
        Subject $subject,
        Message $message
    ) {
        $this->userId = $userId;
        $this->subject = $subject;
        $this->message = $message;
    }

    public function getUserId() : UserId { /* ... */ }

    public function getSubject() : Subject { /* ... */ }

    public function getMessage() : Message { /* ... */ }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

De cette façon, chaque fois que nous modifions la classe de notification en raccourcissant, par exemple, la longueur du message, les modifications ne se propagent plus dans l'application, empêchant ainsi les effets secondaires inattendus.

mais notez qu'il est difficile (sinon impossible) de rendre un objet vraiment immuable en PHP. Mais pour rendre notre code plus résistant aux erreurs, il est déjà utile si nous ajoutons "immuables" avec des méthodes au lieu de méthodes définies, car l'utilisateur de la classe n'a plus besoin de se rappeler de cloner l'objet lui-même avant d'apporter des modifications.

retourner à l'objet vide

Parfois, nous pouvons avoir des fonctions ou des méthodes qui peuvent renvoyer certaines valeurs ou null. Ces valeurs de retour nullables peuvent causer des problèmes, car ils ont presque toujours besoin de vérifier s'ils sont vides avant de les utiliser. Encore une fois, c'est quelque chose que nous pouvons facilement oublier. Pour éviter de toujours vérifier la valeur de retour, nous pouvons retourner un objet vide à la place.

Poka Yoke - Saving Projects with Hyper-Defensive Programming

Par exemple, nous pourrions avoir une carte d'achat où des remises sont appliquées ou aucune remise n'est appliquée:

<?php
class Notification {
    private $userId;
    private $subject;
    private $message;

    public function __construct(
        $userId,
        $subject,
        $message
    ) {
        $this->userId = $userId;
        $this->subject = $subject;
        $this->message = $message;
    }

    public function getUserId()
    {
        return $this->userId;
    }

    public function getSubject()
    {
        return $this->subject;
    }

    public function getMessage()
    {
        return $this->message;
    }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Lors du calcul du prix final de ShoppingCart, nous devons maintenant toujours vérifier si getDiscount () renvoie une réduction nul ou réelle avant de pouvoir appeler la méthode ApplyTo:

<?php
declare(strict_types=1);

class Notification {
    private $userId;
    private $subject;
    private $message;

    public function __construct(
        int $userId,
        string $subject,
        string $message
    ) {
        $this->userId = $userId;
        $this->subject = $subject;
        $this->message = $message;
    }

    public function getUserId() : int
    {
        return $this->userId;
    }

    public function getSubject() : string
    {
        return $this->subject;
    }

    public function getMessage() : string
    {
        return $this->message;
    }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Si nous ne faisons pas ce chèque, nous pouvons recevoir des avertissements PHP et / ou d'autres effets inattendus lorsque getDiscount () renvoie null.

En revanche, si nous retournons un objet vide lorsque la remise n'est pas définie, nous pouvons complètement supprimer ces chèques:

class UserId {
    private $userId;

    public function __construct(int $userId) {
        $this->userId = $userId;
    }

    public function getValue() : int
    {
        return $this->userId;
    }
}

class Subject {
    private $subject;

    public function __construct(string $subject) {
        $this->subject = $subject;
    }

    public function getValue() : string
    {
        return $this->subject;
    }
}

class Message {
    private $message;

    public function __construct(string $message) {
        $this->message = $message;
    }

    public function getMessage() : string
    {
        return $this->message;
    }
}

class Notification {
    /* ... */

    public function __construct(
        UserId $userId,
        Subject $subject,
        Message $message
    ) {
        $this->userId = $userId;
        $this->subject = $subject;
        $this->message = $message;
    }

    public function getUserId() : UserId { /* ... */ }

    public function getSubject() : Subject { /* ... */ }

    public function getMessage() : Message { /* ... */ }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Maintenant, lorsque nous appelons getDiscount (), nous obtenons toujours l'objet de réduction même s'il n'y a pas de rabais disponible. De cette façon, nous pouvons appliquer la remise à notre total, et même sans la remise, nous n'avons plus besoin de la déclaration IF:

class UserId {
    private $userId;

    public function __construct($userId) {
        if (!is_int($userId) || $userId <= 0) {
            throw new \InvalidArgumentException(
                'UserId should be a positive integer.'
            );
        }

        $this->userId = $userId;
    }

    public function getValue() : int
    {
        return $this->userId;
    }
}
Copier après la connexion

dépendances facultatives

Pour les raisons que nous voulons éviter les valeurs de retour inversables, nous voulons peut-être éviter les dépendances facultatives, mais rendre toutes nos dépendances nécessaires.

par exemple, la classe suivante:

interface NotificationSenderInterface
{
    public function send(Notification $notification);
}

class SMSNotificationSender implements NotificationSenderInterface
{
    public function send(Notification $notification) {
        $this->cutNotificationLength($notification);

        // 发送短信...
    }

    /**
     * 确保通知不超过短信长度。
     */
    private function cutNotificationLength(Notification $notification)
    {
        $message = $notification->getMessage();
        $messageString = substr($message->getValue(), 0, 160); // 修正截取长度
        $notification->setMessage(new Message($messageString));
    }
}

class EmailNotificationSender implements NotificationSenderInterface
{
    public function send(Notification $notification) {
        // 发送电子邮件...
    }
}

$smsNotificationSender = new SMSNotificationSender();
$emailNotificationSender = new EmailNotificationSender();

$notification = new Notification(
    new UserId(17466),
    new Subject('Demo notification'),
    new Message('Very long message ... over 160 characters.')
);

$smsNotificationSender->send($notification);
$emailNotificationSender->send($notification);
Copier après la connexion

Il y a deux problèmes avec cette méthode:

  1. Nous devons constamment vérifier s'il existe un enregistreur dans la méthode DoSomething ().
  2. Lors de la configuration de la classe SomeService dans le conteneur de service, quelqu'un peut oublier de configurer réellement l'enregistreur, ou il peut même ne pas savoir que la classe a la possibilité de configurer l'enregistreur.

Nous pouvons simplifier ce problème en faisant de LoggerInterface une dépendance requise:

class Notification {
    public function __construct( ... ) { /* ... */ }

    public function getUserId() : UserId { /* ... */ }

    public function withUserId(UserId $userId) : Notification {
        return new Notification($userId, $this->subject, $this->message); // 使用新的Notification实例
    }

    public function getSubject() : Subject { /* ... */ }

    public function withSubject(Subject $subject) : Notification {
        return new Notification($this->userId, $subject, $this->message); // 使用新的Notification实例
    }

    public function getMessage() : Message { /* ... */ }

    public function withMessage(Message $message) : Notification {
        return new Notification($this->userId, $this->subject, $message); // 使用新的Notification实例
    }
}
Copier après la connexion

De cette façon, notre interface publique devient moins déroutante, et chaque fois que quelqu'un crée une nouvelle instance de SomeService, ils savent que la classe nécessite une instance de LoggerInterface, de sorte qu'il n'oubliera pas d'injecter une instance.

En outre, nous avons omis l'instruction IF pour vérifier si le journaliste a été injecté, ce qui a rendu notre Dosomething () plus facile à lire et à réduire la possibilité d'erreurs lorsque quelqu'un a apporté des modifications.

Si, à un moment donné, nous voulons utiliser un peu de service sans enregistreur, nous pouvons appliquer la même logique que l'instruction de retour, mais utilisez simplement un objet vide:

<?php
class Notification {
    private $userId;
    private $subject;
    private $message;

    public function __construct(
        $userId,
        $subject,
        $message
    ) {
        $this->userId = $userId;
        $this->subject = $subject;
        $this->message = $message;
    }

    public function getUserId()
    {
        return $this->userId;
    }

    public function getSubject()
    {
        return $this->subject;
    }

    public function getMessage()
    {
        return $this->message;
    }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

En fin de compte, cela a le même effet que d'utiliser la méthode SetLogger () en option, mais cela rend notre code plus facile à suivre et réduit la possibilité d'erreurs dans le conteneur d'injection de dépendance.

Interface publique

Pour rendre notre code plus facile à utiliser, il est préférable de maintenir le nombre de méthodes publiques sur la classe au minimum. De cette façon, notre utilisation du code devient moins déroutante, nous maintenons moins de code et moins susceptible de rompre la compatibilité en arrière lors de la refactorisation.

Poka Yoke - Saving Projects with Hyper-Defensive Programming

Pour maintenir la méthode publique au minimum, il peut être considéré comme une méthode publique comme une transaction.

par exemple, transférer de l'argent entre deux comptes bancaires:

<?php
declare(strict_types=1);

class Notification {
    private $userId;
    private $subject;
    private $message;

    public function __construct(
        int $userId,
        string $subject,
        string $message
    ) {
        $this->userId = $userId;
        $this->subject = $subject;
        $this->message = $message;
    }

    public function getUserId() : int
    {
        return $this->userId;
    }

    public function getSubject() : string
    {
        return $this->subject;
    }

    public function getMessage() : string
    {
        return $this->message;
    }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

Bien que la base de données sous-jacente puisse assurer des transactions pour s'assurer que si le dépôt ne peut pas être effectué, aucun fonds ne sera retiré, et vice versa, la base de données ne peut pas nous empêcher d'oublier d'appeler $ compte1- & gt; retrait () ou $ compte2 - & gt; dépôt (), ce qui fera de l'incorrect le solde.

Heureusement, nous pouvons facilement résoudre ce problème en remplaçant nos deux méthodes distinctes par une seule méthode de transaction:

class UserId {
    private $userId;

    public function __construct(int $userId) {
        $this->userId = $userId;
    }

    public function getValue() : int
    {
        return $this->userId;
    }
}

class Subject {
    private $subject;

    public function __construct(string $subject) {
        $this->subject = $subject;
    }

    public function getValue() : string
    {
        return $this->subject;
    }
}

class Message {
    private $message;

    public function __construct(string $message) {
        $this->message = $message;
    }

    public function getMessage() : string
    {
        return $this->message;
    }
}

class Notification {
    /* ... */

    public function __construct(
        UserId $userId,
        Subject $subject,
        Message $message
    ) {
        $this->userId = $userId;
        $this->subject = $subject;
        $this->message = $message;
    }

    public function getUserId() : UserId { /* ... */ }

    public function getSubject() : Subject { /* ... */ }

    public function getMessage() : Message { /* ... */ }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion
Copier après la connexion

En conséquence, notre code devient plus robuste car seules les transactions terminées sont plus sujettes aux erreurs.

Exemple de détection d'erreur

Contrairement aux mécanismes de prévention des erreurs, les mécanismes de détection des erreurs ne sont pas destinés à éviter les erreurs. Au lieu de cela, ils sont conçus pour nous avertir lorsque nous détectons les problèmes.

Ils existent la plupart du temps en dehors de nos applications et s'exécutent régulièrement pour surveiller notre code ou des modifications spécifiques à eux.

Test unitaire

Les tests unitaires peuvent garantir que le nouveau code fonctionne correctement, mais il peut également s'assurer que le code existant fonctionne toujours comme prévu lorsque quelqu'un refactue une partie du système.

Parce que quelqu'un peut toujours oublier d'exécuter nos tests unitaires, il est recommandé d'utiliser des services comme Travis CI et GitLab CI pour les exécuter automatiquement lorsqu'ils apportent des modifications. De cette façon, les développeurs seront automatiquement informés en cas de modifications majeures, ce qui nous aide également à nous assurer que les modifications fonctionnent comme prévu lors de l'examen des demandes de traction.

En dehors de la détection des erreurs, les tests unitaires sont également un excellent moyen de fournir des exemples de la façon dont une partie particulière du code devrait fonctionner, ce qui empêche à son tour les autres de faire des erreurs lors de l'utilisation de notre code.

Rapport de couverture du code et test de mutation

Parce que nous pouvons toujours oublier d'écrire suffisamment de tests, il peut être avantageux d'utiliser des services comme CoverAlls pour générer automatiquement des rapports de couverture de code lors de l'exécution de tests unitaires. Chaque fois que notre couverture de code diminue, CoverAlls nous envoie des notifications afin que nous puissions ajouter des tests unitaires et nous pouvons également voir comment notre couverture de code change au fil du temps.

Poka Yoke - Saving Projects with Hyper-Defensive Programming

Une autre meilleure façon de s'assurer que nous avons suffisamment de tests unitaires pour notre code est de configurer certains tests de mutation, comme l'utilisation de Humbug. Comme son nom l'indique, ces tests sont conçus pour vérifier que nous avons une couverture de code suffisante en modifiant légèrement notre code source, puis en exécutant nos tests unitaires et en garantissant que les tests pertinents commencent à échouer en raison de mutations.

En utilisant les rapports de couverture de code et les tests de mutation, nous pouvons nous assurer que nos tests unitaires couvrent suffisamment de code pour empêcher les erreurs ou les erreurs inattendues.

Analyseur de code

Les analyseurs de code peuvent détecter les erreurs dans les applications au début du processus de développement. Par exemple, des IDE tels que PHPStorm utilisent des analyseurs de code pour nous avertir des erreurs et donner des suggestions lorsque nous écrivons notre code. Ceux-ci vont des erreurs de syntaxe simples à la détection des codes répétés.

En plus des analyseurs intégrés dans la plupart des IDE, des analyseurs tiers ou même personnalisés peuvent être incorporés dans le processus de construction de l'application pour découvrir des problèmes spécifiques. Une liste non exhaustive d'analyseurs adaptées aux projets PHP peut être trouvée dans les outils Exakat / PHP-statique-analyse, allant du codage des analyseurs standard aux analyseurs qui vérifient les vulnérabilités de sécurité.

Des solutions en ligne existent, telles que Sensiolabs Insights.

Message de journal

Contrairement à la plupart des autres mécanismes de détection d'erreur, les messages de journal peuvent nous aider à détecter les erreurs dans l'application lorsqu'elle s'exécute en temps réel en production.

Poka Yoke - Saving Projects with Hyper-Defensive Programming

Bien sûr, notre code est d'abord tenu d'enregistrer le message lorsqu'une situation inattendue se produit. Même si notre code prend en charge les enregistreurs, il peut être facile de tout oublier tout en les configurant. Par conséquent, nous devons essayer d'éviter les dépendances facultatives (voir ci-dessus).

Alors que la plupart des applications enregistrent au moins certains messages, les informations qu'ils fournissent deviennent vraiment intéressantes lorsqu'ils analysent et suivent de manière proactive à l'aide d'outils comme Kibana ou Nagios. Ces outils nous donnent un aperçu des erreurs et des avertissements qui se produisent dans les applications lorsque les utilisateurs utilisent activement l'application, plutôt que lors du test en interne. Nous avons un excellent article sur la surveillance des applications PHP à l'aide de cette pile de wapiti.

Ne supprimez pas les erreurs

Certaines erreurs sont souvent supprimées même si elles enregistrent activement les messages d'erreur. Chaque fois qu'une erreur "récupérable" se produit, PHP a tendance à continuer à fonctionner comme s'il voulait nous aider en gardant l'application en cours d'exécution. Cependant, les erreurs sont souvent très utiles lors du développement ou du test de nouvelles fonctionnalités car elles indiquent généralement des erreurs dans notre code.

C'est pourquoi la plupart des analyseurs de code vous avertissent lorsqu'ils détectent que vous utilisez des erreurs de suppression, car il peut masquer les erreurs qui réapparaissent inévitablement une fois qu'un visiteur utilise réellement l'application.

Généralement, il est préférable de définir le niveau d'erreur_réport de PHP à E_ALL afin que même les moindres avertissements soient signalés. Cependant, assurez-vous que ces messages sont enregistrés quelque part et cachés devant l'utilisateur afin que des informations sensibles sur l'architecture d'application ou les vulnérabilités de sécurité potentielles ne soient pas exposées à l'utilisateur final.

en plus de la configuration Error_Reportting, assurez-vous de toujours activer strict_types afin que PHP n'essaie pas de lancer automatiquement les paramètres de fonction en son type attendu, car c'est lors de la convertis d'un type en un autre (par exemple à partir d'erreurs d'arrondi flottantes lors de la conversion en intr. ) conduisent généralement à des erreurs difficiles à détecter.

Utilisation en dehors de PHP

Étant donné que Poka Yoke est plus un concept qu'une technologie spécifique, elle peut également être appliquée aux zones en dehors de la PHP (mais liées au PHP).

Infrastructure

Au niveau de l'infrastructure, des outils tels que Vagrant peuvent être utilisés pour partager les mêmes paramètres de développement que l'environnement de production, ce qui peut empêcher de nombreuses erreurs.

L'utilisation de la construction de serveurs telle que Jenkins et GOCD peut également aider à prévenir les erreurs lors du déploiement des modifications de nos applications, car cela peut souvent inclure un grand nombre d'étapes nécessaires qui dépendent de l'application, qui peut facilement être oubliée.

API REST

Lors de la construction d'API REST, nous pouvons utiliser Poka Yoke conjointement pour faciliter l'utilisation de notre API. Par exemple, nous pouvons nous assurer qu'une erreur est toujours renvoyée lors du passage des paramètres inconnus dans la requête URL ou le corps de demande. Cela peut sembler étrange parce que nous voulons évidemment éviter de "casser" notre client API, mais il est généralement préférable d'avertir les développeurs en utilisant notre API dès que possible sur une utilisation incorrecte afin que les bogues puissent être corrigés au début du processus de développement.

Par exemple, il peut y avoir un paramètre de couleur sur notre API, mais une personne utilisant notre API pourrait accidentellement utiliser le paramètre de couleur. Sans aucun avertissement, cette erreur peut facilement être en production jusqu'à ce que l'utilisateur final le remarque en raison d'un comportement inattendu. Pour comprendre comment construire une API qui ne vous décevra pas plus tard, un bon livre peut vous aider.

Configuration de l'application

Effectivement, toutes les applications reposent sur au moins une configuration personnalisée. En règle générale, les développeurs préfèrent fournir autant de valeurs par défaut que possible pour les configurations, donc la configuration d'une application est moins de travail.

Cependant, comme les exemples de couleur et de couleur ci-dessus, il est facile de saisir de manière incorrecte les paramètres de configuration, ce qui fait que notre application se replie accidentellement à la valeur par défaut. Il est difficile de retracer ces erreurs lorsque l'application ne lance pas d'erreur, et la meilleure façon de lancer une erreur pour une configuration incorrecte est de ne pas fournir de valeurs par défaut du tout et de lancer immédiatement une erreur lorsque les paramètres de configuration sont manquants.

Empêcher les erreurs de l'utilisateur

Poka Yoke - Saving Projects with Hyper-Defensive Programming

Le concept

POKA Yoke peut également être appliqué pour empêcher ou détecter les erreurs de l'utilisateur. Par exemple, dans le logiciel de paiement, un algorithme de bit de chèque peut être utilisé pour vérifier le compte entré par l'utilisateur. Cela empêche les utilisateurs d'entrer accidentellement un compte avec une faute de frappe.

Conclusion

Bien que Poka Yoke soit plus un concept qu'un ensemble spécifique d'outils, nous pouvons appliquer divers principes à nos processus de code et de développement pour garantir que les erreurs sont évitées ou détectées le plus tôt possible. Souvent, ces mécanismes seront spécifiques à l'application elle-même et à sa logique métier, mais nous pouvons utiliser des techniques et des outils simples pour rendre n'importe quel code plus étanche.

La chose la plus importante est que même si nous voulons évidemment éviter les erreurs de production, elles sont très utiles en développement et que nous ne devons pas avoir peur de les élever dès que possible afin que les erreurs puissent être tracées plus facilement. Ces erreurs peuvent être soulevées par le code lui-même ou par des processus séparés exécutés séparément de notre application et le surveillant de l'extérieur.

Pour réduire davantage les erreurs, nous devons nous efforcer de rendre l'interface publique du code aussi simple et claire que possible.

Si vous avez d'autres conseils sur la façon d'appliquer Poka Yoke au développement PHP ou à la programmation générale, n'hésitez pas à partager les commentaires!

lecture plus approfondie

Poka Yoke

  • Poka-Yoke - Le Guide du système de production Toyota décrit le rôle de Poka Yoke dans le processus de fabrication de Toyota.
  • Comment utiliser la technique Poka-Yoke pour améliorer la qualité du logiciel fournit des conseils sur la façon d'utiliser Poka Yoka pour améliorer la qualité des fonctionnalités logicielles.
  • Poka-yoke Votre code donne un bref aperçu de la façon d'appliquer Poka Yoke à la programmation générale.
  • POKA YOKE - L'application de l'épreuve d'erreur aux logiciels fournit un aperçu plus détaillé de la façon d'appliquer Poka Yoke à la programmation.

POKA TYOKE EN PHP

  • PHP extrêmement défensif explique comment rendre votre code PHP plus à l'épreuve des erreurs.
  • 3 Avantages de l'utilisation d'objets immuables Un bref aperçu des avantages des objets immuables.
  • Les objets de valeur immuable dans PHP décrivent brièvement la façon dont nous rendons réellement un objet de valeur immuable (ou du moins aussi immuable que possible).
  • PHP et l'immuabilité explorent plus en profondeur le fonctionnement de l'immuabilité en PHP (et comment cela ne fonctionne pas).
  • Écrire un bon code: comment réduire la charge cognitive de votre code décrit diverses méthodes qui rendent le code plus facile à suivre, en réduisant la possibilité que quelqu'un fasse des erreurs lors de l'utilisation du code ou des modifications.

Les questions fréquemment posées sur Poka-Yoke et Hyper-Defensive Programming

Quel est le but principal de Poka-Yoke dans la programmation?

Poka-Yoke est un terme japonais qui se traduit par "prévention des erreurs". Dans le contexte de la programmation, il s'agit d'une approche de conception défensive conçue pour empêcher les erreurs de se produire. Il s'agit de mettre en œuvre des mesures de sécurité pour éviter les erreurs et garantir que les fonctions sont utilisées correctement. Le but principal de Poka-Yoke dans la programmation est d'améliorer la qualité du logiciel et de réduire les erreurs, ce qui permet d'économiser du temps et des ressources pendant le processus de développement.

En quoi Poka-Yoke est-il différent des méthodes de programmation traditionnelles?

Les méthodes de programmation traditionnelles se concentrent généralement sur la création de code fonctionnel, la gestion des erreurs et la fixation des erreurs sont généralement effectuées après le développement initial. D'un autre côté, Poka-Yoke adopte une approche active, combinant des mécanismes de prévention des erreurs à sa propre étape de développement. Cela conduit à un code plus puissant et fiable, réduisant le besoin de beaucoup de débogage et de test plus tard.

Poka-yoke peut-il être appliqué à n'importe quel langage de programmation?

Oui, Poka-Yoke est un concept qui peut être appliqué à n'importe quel langage de programmation. Ce n'est pas un outil ou une technologie spécifique, mais une façon de penser ou une méthode de programmation. Peu importe la langue que vous utilisez, vous pouvez implémenter le principe Poka-Yoke pour rendre votre code plus résistant aux erreurs.

Quels sont les exemples de Poka-Yoke dans la programmation?

Des exemples de Poka-Yoke en programmation incluent la vérification des entrées (pour s'assurer que les données sont formatées correctement avant le traitement), en utilisant des affirmations pour vérifier l'état du programme à certains moments et la mise en œuvre de défauts de défaillance (pour minimiser en cas de défaillance ) L'action par défaut pour les dommages).

Comment Poka-Yoke favorise-t-il la qualité globale des produits logiciels?

En empêchant les erreurs pendant la phase de développement, Poka-Yoke aide à améliorer la qualité globale des produits logiciels. Il réduit le nombre d'erreurs et de défauts, résultant en un logiciel plus stable et fiable. Cela améliore non seulement l'expérience utilisateur, mais réduit également le coût et l'heure du débogage et de la maintenance.

Poka-Yoke est-il un processus long?

Bien que la mise en œuvre de Poka-Yoke puisse prendre du temps supplémentaire pendant la phase de développement, il peut économiser beaucoup de temps à long terme. En empêchant les erreurs avant leur lieu de se produire, cela réduit le temps consacré aux erreurs de débogage et de fixation, réduisant ainsi le temps de livraison et améliorant l'efficacité du développement.

Quels sont les avantages de Poka-Yoke dans la fabrication?

Dans la fabrication, Poka-Yoke peut aider à prévenir les erreurs et les défauts, produisant ainsi des produits de meilleure qualité. Il peut également augmenter l'efficacité et la productivité en réduisant le temps et les ressources pour les retouches et les réparations.

Comment commencer à implémenter Poka-Yoke dans ma pratique de programmation?

Pour commencer à implémenter Poka-Yoke dans votre pratique de programmation, vous devez d'abord identifier les erreurs courantes ou les points de défaillance potentiels dans votre code. Ensuite, développez des stratégies pour prévenir ces erreurs ou gérez-les gracieusement lorsqu'elles se produisent. Cela peut inclure la vérification des entrées, les affirmations, les défaillances de sécurité ou d'autres techniques de prévention des erreurs.

Poka-yoke peut-il être utilisé pour le développement agile?

Oui, Poka-Yoke peut être utilisé efficacement pour le développement agile. En fait, il correspond bien au principe Agile (livraison fréquente du logiciel Runnable) car il permet de garantir que chaque itération du logiciel est aussi sans erreur que possible.

Poka-yoke est-il uniquement utile pour les grands projets?

Non, Poka-Yoke est bénéfique pour les projets de toute taille. Même pour les petits projets, la prévention des erreurs pendant la phase de développement peut gagner du temps et des ressources et produire des produits finaux de meilleure qualité.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal