[JAVA] Une collection de modèles dont vous voulez être conscient pour ne pas compliquer le code

introduction

Du code initialement simple s'empile: en ajoutant ou en modifiant des fonctions, en corrigeant des bugs, etc., le code se complique progressivement, entraînant une augmentation du coût de correction et une diminution de la qualité. C'est tout à fait naturel pour un produit d'une certaine taille, et vous devez le concevoir consciemment pour éviter que cela ne se produise.

Ici, nous présenterons les méthodes et les modèles dont nous avons généralement connaissance pour ne pas compliquer le code. (Grande quantité de composants DDD et Clean Architecture) De plus, le code qui apparaît dans cet article est Java, mais je pense que vous pouvez le comprendre dans une certaine mesure sans connaître Java.

Aussi, c'est long car je l'ai écrit dans une large gamme sans être cohérent ... Puisqu'il s'agit d'un lien d'ancrage pour chaque élément ci-dessous, veuillez uniquement l'élément qui vous intéresse.

Pourquoi getter / setter n'est pas bon

Obtenez où la logique métier est impliquée

Le getter ici fait référence à une variable d'instance qui peut être référencée de l'extérieur. La raison pour laquelle il est limité "là où la logique métier est impliquée" est qu'il peut ne pas être possible de le traiter sans condition en raison des restrictions du cadre.

Classe utilisant getter


public class Employee {
    private String name; //OK car il ne peut pas être référencé de l'extérieur
    private LocalDate contractDate;
    public int salary; //Les variables d'instance publiques sont interdites car elles peuvent être référencées de l'extérieur
    ...

    //Interdit car les variables d'instance peuvent être référencées de l'extérieur avec getter
    public LocalDate getContractDate() {
        return this.contractDate;
    }
}

Le processus «getContractDate» dans cet exemple, «obtenir la date du contrat d'un employé», n'a aucune implication commerciale. Vous ne savez pas ce que vous voulez faire en obtenant la date du contrat d'un employé. Qu'est-ce qui a été réalisé ici pour identifier les employés en utilisant la date du contrat? S'agit-il d'un changement des informations sur les employés en raison de la date du contrat? ?? Et ainsi de suite, cela seul est devenu insignifiant. Ceci est synonyme de l'utilisation des données acquises jetées du côté de l'utilisateur, et est un report de la conception.

Maintenant, regardons le côté qui utilise la classe dans laquelle le "report de conception" se produit.

Le côté qui utilise la classe dans laquelle le "report de conception" se produit


int salary;
if (employee.getContractDate().isBefore(LocalDate.now())) {
    //Soyez payé pour les employés contractuels(Cela n'inclut pas le jour, mais il y a des détails...)
    salary = employee.salary;
}

Comme mentionné ci-dessus, la logique métier fuit vers la classe qui souhaite utiliser «employé». Si une classe qui veut utiliser ʻemployee` dans le même but sort à l'avenir, un code de copie similaire sera produit en masse partout. Pour éviter cela, écrivez la logique métier directement dans la classe contenant les données comme indiqué ci-dessous.

Classe qui interdit le getter


public class Employee {
    private LocalDate contractDate;
    private int salary;
    ...

    public int calculateSalary() {
        if (this.contractDate.isBefore(LocalDate.now())) {
            return this.salary;
        }
        return 0; //Cette façon de rentrer n'est pas bonne, mais à titre d'exemple...
    }
}

Classe d'utilisateurs


int salary = employee.calculateSalary();

En arrêtant getter et en écrivant la logique métier dans la classe qui contient les données, vous pouvez empêcher la production en masse de code de copie et vous pourrez connaître les règles métier simplement en regardant la classe correspondante.

Setter où la logique métier est impliquée

Le setter ici fait également référence à une variable d'instance dont la valeur peut être modifiée de l'extérieur.

Classe avec setter


public class Employee {
    public LocalDate contractDate;  //Les variables d'instance publique peuvent être affectées de l'extérieur, donc interdit
    private int salary;
    ...

    //Interdit car les variables d'instance peuvent être affectées en externe avec setter
    public void setSalary(int salary) {
        this.salary = salary;
    }
}

Encore une fois, la logique est «setSalary», qui n'a aucune signification commerciale de «changer le salaire d'un employé». Par exemple, il doit s'agir d'une méthode qui exprime le flux et les règles métier, comme "Je l'ai ajouté parce que j'avais de bonnes performances" et "Je l'ai corrigé parce que j'ai fait une erreur dans l'entrée".

De plus, le fait que la valeur puisse être modifiée par setter signifie que l'état va changer, donc ce n'est pas sûr. En particulier, à mesure que le traitement devient plus profond comme indiqué ci-dessous, l'état qui peut être facilement reconnu par les humains est dépassé et la lisibilité se détériore.

methodA(Employee employee)
  └ methodB(Employee employee)
    └ methodC(Employee employee)
      └ methodD(Employee employee) <-C'est impossible si le statut de salarié est réécrit ici!Je ne peux pas me rattraper!
        └ methodE(Employee employee)

Évitez les setters avec des constructeurs complets et des objets immuables

Constructeur complet: Constructeur complet

Un constructeur complet signifie "fixer les valeurs de toutes les variables d'instance avec le constructeur". Surtout dans les classes qui traitent des règles métier, nous garantissons que le constructeur crée les états suivants.

--Les valeurs de toutes les variables d'instance sont déterminées

À titre d'exemple simple, définissez une classe qui exprime la règle métier selon laquelle «les employés ont toujours un contrat (il y a une date de contrat), et ils peuvent contracter jusqu'à 3 mois à l'avance».

public class Employee {
    private LocalDate contractDate;

    public Employee(LocalDate contractDate) {
        if (contractDate == null) {
            throw new IllegalArgumentException("La date du contrat est requise");
        }
        LocalDate currentDate = LocalDate.now();
        if (contractDate.isAfter(currentDate.plusMonths(3))) {
            throw new IllegalArgumentException("Les dates de contrat de plus de 3 mois à l'avance ne sont pas valides");
        }
        this.contractDate = contractDate;
    }
}

En concevant le constructeur de manière à ce que la date du contrat de l'employé soit toujours requise et que la date dépassant 3 mois à l'avance soit ainsi limitée, les restrictions sur les règles commerciales sont observées de force lors de l'utilisation de cette instance. Il est également garanti qu'il s'agit d'une instance sécurisée. De plus, il devient une instance sûre et prévisible quelle que soit la valeur ajoutée, et l'utilisateur peut l'utiliser sans se soucier des choses inutiles.

Après avoir créé une instance une fois, des opérations telles que la modification de l'état ou l'assemblage progressif de certains champs réduisent la sécurité et la lisibilité. La génération d'instance doit toujours être complétée par des opérations atomiques.

Méthode de fabrique statique au lieu du constructeur

Présentation des méthodes de fabrique statiques en relation avec les constructeurs. Vous souhaiterez peut-être créer plusieurs constructeurs surchargés en termes de règles métier. Comme décrit dans le commentaire de l'exemple ci-dessous, cela ressemble à "ce constructeur pour XX" et "ce constructeur pour △△".

public class SearchDateTime {
   private LocalDate date;
   private LocalTime time;

    //Je veux que vous utilisiez ce constructeur sauf le jour...
    public SearchDateTime(LocalDate date, LocalTime time) {
        this.date = date;
        this.time = time;
    }

    //Je veux que vous n'utilisiez le constructeur que le jour...
    public SearchDateTime() {
        this.date = LocalDate.now();
        this.time = LocalTime.now();
        //Peu importe, mais enchaînons le constructeur!
        // this(LocalDate.now(), LocalTime.now());
    }
}

S'il y a plusieurs constructeurs pour ce qui précède, cela est écrit dans le commentaire, mais il est difficile pour l'utilisateur de comprendre lequel utiliser uniquement par la différence des arguments.

La solution à cela est le titre "Méthode de fabrique statique au lieu de constructeur". Préparez une méthode d'usine statique nommée pour chaque utilisation comme indiqué ci-dessous.

public class SearchDateTime {
   private LocalDate date;
   private LocalTime time;

    public static SearchDateTime of(LocalDate date, LocalTime time) {
        return new SearchDateTime(date, time);
    }

    public static SearchDateTime ofToday() {
        return new SearchDateTime(LocalDate.now(), LocalTime.now());
    }

    //Le constructeur est rendu privé et non exposé à l'extérieur
    private SearchDateTime(LocalDate date, LocalTime time) {
        this.date = date;
        this.time = time;
    }
}

Le constructeur d'origine réduit la visibilité à «private» et l'empêche d'être utilisée directement de l'extérieur. L'avantage des méthodes de fabrique statiques est qu'elles peuvent être ** nommées **. En rendant le nom explicite, vous pouvez transmettre votre intention à l'utilisateur sans le rendre ambigu. De plus, comme autre exemple de cas d'utilisation, il est efficace de limiter les objets devant être gérés par le rôle d'administrateur et le rôle normal, et de clarifier les opérations qui ne doivent pas être confondues avec la dénomination et le type d'argument.

Objet immuable

Vous pouvez augmenter la sécurité en vous assurant que la valeur n'est pas modifiée une fois qu'elle est fixée. En définissant le modificateur final sur la variable d'instance, la valeur ne changera pas après l'initialisation dans le constructeur.

public class Employee {
    //Ajouter le modificateur final à toutes les variables d'instance
    private final String name;
    private final LocalDate contractDate;
    private final int salary;

    public Employee(String name, LocalDate contractDate, int salary) {
        this.name = name;
        this.contractDate = contractDate;
        this.salary = salary;
    }

    public void addSalary(int salary) {
        this.salary += salary //Erreur de compilation!!
    }
}

Le but principal de quitter setter est d'en faire un constructeur complet et un objet immuable afin que vous n'ayez pas à vous soucier des changements d'état. Étant donné que la quantité d'informations dont une personne peut se souvenir temporairement est faible, il est important de réduire autant que possible la quantité de préoccupation.

Vous avez maintenant un objet immuable avec une valeur immuable, mais que se passe-t-il si vous souhaitez modifier la valeur, c'est de créer une nouvelle instance et de la renvoyer. Cela ne change pas sa propre valeur.

public class Employee {
    private final String name;
    private final LocalDate contractDate;
    private final int salary;
    ...

    //Retraiter et modifier la date du contrat et le salaire
    public Employee contractRenewal(int salary) {
        return new Employee(this.name, LocalDate.now(), salary);
    }
}

//Côté utilisateur
Employee employee = new Employee(...);
Employee extendedEmployee = employee.contractRenewal(200000); //Traitez comme une instance distincte.

Ne demandez pas la loi de Demetel (dites, ne demandez pas!)

«La loi de Demeter» et «Ne pas demander» sont des principes de conception liés à la dissimulation d'informations. Tout d'abord, regardons les images d'exemples où ce principe de conception n'est pas suivi et les cas où il est suivi.

Exemples de non-respect des principes de conception

--Le service demande

No good.png

Exemple d'observation des principes de conception

--Je commande

good.png

Vous pouvez voir que si vous suivez ce principe de conception, vous obtiendrez une belle forme en V.

Ensuite, regardons le code. À titre d'exemple simple, il montre la logique de «trouver le prix de vente d'un produit avec des frais de coupon».

Ne demandez pas la logique d'utilisateur de la loi de Demeter qui ne suit pas la commande


Product product = new Product(something);
int sellingPrice;
if (product.price.couponExpiration.isAfore(LocalDate.now())) {
    //Renvoie le prix du coupon de l'article si le coupon est valide(Cela n'inclut pas non plus le jour, mais il y a des détails...)
    sellingPrice = product.price.couponValue;
} else {
    //Renvoyer le prix du produit si la date d'expiration du coupon n'est pas valide
    sellingPrice = product.price.value;
}

Dans ce qui précède, une instruction if est générée dans product.price.couponExpiration pour déterminer la date d'expiration du coupon. Nous accédons également au «prix» appartenant au «produit».

Le code amélioré en appliquant «la loi de Demeter» et «Ne pas demander» à ce code est le suivant.

La loi de Demeter et ne pas demander, logique utilisateur


Product product = new Product(something);
int sellingPrice = product.sellingPrice();

La "logique utilisateur ci-dessus dans laquelle la loi de Demeter et la commande de ne pas demander" est complétée par une ligne d'instructions. L'utilisateur ne dispose que de ** connaissances minimales ** et n'a qu'à appeler la commande "trouver le prix de vente".

La classe Product qui incorpore cette" loi de Demeter et ne pas demander "est la suivante.

public class Product {
    private Price price;
    ...
    int sellingPrice() {
      return price.sellingPrice();
    }
}

class Price {
    private int value;
    private int couponValue;
    private LocalDate couponExpiration;
    ...
    int sellingPrice() {
        if expirationDate.isAfore(LocalDate.now()) {
            return couponValue;
        }
        return value;
    }
}

À l'origine, la classe «Price» est en charge du traitement du jugement tel que le branchement qui était du côté de l'utilisateur, et il est composé dans la classe «Product». La logique métier telle que le calcul et le jugement est ** gérée par la classe qui contient les données **. De plus, la classe Price est définie comme package-private, et la méthode sellingPrice est également package-private. En d'autres termes, cette classe et cette méthode ne peuvent pas être vues du côté utilisateur (en supposant un autre package), et l'exécution directe n'est pas autorisée. En faisant cela, vous pouvez réduire de force ** l'exposition aux connaissances **, réduire la dépendance et les coupler vaguement.

Selon la loi de Déméter, c'est l'image. product.png

Séparation des intérêts: Séparation des préoccupations (SoC)

La séparation des intérêts est souvent évoquée dans MVC et l'architecture en couches, mais c'est un principe dont je veux être conscient quelle que soit la granularité. Par exemple, supposons que vous ayez une classe qui traite des «employés». Dans cette catégorie «employé», nous ne nous soucions pas de «comment notifier» si une erreur commerciale se produit. S'il s'agit d'une API, le message doit être emballé dans un format tel que JSON, et s'il s'agit de quelque chose qui affiche un écran comme un site Web, il doit être modifié et généré afin que l'utilisateur puisse le reconnaître facilement. Dans cette classe «employé», «l'occurrence d'une erreur commerciale» et «le contenu de l'erreur commerciale (message d'erreur)» sont les préoccupations qui doivent être connues. Comment le sortir et comment le modifier est une préoccupation d'une autre classe (par exemple, couche de présentation). Si le traitement d'écran côté utilisateur est écrit dans la classe "employé" sans avoir connaissance de la séparation des intérêts ci-dessus, s'il y a un changement dans la méthode de sortie d'écran, la classe "employé" ne devrait pas être pertinente. Sera également corrigé, élargissant la gamme d'influence. Il viole également l'un des principes SOLID, le ** principe de responsabilité unique **. https://qiita.com/UWControl/items/98671f53120ae47ff93a

En séparant les intérêts selon les rôles, en décidant et en délimitant les limites de la responsabilité, il est possible de concevoir des applications faiblement couplées et sécurisées avec moins de dépendance vis-à-vis de l'extérieur. Est possible. Cela conduit également à une réduction de ** l'exposition aux connaissances **.

Dépendances et leurs directions

TL;DR Parce que c'est un peu long.

Principes relatifs aux associations de composants

Un "composant" est un module, ou une unité de pot ou de gemme. Cependant, il n'est pas limité aux composants, mais doit être appliqué à n'importe quel objet. Par conséquent, l'explication ici est basée sur la classe.

** Principe des dépendances acycliques (ADP) **

Le graphe de dépendance des composants ne doit pas avoir de dépendances circulaires

Il y a un principe. Il doit être conçu de manière à ce qu'il n'y ait aucune dépendance circulaire dans aucune structure, pas seulement des «composants». Assurez-vous que la dépendance est vers l'intérieur et ne dépend que d'une seule direction sans circulation. Il est important de ne pas connaître l'extérieur de l'intérieur et de ne pas amener les connaissances et les règles de l'extérieur vers l'intérieur.

** Principe de dépendance à la stabilité: principe des dépendances stables (SDP) **

Dépend de la direction de haute stabilité

En termes de classes, moins il y a de classes qui en dépendent, plus il est stable. Il est également suffisamment stable pour dépendre de sa propre classe. Surtout pour cette dernière raison (plus elle dépend de sa propre classe), l'effet du changement est important et il est difficile de le changer, il doit donc être stable. (Selon la langue, je pense que le nombre d'importations, d'exiger et d'inclure sera une certaine norme.)

Par conséquent, une classe censée changer ne doit pas dépendre d'une classe difficile à changer.

** Principe de stabilité et d'équivalence d'abstraction: principe d'abstractions stables (SAP) **

Le degré d'abstraction d'un composant doit être comparable à sa stabilité

Le principe est que les composants hautement stables doivent également être très abstraits et qu'une stabilité élevée ne doit pas interférer avec l'expansion. À l'inverse, les composants moins stables devraient être plus spécifiques. En effet, la faible stabilité facilite la modification du code spécifique à l'intérieur.

Dans le "Principe de Stabilité Dépendance (SDP)", cela devrait dépendre de la direction de stabilité plus élevée, et dans le "Principe de Stabilité / Abstrait Equivalence (SAP)", la stabilité devrait être proportionnelle au degré d'abstraction. En d'autres termes, ** devrait dépendre de la direction de l'abstraction supérieure **.

Principe d'inversion de dépendance (DIP)

Il est nécessaire de changer la direction de la dépendance afin de réaliser le "principe de dépendance de stabilité (SDP)" et "principe d'équivalence de stabilité et d'abstraction (SAP)". En introduisant une interface comme moyen de le faire, nous pouvons inverser le sens des dépendances et y parvenir.

Jetons un coup d'œil avant d'appliquer DIP. before_dependency_inversion_principle.png Dans ce qui précède "avant changement", la "classe stable" dépend de la "classe instable avec de nombreuses modifications", donc s'il y a un changement dans la dernière classe, cela affectera la première classe. .. Plus précisément, ce qui suit est un exemple.

Voici un état où la dépendance est inversée et le problème est résolu pour un tel problème.

dependency_inversion_principle.png

En s'appuyant sur l'abstraction via l'interface, les «classes instables avec de nombreuses modifications» ne sont plus directement dépendantes. Peu importe combien vous corrigez une "classe instable avec de nombreuses modifications", vous n'avez pas besoin de modifier une "classe stable" tant que vous héritez de l'interface.

Ce qui est important ici est également décrit dans «Principe de dépendance à la stabilité (SDP)» et «Principe d'équivalence de stabilité et d'abstraction (SAP)», mais la classe abstraite dépendante doit être extrêmement stable. ne pas. Par conséquent, la conception de cette pièce est importante.

Lors de l'application effective du principe d'inversion de dépendance (DIP)

Personnellement, je ne pense pas qu'il soit nécessaire d'appliquer à tout prix ce principe de renversement de dépendance (DIP). Particulièrement dans le cas d'une structure simple à l'heure actuelle, il peut être difficile à comprendre en incluant un objet abstrait à une seule couche.

À titre d'exemple concret, je pense qu'il existe de nombreux cas où vous pouvez penser "Je ne veux pas dépendre de cette classe" en inversant la direction de la dépendance.

--Il existe une classe qui n'est pas stable en raison de changements fréquents de spécifications ou de nombreux bogues, et une autre classe est affectée par elle.

Cela devrait être décidé à l'avance dans une certaine mesure en tant que projet, par exemple "jusqu'où peut-il être verrouillé par le cadre".

Bon SEC et mauvais SEC

Généralement, "duplicate is bad" dans le code source. "Ne vous répétez pas" C'est le principe DRY.

Par exemple, supposons que vous ayez plusieurs cas d'utilisation avec des configurations d'écran similaires. Au début de la fabrication, supposons que le même traitement soit standardisé pour le rendre DRY. Cependant, bien que le code était correct à ce moment-là, les exigences de chaque cas d'utilisation sont différentes, donc la ** logique commune ** a une branche if pour chaque utilisateur avec ses propres intentions individuelles, et quelques années plus tard. Peut se transformer en une ** logique commune ** gonflée pleine de branches de jugement.

Pour éviter cela, il est nécessaire de clarifier les domaines problématiques (domaines) auxquels le produit traite, tels que les domaines qui intéressent l'entreprise. En plus de cela, vous devez déterminer soigneusement s'il est ** accidentellement dupliqué ** ou ** vraiment dupliqué **. Dans le premier cas, il n'est pas nécessaire de le faire DRY car il n'est que ** accidentellement dupliqué **.

Il s'agit d'un problème qui est plus susceptible de se produire lorsque le produit devient plus gros. Il est particulièrement important de noter que les classes qui gèrent les règles métier peuvent avoir des noms tels que «common» et «base».

Nom important

Pour une raison quelconque, c'est la ** question la plus importante ** pour la programmation. Le contenu lié au nom est mentionné dans de nombreux autres articles, je ne donnerai donc qu'un aperçu, mais je pense que les livres suivants seront très utiles.

Après cela, je résumerai la dénomination de «la partie qui gère les règles métier».

Dans DDD, il y a «conception stratégique» et «conception tactique».

L'important est que "le nom propre ne peut être décidé que si la conception stratégique est décidée". Puisque je l'ai mentionné dans l'article de DRY, je le republierai, mais quel est le domaine qui intéresse notre entreprise, et s'il y a quelque chose qui peut être divisé dans ce domaine, etc., le domaine problématique (domaine) traité par le produit est décrit. La tâche de clarifier et de décider du nom du paquet et du nom de la classe en fonction de celui-ci est nécessaire pour une bonne dénomination.

finalement

Il est important de ne pas terminer les méthodes et modèles décrits jusqu'à présent avec une seule application, mais de regarder en arrière encore et encore si cela ne convient pas. À ce moment-là, je pense que même si vous pensez avoir écrit du bon code, si vous y repensez plus tard, ce sera un mauvais code. Cependant, avec un code qui a été conscient d'une telle conception, le coût de la modification devrait être beaucoup plus bas qu'auparavant et il devrait être plus facile à changer. Il est important d'apporter constamment des améliorations et de répéter de petites refactorisations.

Recommended Posts

Une collection de modèles dont vous voulez être conscient pour ne pas compliquer le code
Un exemple d'astuce lorsque vous souhaitez diviser la valeur de la définition en fonction de l'environnement mais ne voulez pas en être conscient
7 choses que je veux que tu gardes pour que ça ne devienne pas un putain de code
Être conscient du code facile à lire
Un mémo lorsque vous souhaitez effacer la partie horaire de l'agenda
L'histoire de l'introduction de Gradle en tant que modernisation d'un système existant qui ne gérait pas de packages
Règles de base à connaître pour écrire du code facile à lire
Même en Java, je veux sortir vrai avec un == 1 && a == 2 && a == 3 (magie grise qui n'est pas tant que magie noire)
[Active Admin] Je souhaite spécifier l'étendue de la collection à afficher dans select_box
Lorsque vous souhaitez modifier le libellé à afficher lors de la création d'une zone de sélection à partir d'énumération
Un mémo du programme que vous pouvez réaliser que la probabilité de lancer des dés est d'environ 1/6
Éléments à prendre en compte lors de l'écriture de code en Java
Traitement inutile des collections - je veux vous donner une chance d'écrire du bon code. 5 [Exemple de refactoring C #]
L'histoire de Collectors.groupingBy que je veux garder pour la postérité
Une histoire qui pourrait être mise en œuvre proprement en utilisant le polymorphisme lorsque vous souhaitez exprimer deux types de données dans une table
Créer une ArrayList qui vous permet de lancer et de récupérer les coordonnées d'un plan bidimensionnel
Correspondant à "erreur que l'authentification de base ne réussit pas" dans le code de test "L'histoire qui n'a pas pu être faite"
[Java] Cela peut être heureux si la valeur de retour de la méthode qui retourne null est facultative <>
[Java] Comparaison d'équivalence où les débutants échouent dans la comparaison de chaînes de caractères. Vous n'êtes peut-être même pas conscient de l'erreur! ??
[RSpec] Lorsque vous souhaitez utiliser la variable d'instance du contrôleur dans le test [attribuer n'est pas recommandé]
À propos du sujet qui a tendance à être confondu avec ARG de Dockerfile qui est une construction en plusieurs étapes
Ne pas être un oncle statique
Un mémo sobrement accro à la demande de multipart / form-data
Comment interagir avec un serveur qui ne plante pas l'application
Une collection de phrases qui impressionne le "sentiment différent" de Java et de JavaScript
A vous qui regrettez que la conversion de JODConverter + LibreOffice soit lente
Notez que VS Code + JUnit 5 peut être utilisé sous Windows 10
Je veux que vous utilisiez Enum # name () pour la clé de SharedPreference
Si vous souhaitez satisfaire la couverture de test des méthodes privées dans JUnit
Comment créer une route directement à partir de l'URL que vous souhaitez spécifier + α
[Android] Je souhaite créer un ViewPager pouvant être utilisé pour les didacticiels
Considérez RxJava comme une bibliothèque qui facilite l'écriture du traitement asynchrone
Vous pouvez ne pas vouloir utiliser la méthode remove sur ArrayList très souvent
Résumer le cycle de vie des objets Java à prendre en compte dans le développement Android
Je veux que vous utilisiez Scala comme meilleur Java pour le moment
Je veux obtenir récursivement la superclasse et l'interface d'une certaine classe
Essayez d'enregistrer les données pouvant être lues par JavaFX au format PNG
[outil java] Un outil utile lorsque vous souhaitez envoyer régulièrement le journal ipmsg de PC-A au PC spécifié.
Je veux que vous racontiez que l'erreur a été résolue lorsque vous avez poignardé le chargeur dans le coin de la tête
Je veux trouver la somme de contrôle MD5 d'un fichier en Java et obtenir le résultat sous forme de chaîne de caractères en notation hexadécimale.
Une histoire que les personnes qui ont fait iOS solidement peuvent être accro à la mise en œuvre de Listener lors du passage à Android
J'ai utilisé Docker pour mon portfolio en tant que débutant, donc j'espère que même 1 mm sera utile à quelqu'un.