英文:
Using strategy pattern along with Dependency injection
问题
I understand that you want a translation of the code-related content. Here's the translated code portion:
我正在研究https://en.wikipedia.org/wiki/Strategy_pattern 中列出的策略模式,并尝试了解如何在使用依赖注入并使其易于进行单元测试时运行。所以接口是:
interface BillingStrategy {
// 以分为单位使用价格,以避免浮点数舍入误差
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));
}
// 支付账单
public int getBill() {
int sum = this.drinks.stream().mapToInt(v -> v).sum();
this.drinks.clear();
return sum;
}
// 设置策略
public void setStrategy(BillingStrategy strategy) {
this.strategy = strategy;
}
}
现在假设我有一个文件,其中包含每小时进行的购买信息,我需要为客户计算最终账单:
@Component
public class Calculator {
public int calculate(File file) {
//伪代码在此处
Customer customer = new Customer();
for(line in file) {
//从行中解析价格、策略和数量
customer.setStrategy(strategy);
customer.add(price, quantity);
}
return customer.getBill();
}
}
显然,这对于单元测试不是很好,因为该方法内创建了一个新对象,而仅使用常规的Mockito将很难模拟Calculator类中策略返回的值。是否有不同的方法来建模Customer和Calculator类,以确保测试容易,并尽可能使用DI?
我知道一种方法是让Calculator的调用者传递Customer对象,但是根据维基百科的解释,这个特定的Customer类无法作为Singleton,同样的问题将存在。
英文:
I'm going through Strategy pattern listed out in https://en.wikipedia.org/wiki/Strategy_pattern and trying to understand how this would work when you want to use Dependency injection as well and make it easy for unit testing.
So the interface is:
interface BillingStrategy {
// Use a price in cents to avoid floating point round-off error
int getActPrice(int rawPrice);
}
There are two implementations:
@Component
NormalHours implements BillingStrategy {
public int getActPrice(int rawPrice) {
return rawPrice;
}
}
@Component
HappyHours implements BillingStrategy {
public int getActPrice(int rawPrice) {
return rawPrice/2;
}
}
Now there is a customer object for whom I want to keep track of how the total price:
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;
}
}
Now say I've a file which I've which has information about purchases made during each hour of the day, and I need to compute the final bill for the customer.
@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();
}
}
Obviously this doesn't work great for unit testing as a new object is created within the method and by using just regular Mockito it's going to be hard to mock the values returned for the strategies in Calculator class. Is there a different way to model the Customer & Calculator class to ensure ease of testing and use DI as much as possible?
I know one way is let the caller of Calculator pass in the object Customer, but however with the way the wiki has explained this particular Customer class we cannot make it as a Singleton and the same problem will exist.
答案1
得分: 1
Here's the translated code:
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();
}
}
英文:
Given strategy isn't related to the Customer itself and placing it inside Customer is DI violation. Instead just pass strategy as method argument
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();
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论