5
votes

Convertir la boucle en lambda et lancer une exception

Comment puis-je écrire le code ci-dessous en utilisant l'expression lambda dans java8. Je suis nouveau sur Java 8.

globalPricingRequests.forEach((globalPricingRequest) -> {
    if (checkIfValueLessThanZero(globalPricingRequest.getFeePerTransact())) {
        throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
    }
    List<EventTypePricingMapping> eventTypePricingMappings = globalPricingRequest.getEventTypePricingList();
    eventTypePricingMappings.forEach((eventTypePricingMapping) -> {
        if (checkIfValueLessThanZero(eventTypePricingMapping.getFeePerRevenue())) {
            throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
        }
        if (checkIfValueLessThanZero(eventTypePricingMapping.getFeePerReg())) {
            throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
        }
    });
});


private boolean checkIfValueLessThanZero(Object object) {
    if (object instanceof BigDecimal) {
       if (object != null && ((BigDecimal) object).intValue() < 0) {
           return true;
       }
    }
    return false;
}

J'ai encore essayé le code ci-dessous selon la suggestion. Y a-t-il autre chose que nous pouvons améliorer dans ce code pour l'écrire en utilisant davantage lambdas.

for (GlobalPricingRequest globalPricingRequest : globalPricingRequests) {
    BigDecimal feePerTrans = globalPricingRequest.getFeePerTransact();
    if (feePerTrans != null && feePerTrans.intValue() < 0) {
        throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
    }
    List<EventTypePricingMapping> eventTypePricingMappings = globalPricingRequest.getEventTypePricingList();
    for (EventTypePricingMapping eventTypePricingMapping : eventTypePricingMappings) {
        BigDecimal feePerRevenue = eventTypePricingMapping.getFeePerRevenue();
        if (feePerRevenue != null && feePerRevenue.intValue() < 0) {
            throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
        }
        if (eventTypePricingMapping.getFeePerRevenue().intValue() < 0) {
            throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
        }
    }
}


7 commentaires

Qu'est-ce que vous avez déjà essayé? Nous ne sommes pas là pour réécrire vos trucs.


Je ne comprends même pas comment écrire lambda qui vérifie une condition et lance une exception sur la base si cela, un indice ou une suggestion serait utile


oui si je peux gérer la redondance des conditions


Mise à jour du code que j'ai encore essayé


Veuillez également noter que BigInteger # intValue ( ) est une mauvaise manière de vérifier le signe d'un BigInteger . Selon la documentation: Notez que cette conversion peut [...] renvoyer un résultat avec le signe opposé ! Vous devez utiliser BigInteger # signum () à la place


Juste un avis personnel, mais je déconseillerais de longs lambdas comme celui-ci, du point de vue de la maintenance future. Envisagez plutôt d'extraire des méthodes et utilisez une référence de méthode.


notez que if (eventTypePricingMapping.getFeePerRevenue (). intValue () <0) {... est redondant comme vous l'avez déjà fait BigDecimal feePerRevenue = eventTypePricingMapping.getFeePerRevenue (); if (feePerRevenue! = null && feePerRevenue.intValue () <0) {... avant cela.


4 Réponses :


2
votes

Vous pouvez utiliser stream deux fois et améliorer la lisibilité de votre code comme:

Predicate<BigDecimal> feeCheck =
        feePerTransactOrRevenue -> feePerTransactOrRevenue != null
                && feePerTransactOrRevenue.intValue() < 0;

boolean globalRequestCheck = globalPricingRequests.stream()
        .map(GlobalPricingRequest::getFeePerTransact)
        .anyMatch(feeCheck); 

boolean eventTypeCheck = globalPricingRequests.stream()
        .map(GlobalPricingRequest::getEventTypePricingList)
        .flatMap(List::stream)
        .map(EventTypePricingMapping::getFeePerRevenue)
        .anyMatch(feeCheck);

// if any of the element matches the condition, throw the exception
if (globalRequestCheck || eventTypeCheck) { 
    throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
}


1 commentaires

Cela peut être basé sur une opinion, cela améliore certainement la lisibilité par rapport aux autres solutions forEach. De plus, le temps d'exécution global dans le pire des cas doit rester le même que O (n * m) n étant la taille de globalPricingRequests et m étant la taille maximale d'une liste getEventTypePricingList .



0
votes
globalPricingRequest.forEach(data -> {
      if (data.getFeePerTransact() != null && data.getFeePerTransact().intValue() < 0) {
        // you can only throw unchecked exception in lambda function
        throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
      }
      data.getEventTypePricingList().forEach(event-> {
        if (event.getFeePerRevenue() != null && event.getFeePerRevenue().intValue() < 0) {
          // you can only throw unchecked exception in lambda function
          throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
        }// this if statemnt in your code is twice
      });
    });

0 commentaires

4
votes

Problème

Votre problème ne concerne pas les lambdas, mais l'organisation du code. Vous disposez d'une donnée, c'est-à-dire List et un ensemble de règles de validation . Tout ce dont vous avez besoin pour appliquer ces règles de validation aux données données.

Cette approche vous donne la flexibilité d'ajouter ou de supprimer facilement des règles de validation. Et testez ou vérifiez chaque règle séparément.

Solution

La solution optimale consiste à diviser chaque validation en une classe distincte.

D'abord , créer un gestionnaire et une interface pour la règle de validation:

GlobalPricingRequestValidationManager validationManager = new GlobalPricingRequestValidationManager();
List<GlobalPricingRequest> globalPricingRequests = Collections.emptyList();
validationManager.validate(globalPricingRequests);

Deuxièmement , implémentez chaque règle de validation dans la classe séparée (a été ajoutée au gestionnaire):

public final class TransactionFeeEqualOrGreaterThanZeroValidationRule implements GlobalPricingRequestValidationManager.ValidationRule {

    @Override
    public void validate(List<GlobalPricingRequest> globalPricingRequests) {
        if (globalPricingRequests.stream()
                                 .map(GlobalPricingRequest::getFeePerTransact)
                                 .filter(Objects::nonNull)
                                 .anyMatch(val -> val.signum() == -1)))
            throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
    }
}

public final class RevenueFeeEqualOrGreaterThanZeroValidationRule implements GlobalPricingRequestValidationManager.ValidationRule {

    @Override
    public void validate(List<GlobalPricingRequest> globalPricingRequests) {
        if (globalPricingRequests.stream()
                                 .map(GlobalPricingRequest::getEventTypePricingList)
                                 .flatMap(List::stream)
                                 .map(EventTypePricingMapping::getFeePerRevenue)
                                 .filter(Objects::nonNull)
                                 .anyMatch(val -> val.signum() == -1)))
            throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");

    }
}

Code Clinet:

public final class GlobalPricingRequestValidationManager {

    private final List<ValidationRule> validationRules =
            Arrays.asList(
                new TransactionFeeEqualOrGreaterThanZeroValidationRule(),
                new RevenueFeeEqualOrGreaterThanZeroValidationRule());

    public void validate(List<GlobalPricingRequest> globalPricingRequests) {
        validationRules.forEach(validationRule -> validationRule.validate(globalPricingRequests));
    }

    public interface ValidationRule {

        void validate(List<GlobalPricingRequest> globalPricingRequests);
    }

}

0 commentaires

2
votes

Ce type de validation que vous effectuez est meilleur via l'approche impérative, néanmoins nous pouvons utiliser des lambdas le cas échéant.

Premièrement, j'isolerais les conditions répétitives à un prédicat local avec l'utilisation de signum code> comme également suggéré par @Thomas Kläger sous le message car il est plus approprié dans ce cas précis que intValue.

public static void isValidOrElseThrowBadRequestException(BigDecimal data, Predicate<BigDecimal> criteria) throws Exception { // change the exception to the specific one you're using 
       if(criteria.test(data)) throw ExceptionHelper.badRequest("Fee Per Transaction can't be less than zero");
}

Alors votre impératif approche ressemblerait à:

for (GlobalPricingRequest globalPricingRequest : globalPricingRequests) {
      isValidOrElseThrowBadRequestException(globalPricingRequest.getFeePerTransact(), criteria);
      for (EventTypePricingMapping eventTypePricingMapping : globalPricingRequest.getEventTypePricingList()) {
          isValidOrElseThrowBadRequestException(eventTypePricingMapping.getFeePerRevenue(), criteria);
      }
}

isValidOrElseThrow est défini comme:

Predicate<BigDecimal> criteria = b -> b != null && b.signum() < 0;

Juste avec quelques isolations ici et là nous sommes en mesure de rendre le code plus lisible.


0 commentaires