Couple Domain Object with Business Logic

GaripTipici :

Assume that we have a domain object and an evaluation class of this object. For instance a promotion and its evaluation logic in seperated classes:

class BuyXGetYFreePromotion extends AbstractPromotion{
   String x;
   String y;
}

class BuyXGetYFreePromotionEvaluation {
   public void evaluate(Cart cart){
       for(String product : cart.getProducts()){
           if(product.equal(BuyXGetYFreePromotion.x)){
              //some code to discount y price
           }
       }
   }
}

and another example:

class FixedPricePromotion extends AbstractPromotion{
    String product;
    Double price;
}

class FixedPricePromotionEvaluation {
    public void evaluate(Cart cart){
        for(String product : cart.getProducts()){
           if(product.equal(FixedPricePromotion.product)){
              //some code to discount y price
           }
        }
    }
}

We have a lot of pairs like this.

We can't inject the evaluation in domain objects but we can relate them in evaluation classes or another class.

First option is relate them with instanceof statement.

For example:

class PromotionService {
    void evaluation(Cart cart){
        for(AbstractPromotion p : getPromotions){
            if(p instanceof BuyXGetYFreePromotion)
               BuyXGetYFreePromotionEvaluation.evaluate(cart);
            else if(p instanceof FixedPricePromotion)
               FixedPricePromotionEvaluation.evaluate(cart);
        }
    }
}

But this example violates Open-Closed Principle.

My question is, how should I couple these pairs by considering SOLID principles.

davidxxx :

1) model domain driven design way

You could do things much simpler by making your domain objects with behavior.
You could for example merge Promotion and Promotion evaluation in a single class.
Anemic objects are not necessarily the best thing.
For example :

class FixedPricePromotion extends AbstractPromotion{
    String product;
    Double price;
    public void evaluate(Cart cart){
        for(String product : cart.getProducts()){
           if(product.equal(product)){
              //some code to discount y price
           }
        }
    }    
}

PromotionService can be changed in this way now :

class PromotionService {
    void evaluation(Cart cart){
        for(AbstractPromotion p : getPromotions){                
               p.evaluate(cart);
        }
    }
}

2) model domain and logic separated way

If you don't want to merge them you could bridge them thanks a field dependency from the one to the other.

PromotionEvaluation could be an interface that defines the template of the logic and abstract isMatch() and applyPromotion() method to define in subclasses :

public interface PromotionEvaluation{

       boolean isMatch(String product);
       void applyPromotion(String product);
       default void evaluate(Cart cart){
            for(String product : cart.getProducts()){
               if(isMatch(product)){
                  applyPromotion(product);
               }
            }
        }       
}

And subclass could be such as :

class FixedPricePromotionEvaluation implements PromotionEvaluation{
    FixedPricePromotion promotion;

    public FixedPricePromotionEvaluation(FixedPricePromotion promotion){
      this.promotion = promotion;
    }

    public boolean isMatch(String product){
       return product.equal(promotion.product)
    }

    public void applyPromotion(String product){
      // do your logic
    }


}

Now you could iterate on the evaluations in this way :

class PromotionService {
    void evaluation(Cart cart){
        for(PromotionEvaluation evaluation : getEvaluations()){
            e.evaluate(cart);
        }
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=151202&siteId=1