Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 改良复合模式/食品、配方和子配方_Java_Design Patterns_Composite - Fatal编程技术网

Java 改良复合模式/食品、配方和子配方

Java 改良复合模式/食品、配方和子配方,java,design-patterns,composite,Java,Design Patterns,Composite,我在正确实现复合模式(使其最有效)方面遇到了困难 涉及到两个实体:基本食物和食谱。它们将从CSV文件中解析。 基本食物对象将包含一个字母(表示它是食物还是食谱——在本例中为f)、名称、卡路里、脂肪、碳水化合物和蛋白质。配方将包含字母r和配方名称 我知道基本食物应该是相应的叶子,而食谱应该是复合的。但是,配方文件的CSV行中的行表示配方可以包含更多的食品,其方式是具有名称和计数(份数)对。名称既可以是基本食物,也可以是(子)食谱,这就引发了如何制定最佳解决方案的问题 我的第一个想法是让组合保存一个

我在正确实现复合模式(使其最有效)方面遇到了困难

涉及到两个实体:基本食物食谱。它们将从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());