• les dernières technologies innovantes en cours de développement ou en phase de lancement sur le marché.
  • les critiques de produits technologiques populaires, tels que les téléphones intelligents, les ordinateurs portables, les tablettes, les écouteurs, les caméras, etc.
  • des conseils sur la façon de rester en sécurité en ligne, notamment en ce qui concerne la protection de la vie privée, la sécurité des données et la prévention des escroqueries en ligne.
  • les dernières nouvelles et mises à jour dans le monde de la technologie, y compris les fusions et acquisitions, les partenariats, les changements de direction, etc.
Affichage des articles dont le libellé est Java. Afficher tous les articles
Affichage des articles dont le libellé est Java. Afficher tous les articles

jeudi 16 mai 2024

Les Nouveautés de Java 21


Java 21 est enfin disponible ! En plus d'être une version LTS (Long Term Support), elle regorge d'innovations. Vous êtes un peu perdu ? Explorons ces nouveautés ensemble.

Nouveautés de l'API

Ces fonctionnalités sont immédiatement utilisables dans Java 21.

Collections Séquencées (JEP 431)

Un rafraîchissement bienvenu de l'API Collection ! La gestion des collections ordonnées est enfin là. Trois nouvelles interfaces sont ajoutées :

  • SequencedCollection
  • SequencedSet
  • SequencedMap

Ces interfaces fournissent des méthodes cohérentes pour manipuler ou ajouter des éléments en début ou en fin de collection.

Illustration des Collections Séquencées

Notez que ArrayList, qui implémentait déjà List, est maintenant aussi une SequencedCollection.

Pattern pour les Records (JEP 440)

Initialement en preview, cette fonctionnalité est maintenant officielle, permettant de déconstruire un record pour accéder directement à ses composants. Par exemple :

record Point(int x, int y) {}

if (o instanceof Point(int a, int b)) {
    int sum = a + b;
}

Cela fonctionne aussi dans les instructions switch :

int sum = switch (o) {
    case Point(int a, int b) -> a + b;
    default -> 0;
};

Threads Virtuels (JEP 444)

Très attendue, cette fonctionnalité est enfin stable ! Pour plus de détails, vous pouvez consulter mon article précédent.

Autres ajouts

Quelques autres nouveautés intéressantes :

  • Gestion des emojis avec Character.isEmoji()
  • StringBuilder.repeat() pour répéter une chaîne de caractères
  • HttpClient implémente AutoCloseable et peut être utilisé dans un bloc try-with-resources

Nouveautés en Preview

Ces fonctionnalités ne sont pas activées par défaut. Pour les utiliser, ajoutez les options de compilation :

--release 21 --enable-preview

et pour l'exécution :

--enable-preview

Modèles de Chaînes (JEP 430)

Permet d'intégrer des expressions dans les chaînes de caractères avec interpolation. Par exemple :

String str = "World";
String result = STR."Hello \{str}";

Utilisez la notation \{expression} et un Processor comme STR pour traiter l'interpolation. Le JDK inclut trois Processors par défaut :

  • RAW : Pas d'interpolation
  • STR : Concatène les chaînes
  • FMT : Formatte les chaînes en utilisant un formateur

Vous pouvez également créer votre propre Processor en implémentant l'interface StringTemplate.Processor.

Variables et Patterns Anonymes (JEP 443)

Parfois, il est nécessaire de déclarer une variable sans l'utiliser. Cette fonctionnalité permet d'utiliser le caractère '_' (underscore) pour ces cas. Par exemple :

for (Element _ : elements) {
    // Bloc qui n'utilise pas les éléments
}

var _ = mySet.remove(myObject);

try {
    int i = Integer.parseInt(str);
} catch (NumberFormatException _) {
    logger.warn("Not a number");
}

Map> employees = new HashMap<>();
employees.computeIfAbsent(id, _ -> new ArrayList<>());

Cela fonctionne aussi avec le pattern matching :

if (object instanceof Point(int _, int y)) {
    // ...
}

int result = switch (obj) {
    case Point(int x, int _) -> x;
};

Classes et Méthodes Main Sans Nom (JEP 445)

Pour simplifier l'apprentissage de Java, cette fonctionnalité permet de créer des classes sans nom avec une méthode main simplifiée. Par exemple, au lieu de :

public class HelloWorld { 
    public static void main(String[] args) { 
        System.out.println("Hello World!");
    }
}

On peut écrire :

class HelloWorld { 
    void main() { 
        System.out.println("Hello World!");
    }
}

Ou même :

void main() { 
    System.out.println("Hello World!");
}

Les classes sans nom simplifient l'écriture de la méthode main, facilitant l'apprentissage de Java.

Conclusion

Java 21 apporte une série d'améliorations et de nouvelles fonctionnalités visant à rendre le langage plus moderne, performant et facile à utiliser. Avec le pattern matching, les records améliorés, la nouvelle API FFM, les threads virtuels et les valeurs à portée, cette version offre des outils puissants pour les développeurs cherchant à écrire du code plus propre, plus efficace et plus sûr. Les améliorations continues de la sécurité et des performances font de Java 21 une mise à jour incontournable pour les développeurs et les entreprises utilisant cette plate-forme pour leurs applications critiques.

mercredi 8 mai 2024

Intégration de l'architecture hexagonale dans un projet Quarkus avec des exemples Java



L'architecture hexagonale, également connue sous le nom de portes et adaptateurs ou architecture en oignon, est un modèle de conception logicielle qui favorise la séparation des préoccupations et la facilité de testabilité. En mettant l'accent sur la dépendance inverse, elle permet de créer des systèmes hautement modulaires et évolutifs. Dans cet article, nous explorerons comment intégrer l'architecture hexagonale dans un projet Quarkus en utilisant des exemples concrets en Java.

Comprendre l'architecture hexagonale

L'architecture hexagonale repose sur le principe de séparation des préoccupations en divisant l'application en trois couches principales : le domaine, les adaptateurs et l'infrastructure.

  • Le domaine représente le cœur métier de l'application, il contient les entités, les règles métier et les services.
  • Les adaptateurs, également appelés ports, sont les points d'entrée et de sortie de l'application. Ils permettent d'interagir avec le domaine en fournissant des interfaces pour les entrées (ports primaires) et les sorties (ports secondaires).
  • L'infrastructure est responsable de la mise en œuvre des adaptateurs. Elle comprend les bases de données, les API externes, les frameworks, etc.

Intégration de l'architecture hexagonale dans Quarkus

Quarkus est un framework Java open source conçu pour créer des applications cloud natives. Il offre une prise en charge de premier ordre pour les conteneurs, la réactivité et le développement rapide. Intégrer l'architecture hexagonale dans un projet Quarkus peut être réalisé en suivant quelques principes de conception simples.

Création du domaine

Commençons par définir le domaine de notre application. Supposons que nous construisons une application de gestion de tâches. Nous pouvons avoir des entités telles que Task, User, et des services comme TaskService qui implémentent la logique métier.


public class Task {
    private String id;
    private String title;
    private boolean completed;
    // Getters and setters
}

public interface TaskService {
    Task createTask(String title);
    Task markTaskAsCompleted(String taskId);
    List<Task> getAllTasks();
    // Other business methods
}
    

Implémentation des adaptateurs

Les adaptateurs fournissent des interfaces pour interagir avec le domaine. Dans notre exemple, nous pourrions avoir des adaptateurs pour interagir avec une base de données, une API REST, ou toute autre source de données.


@ApplicationScoped
public class TaskRepository implements TaskService {
    @Transactional
    public Task createTask(String title) {
        // Logic to persist task in database
    }

    @Transactional
    public Task markTaskAsCompleted(String taskId) {
        // Logic to mark task as completed in database
    }

    @Transactional
    public List<Task> getAllTasks() {
        // Logic to retrieve all tasks from database
    }
    // Other database interactions
}
    

Configuration de l'infrastructure

Dans Quarkus, la configuration de l'infrastructure peut être réalisée en utilisant les fonctionnalités natives de Quarkus pour la persistance des données, les appels REST, etc.


@QuarkusTest
public class TaskResourceTest {

    @Inject
    TaskService taskService;

    @Test
    public void testCreateTask() {
        // Test logic to create task via REST API
    }

    @Test
    public void testMarkTaskAsCompleted() {
        // Test logic to mark task as completed via REST API
    }

    @Test
    public void testGetAllTasks() {
        // Test logic to get all tasks via REST API
    }
}
    

Conclusion

En utilisant l'architecture hexagonale dans un projet Quarkus, nous pouvons créer des applications bien structurées, modulaires et faciles à tester. En séparant clairement le domaine des adaptateurs et de l'infrastructure, nous obtenons une meilleure maintenabilité et évolutivité de notre code. En combinant la flexibilité de Quarkus avec les principes de conception solides de l'architecture hexagonale, nous pouvons construire des applications Java robustes pour répondre aux besoins métier les plus exigeants.

mercredi 29 mars 2023

Event Sourcing en Java: Comment stocker et reconstruire l'état d'un système avec fiabilité


L'Event Sourcing est une technique de développement de logiciels qui a gagné en popularité ces dernières années en raison de sa capacité à améliorer la résilience et la scalabilité des systèmes. En utilisant cette technique, un développeur peut stocker l'état d'un système en enregistrant une séquence d'événements plutôt que de stocker uniquement l'état actuel. Dans cet article, nous allons examiner comment implémenter l'Event Sourcing en Java en utilisant un exemple concret.


Qu'est-ce que l'Event Sourcing ?


L'Event Sourcing est une approche de développement de logiciels dans laquelle l'état du système est stocké sous forme de séquence d'événements. Chaque événement représente une modification de l'état du système à un moment précis. En utilisant cette technique, les développeurs peuvent facilement récupérer l'état du système à n'importe quel moment en rejouant simplement les événements.


Cette approche a plusieurs avantages. Tout d'abord, elle offre une résilience accrue. En enregistrant chaque événement, le développeur peut facilement retracer l'historique de toutes les modifications apportées au système. Cela facilite également la détection et la correction des erreurs. De plus, l'Event Sourcing offre une scalabilité améliorée, car il est facile de partitionner les événements pour les stocker dans différents emplacements.


Comment implémenter l'Event Sourcing en Java ?


En Java, l'Event Sourcing peut être implémenté en utilisant une base de données NoSQL. Les bases de données NoSQL sont particulièrement bien adaptées à cette approche car elles offrent une flexibilité accrue par rapport aux bases de données relationnelles traditionnelles.

Exemple concret

Pour illustrer comment implémenter l'Event Sourcing en Java, prenons l'exemple d'un système de réservation de vols.

1. Créer des événements

Pour chaque action effectuée dans le système de réservation de vols, nous créons un événement correspondant. Par exemple, lorsque nous créons une nouvelle réservation, nous créons un événement "ReservationCreatedEvent". Chaque événement est représenté par une classe Java qui contient les informations nécessaires pour décrire l'action effectuée.

Exemple de classe d'événement

public class ReservationCreatedEvent {
private UUID reservationId;
private String passengerName;
private String flightNumber;

public ReservationCreatedEvent(UUID reservationId, String passengerName, String flightNumber) {
	this.reservationId = reservationId;
	this.passengerName = passengerName;
	this.flightNumber = flightNumber;
}

// getters and setters
}

2. Stocker les événements

Pour stocker les événements, nous utilisons une base de données NoSQL telle que Cassandra. Nous créons une table "Reservations" qui contient une colonne "events" de type liste. Pour ajouter un nouvel événement à une réservation, nous pouvons utiliser la méthode "saveEvent" de notre repository.

Exemple de repository

public class ReservationRepository {
private static final String INSERT_EVENT_QUERY = "UPDATE Reservations SET events = events + ? WHERE id = ?";
private static final String SELECT_EVENTS_QUERY = "SELECT events FROM Reservations WHERE id = ?";

private final Session session;

public ReservationRepository(Session session) {
	this.session = session;
}

public void saveEvent(UUID id, Object event) {
	session.execute(INSERT_EVENT_QUERY, id, toJson(event));
}

public List getEvents(UUID id) {
	ResultSet result = session.execute(SELECT_EVENTS_QUERY, id);
	Row row = result.one();
	List events = new ArrayList<>();
	if (row != null) {
		List<JsonNode> jsonNodes = row.getList("events", JsonNode.class);
		jsonNodes.forEach(jsonNode -> events.add(fromJson(jsonNode)));
	}
	return events;
}

private JsonNode toJson(Object object) {
	ObjectMapper mapper = new ObjectMapper();
	return mapper.valueToTree(object);
}

private Object fromJson(JsonNode jsonNode) {
	try {
		ObjectMapper mapper = new ObjectMapper();
		Class<?> clazz = Class.forName(jsonNode.get("type").asText());
		return mapper.treeToValue(jsonNode, clazz);
	} catch (ClassNotFoundException | JsonProcessingException e) {
		throw new RuntimeException(e);
	}
}
}

3. Rejouer les événements

Pour récupérer l'état actuel d'une réservation, nous devons rejouer tous les événements associés à cette réservation. Pour ce faire, nous pouvons utiliser la méthode "getEvents" de notre repository pour récupérer tous les événements associés à une réservation, puis les rejouer dans l'ordre chronologique pour reconstruire l'état actuel de la réservation.

Exemple de méthode pour récupérer l'état actuel d'une réservation

public class ReservationService {
private final ReservationRepository reservationRepository;

public ReservationService(ReservationRepository reservationRepository) {
	this.reservationRepository = reservationRepository;
}

public Reservation getReservation(UUID id) {
	List events = reservationRepository.getEvents(id);
	Reservation reservation = new Reservation(id);
	events.forEach(event -> {
		if (event instanceof ReservationCreatedEvent) {
			reservation.createReservation((ReservationCreatedEvent) event);
		} else if (event instanceof ReservationCancelledEvent) {
			reservation.cancelReservation((ReservationCancelledEvent) event);
		}
		// add more else-if clauses for other types of events
	});
	return reservation;
}
}

Dans cet exemple, la classe "Reservation" représente l'état actuel d'une réservation. Lorsque nous rejouons les événements associés à une réservation, nous utilisons les méthodes "createReservation" et "cancelReservation" pour mettre à jour l'état de la réservation en fonction des événements.

Avantages de l'Event Sourcing

L'Event Sourcing présente plusieurs avantages par rapport aux méthodes traditionnelles de développement de logiciels:

  • Réversibilité: En stockant tous les événements qui se produisent dans un système, nous pouvons facilement revenir en arrière en rejouant simplement les événements dans l'ordre inverse.
  • Reconstruction de l'état: En rejouant tous les événements associés à une entité, nous pouvons reconstruire son état actuel de manière fiable.
  • Facilité de mise à l'échelle: En stockant les événements dans une base de données NoSQL, nous pouvons facilement mettre à l'échelle horizontalement en ajoutant des nœuds à notre cluster.
  • Meilleure traçabilité: En enregistrant tous les événements qui se produisent dans un système, nous pouvons facilement retracer l'historique de toutes les actions effectuées.

Conclusion

L'Event Sourcing est une technique de développement de logiciels puissante qui peut être utilisée pour construire des systèmes fiables, évolutifs et traçables. En utilisant l'Event Sourcing en Java, nous pouvons facilement stocker tous les événements qui se produisent dans un système, récupérer l'état actuel de ce système en rejouant ces événements, et profiter des avantages que cette approche offre.

Guide complet de CQRS (Command Query Responsibility Segregation) en Java pour des performances et une évolutivité améliorées

CQRS (Command Query Responsibility Segregation) - une approche de développement pour améliorer les performances et la scalabilité des applications

L'approche CQRS (Command Query Responsibility Segregation) est une approche de développement qui consiste à séparer les opérations d'écriture et de lecture en utilisant des modèles de données différents. En utilisant cette approche, nous pouvons optimiser les performances et la scalabilité des applications en réduisant la charge sur la base de données et en permettant une gestion plus efficace de l'état de l'application.

Comment fonctionne CQRS ?

CQRS divise une application en deux parties distinctes : une partie pour les opérations d'écriture (ou de commande) et une partie pour les opérations de lecture (ou de requête).

La partie pour les opérations d'écriture traite les commandes pour créer, mettre à jour ou supprimer des données dans l'application. Cette partie est souvent appelée le modèle de commande.

La partie pour les opérations de lecture traite les requêtes pour récupérer des données de l'application. Cette partie est souvent appelée le modèle de requête.

En utilisant des modèles de données différents pour chaque type d'opération, nous pouvons optimiser les performances et simplifier la gestion de l'état de l'application.

Exemple de code Java utilisant CQRS

Voici un exemple de code Java utilisant CQRS pour gérer les opérations de commande et de requête pour une application de gestion de commandes :


public class CreateOrderCommand {
    private final String customerId;
    private final List orderLines;

    public CreateOrderCommand(String customerId, List orderLines) {
        this.customerId = customerId;
        this.orderLines = orderLines;
    }

    public String getCustomerId() {
        return customerId;
    }

    public List getOrderLines() {
        return orderLines;
    }
}

Ensuite, nous créons une classe CommandHandler pour gérer les commandes :


public class CreateOrderCommandHandler {
    private final OrderRepository orderRepository;

    public CreateOrderCommandHandler(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public void handle(CreateOrderCommand command) {
        Order order = new Order(command.getCustomerId(), command.getOrderLines());
        orderRepository.save(order);
    }
}

Maintenant, nous créons une classe Query pour gérer les opérations de lecture :


public class GetOrderQuery {
    private final String orderId;

    public GetOrderQuery(String orderId) {
        this.orderId = orderId;
    }

    public String getOrderId() {
        return orderId;
    }
}

Ensuite, nous créons une classe QueryHandler pour gérer les requêtes :


public class GetOrderQueryHandler {
    private final OrderRepository orderRepository;

    public GetOrderQueryHandler(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public Order handle(GetOrderQuery query) {
        return orderRepository.findById(query.getOrderId());
    }
}

Enfin, nous créons une classe Controller pour gérer les requêtes HTTP et les commandes :
@RestController
public class OrderController {
private final CreateOrderCommandHandler createOrderCommandHandler;
private final GetOrderQueryHandler getOrderQueryHandler;
public OrderController(CreateOrderCommandHandler createOrderCommandHandler,
                       GetOrderQueryHandler getOrderQueryHandler) {
    this.createOrderCommandHandler = createOrderCommandHandler;
    this.getOrderQueryHandler = getOrderQueryHandler;
}

@PostMapping("/orders")
public ResponseEntity createOrder(@RequestBody CreateOrderCommand command) {
    createOrderCommandHandler.handle(command);
    return ResponseEntity.ok().build();
}

@GetMapping("/orders/{orderId}")
public ResponseEntity getOrder(@PathVariable String orderId) {
    GetOrderQuery query = new GetOrderQuery(orderId);
    Order order = getOrderQueryHandler.handle(query);
    return ResponseEntity.ok(order);
}
}

Dans cet exemple, la classe CreateOrderCommand représente une commande pour créer une nouvelle commande avec un client et une liste de lignes de commande. Le CreateOrderCommandHandler gère cette commande en créant une nouvelle commande et en la sauvegardant dans le dépôt d'ordres.

La classe GetOrderQuery représente une requête pour obtenir une commande existante en utilisant son identifiant. Le GetOrderQueryHandler gère cette requête en recherchant la commande correspondante dans le dépôt d'ordres et en la renvoyant.

Enfin, la classe OrderController utilise ces deux classes pour exposer des API REST pour les opérations de création et de lecture des commandes.

Avantages de CQRS

L'utilisation de l'approche CQRS peut offrir plusieurs avantages pour les applications, notamment :

  • Amélioration des performances : En séparant les modèles de données pour les opérations d'écriture et de lecture, nous pouvons optimiser les performances en réduisant la charge sur la base de données et en permettant une gestion plus efficace de l'état de l'application.
  • Scalabilité améliorée : En permettant une gestion plus efficace de l'état de l'application, nous pouvons rendre l'application plus facilement scalable en ajoutant des nœuds supplémentaires.
  • Meilleure évolutivité : En séparant les modèles de données, nous pouvons rendre l'application plus facilement évolutive en permettant des modifications indépendantes des opérations de commande et de requête.
  • Meilleure séparation des responsabilités : En séparant les opérations de commande et de requête, nous pouvons simplifier la gestion de l'état de l'application et réduire les risques de bogues.

Conclusion

CQRS (Command Query Responsibility Segregation) est une approche de développement qui peut aider à améliorer les performances, la scalabilité et l'évolutivité des applications. En séparant les opérations d'écriture et de lecture en utilisant des modèles de données différents, nous pouvons simplifier la gestion de l'état de l'application et optimiser les performances de la base de données. Bien que l'approche CQRS puisse être plus complexe à mettre en œuvre que d'autres approches de développement, elle peut offrir des avantages significatifs pour les applications à forte charge ou à forte concurrence.

mardi 21 mars 2023

Guide complet des méthodologies TDD et BDD en Java : avantages, inconvénients et comment choisir la meilleure approche pour votre projet

TDD et BDD en Java

Les approches de développement piloté par les tests (Test-Driven Development ou TDD) et de développement piloté par le comportement (Behavior-Driven Development ou BDD) sont deux méthodologies de développement logiciel très populaires en Java. Dans cet article, nous allons explorer ces deux approches et discuter de leur utilisation en Java.

Le développement piloté par les tests (TDD)

Le TDD est une approche de développement logiciel qui consiste à écrire des tests unitaires avant d'écrire le code de l'application. Cette approche de développement permet de s'assurer que chaque morceau de code est testé et fonctionne comme prévu avant d'être intégré à l'ensemble de l'application. Le processus TDD consiste en trois étapes :

  1. Écrire un test : Écrire un test unitaire qui décrive le comportement attendu du code. Le test doit échouer car le code n'a pas encore été écrit.
  2. Écrire le code : Écrire le code nécessaire pour passer le test.
  3. Refactoriser le code : Améliorer le code pour qu'il soit plus lisible, plus efficace et plus maintenable.

En Java, JUnit est une bibliothèque de test unitaire très populaire pour le développement piloté par les tests. Les développeurs Java peuvent utiliser JUnit pour écrire des tests unitaires qui couvrent chaque morceau de code de leur application. JUnit fournit également des outils pour exécuter les tests et signaler les erreurs.

Le développement piloté par le comportement (BDD)

Le BDD est une approche de développement logiciel qui se concentre sur le comportement attendu de l'application plutôt que sur les fonctionnalités individuelles. Le BDD encourage les développeurs à décrire le comportement attendu de l'application en utilisant un langage naturel, comme l'anglais, plutôt que de se concentrer sur les détails techniques. Le processus BDD consiste en trois étapes :

  1. Décrire le comportement : Écrire un scénario de test en utilisant un langage naturel pour décrire le comportement attendu de l'application.
  2. Implémenter le comportement : Écrire le code nécessaire pour que l'application se comporte comme décrit dans le scénario de test.
  3. Vérifier le comportement : Exécuter le scénario de test pour vérifier que l'application se comporte comme prévu.

En Java, Cucumber est une bibliothèque de test BDD populaire pour le développement piloté par le comportement. Les développeurs Java peuvent utiliser Cucumber pour écrire des scénarios de test en langage naturel qui décrivent le comportement attendu de leur application. Cucumber fournit également des outils pour exécuter les scénarios de test et signaler les erreurs.

Avantages et inconvénients du TDD

Avantages du TDD :

  • Le TDD garantit que chaque morceau de code est testé et fonctionne comme prévu avant d'être intégré à l'ensemble de l'application.
  • Le TDD aide à éviter les erreurs de programmation et à améliorer la qualité globale du code.
  • Le TDD permet de détecter rapidement les erreurs de code et de les corriger avant qu'elles ne deviennent des problèmes plus importants.

Inconvénients du TDD :

  • Le processus TDD peut être plus lent car les développeurs doivent écrire des tests avant d'écrire du code.
  • Les tests unitaires peuvent nécessiter beaucoup de temps et d'efforts pour être écrits et maintenus.
  • Le TDD peut être difficile à appliquer dans des projets très complexes.

Avantages et inconvénients du BDD

Avantages du BDD :

  • Le langage naturel utilisé dans les scénarios de test est facile à comprendre pour les non-techniciens.
  • Le BDD aide à garantir que l'application répond aux besoins des utilisateurs.
  • Le BDD encourage la collaboration entre les membres de l'équipe de développement et les parties prenantes de l'application.

Inconvénients du BDD :

  • Le processus BDD peut être plus lent car les scénarios de test doivent être écrits avant de pouvoir écrire du code.
  • Le BDD peut nécessiter des compétences en rédaction de scénarios de test et en communication pour décrire le comportement attendu de l'application.
  • Le BDD peut être difficile à appliquer dans des projets très techniques ou très complexes.

Exemple de code pour le TDD en Java

Voici un exemple simple de code en Java pour illustrer le TDD :

Supposons que vous écriviez une méthode pour ajouter deux nombres entiers et que vous souhaitez l'implémenter en utilisant le TDD. Vous commencez par écrire le test pour la méthode :

public class CalculatorTest {

   @Test
   public void testAddition() {
      Calculator calculator = new Calculator();
      assertEquals(5, calculator.add(2, 3));
   }
}

Ce test vérifie que la méthode add() de la classe Calculator retourne la somme de deux nombres entiers. Maintenant, vous pouvez écrire le code de la méthode add() :

public class Calculator {

   public int add(int a, int b) {
      return a + b;
   }
}

Vous pouvez exécuter le test pour vérifier que la méthode add() fonctionne correctement.

Exemple de code pour le BDD en Java avec Cucumber

Voici un exemple simple de code en Java pour illustrer le BDD avec Cucumber :

Supposons que vous écriviez une méthode pour ajouter deux nombres entiers et que vous souhaitez l'implémenter en utilisant le BDD avec Cucumber. Vous commencez par écrire le scénario de test en langage naturel :

Feature: Addition
   As a user
   I want to be able to add two numbers
   So that I can perform simple calculations

Scenario: Add two numbers
   Given I have entered 2 into the calculator
   And I have entered 3 into the calculator
   When I press add
   Then the result should be 5 on the screen

Ce scénario décrit le comportement attendu de la méthode add(). Maintenant, vous pouvez écrire les étapes de test correspondantes en Java :

public class CalculatorSteps {

   private Calculator calculator;
   private int result;

   @Given("^I have entered (\\d+) into the calculator$")
   public void i_have_entered_into_the_calculator(int arg1) {
      calculator = new Calculator();
      calculator.enter(arg1);
   }

   @When("^I press add$")
   public void i_press_add() {
      result = calculator.add();
   }

   @Then("^the result should be (\\d+) on the screen$")
   public void the_result_should_be_on_the_screen(int arg1) {
      assertEquals(arg1, result);
   }
}

Ces étapes correspondent aux étapes du scénario de test. Vous pouvez maintenant écrire le code de la méthode add() :

public class Calculator {

   private int a;
   private int b;

   public void enter(int num) {
      if (a == 0)
         a = num;
      else
         b = num;
   }

   public int add() {



      return a + b;
   }
}

Vous pouvez exécuter le scénario de test avec Cucumber pour vérifier que la méthode add() fonctionne correctement.

Conclusion

Le TDD et le BDD sont deux approches de développement logiciel qui visent à améliorer la qualité du code et la satisfaction des utilisateurs. En utilisant ces approches, les développeurs peuvent écrire des tests pour chaque fonctionnalité avant de les implémenter, ce qui permet de s'assurer que le code fonctionne correctement et de réduire les erreurs de programmation. Le BDD en particulier utilise un langage naturel pour décrire le comportement attendu du logiciel, ce qui peut aider les développeurs et les utilisateurs à mieux comprendre le logiciel.

En utilisant des outils tels que JUnit et Cucumber, les développeurs Java peuvent facilement mettre en œuvre le TDD et le BDD dans leur flux de travail de développement logiciel. En écrivant des tests pour chaque fonctionnalité et en s'assurant que le code est conforme aux spécifications, les développeurs peuvent créer des logiciels plus fiables et plus robustes.

Les approches TDD et BDD sont deux méthodologies de développement logiciel très populaires en Java. Le choix entre TDD et BDD dépend des besoins de l'application et des préférences de l'équipe de développement. Quelle que soit l'approche choisie, il est important de s'assurer que chaque morceau de code est testé et fonctionne comme prévu avant d'être intégré à l'ensemble de l'application.

mercredi 15 mars 2023

Java 11 : Nouvelles fonctionnalités et exemples de code pour améliorer votre développement

Java 11 est la version la plus récente de la plate-forme de développement Java Standard. Cette version a été publiée en septembre 2018 et apporte de nombreuses améliorations par rapport à ses prédécesseurs.

Spécification HTTP/2

L'une des principales fonctionnalités de Java 11 est la mise en œuvre de la spécification HTTP/2. Cette nouvelle fonctionnalité permet d'améliorer les performances de communication entre les clients et les serveurs en utilisant un protocole plus efficace que HTTP/1.1. En outre, Java 11 introduit également un nouveau module HTTP Client qui permet aux développeurs de créer des applications de manière plus facile et plus efficace en utilisant HTTP/2.

Améliorations de performances

Java 11 inclut également de nombreuses autres améliorations de performances, telles que des améliorations de l'ordonnanceur de threads et une meilleure gestion de la mémoire. Ces améliorations se traduisent par des temps de réponse plus rapides pour les applications Java.

API pour les chaînes de caractères

En outre, Java 11 inclut également une nouvelle API pour les chaînes de caractères, qui permet aux développeurs de manipuler facilement les chaînes de caractères. Cette API offre des fonctions telles que strip(), qui permet de supprimer les espaces blancs en début et en fin de chaîne, ainsi que des méthodes pour vérifier si une chaîne commence ou se termine par une sous-chaîne spécifique.

Flight Recorder

Java 11 apporte également une nouvelle fonctionnalité appelée Flight Recorder. Cette fonctionnalité permet aux développeurs de surveiller les performances et le comportement de leurs applications en temps réel. Les développeurs peuvent ainsi mieux comprendre le comportement de leur application et détecter plus facilement les problèmes de performance.

Améliorations de sécurité

Enfin, Java 11 apporte également des améliorations de sécurité importantes. Il inclut une nouvelle fonctionnalité appelée Application Class-Data Sharing, qui permet aux développeurs de créer des fichiers partagés pour les classes communes. Cela réduit la taille de l'application et améliore la sécurité en permettant de stocker les informations de sécurité de manière centralisée.

En somme, Java 11 est une mise à jour importante de la plate-forme de développement Java Standard. Elle apporte de nombreuses améliorations de performances, de nouvelles fonctionnalités pour les développeurs et des améliorations de sécurité importantes. Les développeurs Java devraient envisager de passer à Java 11 pour profiter de ces avantages.

Exemple d'utilisation de la nouvelle API pour les chaînes de caractères :


	String phrase = "   Hello World!   ";
	String trimmedPhrase = phrase.strip(); // Retire les espaces blancs en début et en fin de chaîne
	System.out.println(trimmedPhrase); // Affiche "Hello World!"

Exemple d'utilisation du nouveau module HTTP Client pour envoyer une requête GET :


	HttpClient client = HttpClient.newHttpClient();
	HttpRequest request = HttpRequest.newBuilder()
	        .uri(URI.create("https://www.example.com"))
	        .build();
	HttpResponse response =
	        client.send(request, HttpResponse.BodyHandlers.ofString());
	String responseBody = response.body();
	System.out.println(responseBody); // Affiche le contenu de la réponse

Exemple d'utilisation de la fonctionnalité Flight Recorder pour surveiller les performances d'une application :


	FlightRecorder recorder = FlightRecorder.getFlightRecorder();
	recorder.startRecording();
	// Code à surveiller
	recorder.stopRecording();
	List<RecordedEvent> events = recorder.getEvents();
	// Analyse des événements enregistrés pour détecter des problèmes de performance

Exemple d'utilisation de la nouvelle fonctionnalité Application Class-Data Sharing pour créer un fichier partagé pour les classes communes :


	java -Xshare:dump -XX:SharedClassListFile=classes.txt -XX:SharedArchiveFile=app.jsa MainClass
	java -Xshare:on -XX:SharedArchiveFile=app.jsa MainClass

Ces exemples sont loin d'être exhaustifs, mais ils devraient donner une idée de certaines des fonctionnalités de Java 11 et de la manière dont elles peuvent être utilisées.

Java 10 : Les Nouvelles Fonctionnalités et Améliorations

Java est l'un des langages de programmation les plus populaires au monde, utilisé pour développer des applications pour une grande variété de plates-formes, y compris les ordinateurs de bureau, les serveurs, les téléphones portables et les objets connectés. La dernière version de Java, Java 10, est sortie en mars 2018, apportant de nouvelles fonctionnalités et améliorations à la langue. Dans cet article, nous allons explorer certaines des nouveautés de Java 10 et leurs avantages.

Type d'Inférence de variable local amélioré

La principale nouveauté de Java 10 est l'amélioration du type d'inférence de variable locale. Cela signifie que vous pouvez désormais déclarer une variable sans spécifier son type et le compilateur déterminera automatiquement le type approprié en fonction de la valeur qu'elle contient. Cette fonctionnalité réduit considérablement le code source, le rendant plus facile à lire et à maintenir.

Application Dockerisée

Java 10 prend également en charge l'utilisation de conteneurs Docker pour exécuter des applications Java. Cela signifie que les développeurs peuvent empaqueter leur application dans un conteneur Docker et l'exécuter sur n'importe quelle plate-forme qui prend en charge Docker, sans avoir à se soucier des dépendances et des différences de configuration de l'environnement.

Améliorations de la Performance

Java 10 comprend également plusieurs améliorations de performance, notamment des améliorations de l'ordonnanceur de threads et de la gestion de la mémoire, qui peuvent améliorer les performances de l'application jusqu'à 30%.

Extension de l'API Time-Based

Java 10 étend également l'API time-based avec des fonctionnalités telles que la récupération de la date et de l'heure actuelles en un seul appel, ainsi que la prise en charge de nouvelles unités de temps, telles que les semaines et les quarts.

Réduction de la Taille du Fichier Jar

Java 10 comprend également une fonctionnalité appelée "Application Class-Data Sharing" qui permet aux classes fréquemment utilisées d'être stockées dans une archive partagée. Cela réduit la taille du fichier jar, ce qui permet des temps de chargement plus rapides et une utilisation plus efficace de la mémoire.

Exemples de code Java 10

Type d'Inférence de variable local amélioré

var age = 30; // Le compilateur infère automatiquement que age est un entier
var message = "Bonjour, le monde !"; // Le compilateur infère automatiquement que message est une chaîne de caractères
    

Application Dockerisée

# Créez une image Docker pour votre application Java
FROM openjdk:10-jdk
COPY . /usr/src/myapp
WORKDIR /usr/src/myapp
RUN javac Main.java
CMD ["java", "Main"]

Améliorations de la Performance

// Amélioration de l'ordonnanceur de threads
Thread.onSpinWait();

// Amélioration de la gestion de la mémoire
System.gc();

Extension de l'API Time-Based

// Récupération de la date et de l'heure actuelles en un seul appel
 Instant.now();

// Utilisation de nouvelles unités de temps
Duration.of(2, ChronoUnit.WEEKS);

Réduction de la Taille du Fichier Jar

// Ajouter l'option -Xshare:dump au lancement de l'application pour créer un fichier partagé
java -Xshare:dump -jar monApplication.jar

En conclusion, Java 10 est une version importante de la langue qui apporte de nouvelles fonctionnalités et améliorations qui peuvent aider les développeurs à écrire du code plus rapidement et à améliorer les performances de leurs applications. Les développeurs doivent envisager de passer à Java 10 pour tirer parti de ces améliorations et maintenir leur code à jour.

lundi 6 mars 2023

Tout ce que vous devez savoir sur les nouvelles fonctionnalités du Java 19



Le JDK 19 est sur le point d'être publié, et il comprend plusieurs nouvelles fonctionnalités passionnantes pour les développeurs Java. Dans cet article, nous allons vous présenter les principales nouveautés du JDK 19, notamment l'aperçu des modèles d'enregistrement, la prise en charge de l'architecture de jeu d'instructions Linux/RISC-V, l'aperçu d'une API de fonction et de mémoire étrangères, ainsi que l'aperçu des threads virtuels.


L'aperçu des modèles d'enregistrement est une fonctionnalité du langage qui permet de déconstruire les valeurs d'enregistrement en utilisant des motifs d'enregistrement pour améliorer la navigation et le traitement des données. Cette fonctionnalité s'appuie sur la correspondance de motifs pour instanceof, livré dans le JDK 16 en mars 2021, et fait partie du projet Amber.


La prise en charge de l'architecture de jeu d'instructions Linux/RISC-V est une autre nouveauté importante du JDK 19. RISC-V est une architecture de jeu d'instructions libre et open source qui est déjà prise en charge par un large éventail de chaînes d'outils de langage. Avec le portage Linux/RISC-V, Java obtiendrait la prise en charge d'un jeu d'instructions matérielles qui est déjà pris en charge par un large éventail de chaînes d'outils de langage.


L'aperçu d'une API de fonction et de mémoire étrangères est une autre fonctionnalité du JDK 19 qui permet aux programmes Java d'interagir avec des codes et des données en dehors de l'environnement d'exécution Java en invoquant efficacement des fonctions étrangères et en accédant en toute sécurité à la mémoire étrangère.


Enfin, l'aperçu des threads virtuels est une autre nouveauté importante du JDK 19. Les threads virtuels sont des threads légers qui réduisent considérablement les efforts d'écriture, de maintenance et d'observation des applications concurrentes à haut débit.


En conclusion, le JDK 19 apporte plusieurs nouvelles fonctionnalités passionnantes pour les développeurs Java. Les modèles d'enregistrement, la prise en charge de l'architecture de jeu d'instructions Linux/RISC-V, l'API de fonction et de mémoire étrangères et les threads virtuels sont autant de fonctionnalités qui devraient améliorer considérablement l'expérience de développement Java.



JEP 405 : un aperçu des modèles d'enregistrement pour déconstruire les valeurs d'enregistrement

// Old code
if (o instanceof String) {
    String s = (String)o;
    ... use s ...
}

// New code
if (o instanceof String s) {
    ... use s ...
}

JEP 422 : la prise en charge de l'architecture de jeu d'instructions Linux/RISC-V

JEP 424 : un aperçu d'une API de fonction et de mémoire étrangères pour interagir avec le code et les données en dehors de l'environnement d'exécution Java

JEP 425 : un aperçu des threads virtuels pour réduire considérablement les efforts d'écriture, de maintenance et d'observation des applications concurrentes à haut débit.

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10_000).forEach(i -> {
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return i;
        });
    });
}  // executor.close() is called implicitly, and waits


void handle(Request request, Response response) {
    var url1 = ...
    var url2 = ...
 
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        var future1 = executor.submit(() -> fetchURL(url1));
        var future2 = executor.submit(() -> fetchURL(url2));
        response.send(future1.get() + future2.get());
    } catch (ExecutionException | InterruptedException e) {
        response.fail(e);
    }
}
 
String fetchURL(URL url) throws IOException {
    try (var in = url.openStream()) {
        return new String(in.readAllBytes(), StandardCharsets.UTF_8);
    }
}


samedi 4 mars 2023

Java 8 à Java 17 : les évolutions majeures de la plateforme Java




De Java 8 à Java 17 : Les fonctionnalités clés de chaque version

Depuis la version 8 de Java, de nombreuses fonctionnalités ont été ajoutées pour améliorer l'efficacité et la simplicité de la programmation. Voici quelques exemples d'utilisation des fonctionnalités clés introduites dans chaque version :

Java 8

  • Utilisation des expressions lambdas pour simplifier la manipulation des collections et des flux de données
  • Utilisation des méthodes de référence pour simplifier la manipulation des fonctions

Java 9

  • Utilisation des modules pour organiser les applications en unités autonomes
  • Utilisation des flux réactifs pour la programmation asynchrone

Java 10

  • Utilisation de la méthode local variable type inference pour réduire la verbosité du code
  • Utilisation de la classe Optional.orElseThrow() pour éviter les exceptions de type NullPointerException

Java 11

  • Utilisation de la classe HttpClient pour simplifier les requêtes HTTP
  • Utilisation de la méthode String.isBlank() pour vérifier si une chaîne de caractères est vide ou contient uniquement des espaces

Java 12

  • Utilisation de la classe Collectors.teeing() pour combiner plusieurs opérations de collecte de données en une seule opération
  • Utilisation de la méthode String.indent() pour ajouter une indentation à une chaîne de caractères multiligne

Java 13

  • Utilisation de la classe Switch Expressions pour simplifier les instructions switch
  • Utilisation de la classe Text Blocks pour écrire des chaînes de caractères multilignes plus facilement

Java 14

  • Utilisation de la preview des records pour créer des classes immuables plus facilement
  • Utilisation de la classe Pattern.asMatchPredicate() pour créer des prédicats à partir de motifs de chaînes de caractères

Java 15

  • Utilisation des méthodes String.repeat() et String.trim() pour manipuler les chaînes de caractères plus facilement
  • Utilisation de la preview des text blocks pour écrire des chaînes de caractères multilignes plus facilement

Java 16

  • Utilisation des records pour représenter des données de manière plus concise
  • Utilisation des text blocks pour écrire des chaînes de caractères multilignes plus facilement

Java 17

  • Utilisation de la preview des Pattern Matching for switch pour améliorer les instructions switch
  • Utilisation de la classe Sealed pour définir des hiérarchies de classes fermées
Cet article vous permettra de découvrir les nouveautés de Java 17, y compris des exemples concrets pour mieux comprendre ces améliorations.



De la version 8 à la version 17, Java a continué à évoluer pour fournir des fonctionnalités plus efficaces et plus simples pour la programmation. Il est important de se tenir au courant des dernières mises à jour de Java pour profiter de ces améliorations.

La transition de Java 8 à Java 17 nécessite d’être attentif à plusieurs éléments comme la compatibilité des librairies, les évolutions de syntaxe, les changements de l’API et les outils de migration. Il faut également vérifier son code sur chaque version intermédiaire pour prévenir les erreurs ou les anomalies.

Java 17 pour un code efficace et maintenable




1. Pattern matching pour les instances de classe

Java 17 introduit le pattern matching pour les instances de classe, qui permet de simplifier les tests sur les instances de classe. Il est possible d'écrire des expressions switch avec des modèles pour les instances de classe :

if (obj instanceof String str) {
System.out.println(str.toUpperCase());
}

2. Fonctions Lambda pour les cas particuliers

Java 17 permet désormais d'utiliser des fonctions lambda pour les cas particuliers. Il est possible d'écrire des méthodes statiques qui renvoient des expressions lambda :

var myFunction = switch (x) {
case 1 -> () -> "One";
case 2 -> () -> "Two";
default -> () -> "Many";
};

String result = myFunction.apply();

3. Améliorations de la gestion des erreurs

Java 17 améliore la gestion des erreurs en introduisant de nouvelles classes pour les exceptions et les erreurs. Il est possible d'utiliser la classe Throwable pour créer des erreurs personnalisées et la classe Exception pour créer des exceptions personnalisées :

public class MyError extends Throwable {
public MyError(String message) {
super(message);
}
}

public class MyException extends Exception {
public MyException(String message) {
super(message);
}
}

4. Fonctions pour les tableaux

Java 17 introduit de nouvelles fonctions pour les tableaux, telles que la méthode copyOfRange() qui permet de copier une partie d'un tableau dans un autre tableau :

int[] array = {1, 2, 3, 4, 5};
int[] subArray = Arrays.copyOfRange(array, 1, 3); // {2, 3}

5. Support des records

Java 17 introduit le support des records, qui sont des classes spéciales pour lesquelles le code de base est généré automatiquement. Les records sont conçus pour stocker des données et sont utilisés pour remplacer les classes Java traditionnelles qui stockent uniquement des données. Voici un exemple :

public record Person(String name, int age) {}
        Person alice = new Person("Alice", 25);
String name = alice.name();
int age = alice.age();

5. Les classes sealed

Les classes "sealed" sont une nouvelle fonctionnalité introduite dans Java 17. Les classes "sealed" permettent de limiter l'héritage d'une classe en spécifiant explicitement les classes qui peuvent en hériter. Cela peut aider à améliorer la sécurité et la maintenabilité du code en limitant l'ensemble des classes qui peuvent être utilisées dans un contexte particulier.

Une classe scellée est définie à l'aide du mot-clé "sealed" avant la définition de la classe. Ensuite, vous pouvez spécifier une liste de classes qui peuvent hériter de cette classe à l'aide du mot-clé "permits". Par exemple, la syntaxe pour déclarer une classe scellée avec des classes autorisées pourrait ressembler à ceci :

public sealed class MaClasse permits ClasseAutorisee1, ClasseAutorisee2 {
    // Définition de la classe
}

Dans cet exemple, "MaClasse" est la classe scellée et "ClasseAutorisee1" et "ClasseAutorisee2" sont les classes autorisées à hériter de "MaClasse".

Il est important de noter que les classes autorisées peuvent également être des classes scellées, ce qui signifie qu'elles peuvent spécifier à leur tour une liste de classes autorisées à hériter d'elles.

Lorsqu'une classe est scellée, toutes les classes qui héritent de cette classe doivent être déclarées dans le même fichier source que la classe scellée. Cela aide à garantir que toutes les classes autorisées sont connues et gérées dans le même contexte.