Java 改良复合模式/食品、配方和子配方
我在正确实现复合模式(使其最有效)方面遇到了困难 涉及到两个实体:基本食物和食谱。它们将从CSV文件中解析。 基本食物对象将包含一个字母(表示它是食物还是食谱——在本例中为f)、名称、卡路里、脂肪、碳水化合物和蛋白质。配方将包含字母r和配方名称 我知道基本食物应该是相应的叶子,而食谱应该是复合的。但是,配方文件的CSV行中的行表示配方可以包含更多的食品,其方式是具有名称和计数(份数)对。名称既可以是基本食物,也可以是(子)食谱,这就引发了如何制定最佳解决方案的问题 我的第一个想法是让组合保存一个Java 改良复合模式/食品、配方和子配方,java,design-patterns,composite,Java,Design Patterns,Composite,我在正确实现复合模式(使其最有效)方面遇到了困难 涉及到两个实体:基本食物和食谱。它们将从CSV文件中解析。 基本食物对象将包含一个字母(表示它是食物还是食谱——在本例中为f)、名称、卡路里、脂肪、碳水化合物和蛋白质。配方将包含字母r和配方名称 我知道基本食物应该是相应的叶子,而食谱应该是复合的。但是,配方文件的CSV行中的行表示配方可以包含更多的食品,其方式是具有名称和计数(份数)对。名称既可以是基本食物,也可以是(子)食谱,这就引发了如何制定最佳解决方案的问题 我的第一个想法是让组合保存一个
列表
来存储它的组合,并拥有一个属性映射
。如果我们正在处理某种类型的食品并提供类似积垢的功能,最简单的检查方法是什么?该程序是用Java实现的
E.g. b, FoodName, 1, 2, 3, 4 (basic food)
E.g. r, RecipeName, foodOneName, foodOneCount, foodTwoName, foodTwoCount, ...
这是一个可能的模型。首先,让我们定义一个表示成分(可以是基本食物或食谱)的抽象类: 基本食物是一种成分,因此它扩展了成分:
class BasicFood extends Ingredient
{
int calories;
int fat;
int carb;
int protein;
}
食谱也是一种配料,它包含一系列项目。每个项目包含一种成分和数量:
class Item
{
Ingredient ingredient;
int quantity;
}
class Recipe extends Ingredient
{
List<Item> items;
}
类项目
{
成分;
整数;
}
类配方扩展了配料
{
清单项目;
}
要实现复合模式,您需要一个由食品
和配方
实现的接口,以便统一处理它们
例如,此界面可能会公开获取食品或配方特征的方法:
public interface Component {
String getName();
double getCalories();
double getFat();
double getCarb();
double getProtein();
}
对于食品
,该接口的实现很简单,因为它只匹配其getter:
public class Food implements Component {
private String name;
private double calories;
private double fat;
private double carb;
private double protein;
public Food(String name, double calories, double fat, double carb, double protein) {
this.name = name;
this.calories = calories;
this.fat = fat;
this.carb = carb;
this.protein = protein;
}
@Override
public String getName() {
return name;
}
@Override
public double getCalories() {
return calories;
}
@Override
public double getFat() {
return fat;
}
@Override
public double getCarb() {
return carb;
}
@Override
public double getProtein() {
return protein;
}
}
在Recipe
中,正如您所说,我们可以使用Map
保存其成分和数量
为了实现组件
接口,我们可以返回其所有组件特征的总和(乘以数量),可以是食品和子配方。
但是由于多态性,我们不必对它们进行不同的处理,这是复合模式的目标:
public class Recipe implements Component {
private String name;
private Map<Component, Double> components;
public Recipe(String name) {
this.name = name;
components = new HashMap<>();
}
@Override
public String getName() {
return name;
}
@Override
public double getCalories() {
return componentsSum(Component::getCalories);
}
@Override
public double getFat() {
return componentsSum(Component::getFat);
}
@Override
public double getCarb() {
return componentsSum(Component::getCarb);
}
@Override
public double getProtein() {
return componentsSum(Component::getProtein);
}
private double componentsSum(Function<Component, Double> function) {
return components.entrySet().stream()
// get the requested characteristic and multiply it by the quantity
.mapToDouble(entry -> function.apply(entry.getKey()) * entry.getValue())
.sum();
}
}
您需要覆盖食品
和配方
的等于
和哈希代码
,以使这些方法正常工作。在我的例子中,我们应该只考虑变量<代码>名称<代码> .<
然后是一些代码来测试它:
// foods
Component f1 = new Food("f1", 1, 1, 1, 1);
Component f2 = new Food("f2", 2, 2, 2, 2);
Component f3 = new Food("f3", 3, 3, 3, 3);
// recipe with only food
Recipe r1 = new Recipe("r1");
r1.add(f1, 2);
r1.add(f2, 1);
// recipe with food and sub-recipe
Recipe r2 = new Recipe("r2");
r2.add(f3, 1);
r2.add(r1, 2);
// prints 11.0 11.0 11.0 11.0
System.out.println(r2.getCalories() + " " +r2.getFat() + " " + r2.getCarb() + " " + r2.getProtein());
如果你有一张
地图
,你真的需要一张列表
?这似乎是多余的。我的建议是通过简化要求开始编码:首先实现基本食物,然后是没有子食谱的食谱,然后是没有服务的子食谱,等等。如果你在过程中遇到困难,请发布你的代码。因为食谱可以包含食物和食谱,你需要地图,其中字符串是食谱的名称,RecipeItem是地图,对象是食物或食谱,整数是要包含的对象数。
public Double add(Component component, double quantity) {
// if already present, add the quantity
return components.merge(component, quantity, Double::sum);
}
public Double remove(Component component) {
return components.remove(component);
}
public Double update(Component component, double quantity) {
// if present, replace the quantity
return components.computeIfPresent(component, (k, v) -> quantity);
}
// foods
Component f1 = new Food("f1", 1, 1, 1, 1);
Component f2 = new Food("f2", 2, 2, 2, 2);
Component f3 = new Food("f3", 3, 3, 3, 3);
// recipe with only food
Recipe r1 = new Recipe("r1");
r1.add(f1, 2);
r1.add(f2, 1);
// recipe with food and sub-recipe
Recipe r2 = new Recipe("r2");
r2.add(f3, 1);
r2.add(r1, 2);
// prints 11.0 11.0 11.0 11.0
System.out.println(r2.getCalories() + " " +r2.getFat() + " " + r2.getCarb() + " " + r2.getProtein());