使用策略模式与依赖注入

huangapple go评论85阅读模式
英文:

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&lt;Integer&gt; drinks = new ArrayList&lt;&gt;();
    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 -&gt; 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&lt;Integer&gt; drinks = new ArrayList&lt;&gt;();
    
    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 -&gt; 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();
    }
}

huangapple
  • 本文由 发表于 2020年8月12日 12:10:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/63369649.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定