将策略模式与依赖注入一起使用

问题描述

我正在研究https://en.wikipedia.org/wiki/Strategy_pattern中列出的Strategy模式,并试图了解当您还要使用依赖注入并使其易于进行单元测试时,该模式如何工作。

所以界面是:

interface BillingStrategy {
    // Use a price in cents to avoid floating point round-off error
    int getActPrice(int rawPrice);
}

有两种实现方式:

@Component
NormalHours implements BillingStrategy {
    public int getActPrice(int rawPrice) {
       return rawPrice;
    }

}

@Component
HappyHours implements BillingStrategy {
    public int getActPrice(int rawPrice) {
       return rawPrice/2;
    }

}

现在有一个我要为其跟踪总价的客户对象:

class Customer {
    private final List<Integer> drinks = new ArrayList<>();
    private BillingStrategy strategy;

    public Customer(BillingStrategy strategy) {
        this.strategy = strategy;
    }

    public void add(int price,int quantity) {
        this.drinks.add(this.strategy.getActPrice(price*quantity));
    }

    // Payment of bill
    public int getBill() {
        int sum = this.drinks.stream().mapToInt(v -> v).sum();
        
        this.drinks.clear();
        return sum;
    }

    // Set Strategy
    public void setStrategy(BillingStrategy strategy) {
        this.strategy = strategy;
    }
}

现在说我有一个文件,其中包含一天中每个小时的购买信息,我需要为客户计算最终账单。

@Component 
public class Calculator {
   
public int calculate(File file) {
   //pseudo code here
   Customer customer = new Customer();
   for(line in file) {
      //parse price,strategy and quantity from line
      customer.setStrategy(strategy);
      customer.add(price,quantity);
     
   }

   return customer.getBill();
}


}

很显然,这对于单元测试不是很好,因为在该方法中创建了一个新对象,并且仅使用常规Mockito很难模拟在Calculator类中为策略返回的值。有没有其他方法可以对Customer&Calculator类进行建模,以确保易于测试并尽可能使用DI?

我知道一种方法是让Calculator的调用者传递对象Customer,但是用Wiki解释这种特定Customer类的方式,我们不能将其作为Singleton进行处理,并且会存在相同的问题。

解决方法

给定策略与客户本身无关,将其置于客户违反了直接投资。而是将策略作为方法参数传递

class Customer {
    private final List<Integer> drinks = new ArrayList<>();
    
    public void add(BillingStrategy strategy,int price,int quantity) {
        drinks.add(strategy.getActPrice(price * quantity));
    }
    
    // Payment of bill
    public int getBill() {
        int sum = drinks.stream().mapToInt(v -> v).sum();
        drinks.clear();
        return sum;
    }
}

@Component
public class Calculator {

    public int calculate(Customer customer,File file) {
        //pseudo code here
        for(line in file) {
            //parse price,strategy and quantity from line
            customer.add(strategy,price,quantity);
        }

        return customer.getBill();
    }
}

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...