【问题标题】:Creating childs of an abstract class based on childs of another abstract class without knowing their type在不知道其类型的情况下基于另一个抽象类的子类创建一个抽象类的子类
【发布时间】:2023-03-07 10:22:01
【问题描述】:

考虑以下情况:

  • 我有一个名为Caracteristic 的抽象类,它可以有名称和级别。它还覆盖Object.toString()
  • 我有 3 个 Caracteristic 的子代,分别称为 CaracteristicACaracteristicBCaracteristicC,它们覆盖了名称(可能还有级别)。
  • 我有一个名为Statistic 的抽象类,它有两个抽象函数:int getValue() 和一个Object.toString() 覆盖。
  • 我有一个名为StatisticA 的类扩展Statistic,其中覆盖int getValue() 的结果取决于所需的CaracteristicACaracteristicB 实例。我有一个类似的类,称为StatisticB,但int getValue() 的结果取决于所需的CaracteristicC 实例。所有的孩子也会覆盖Statistic.toString()

当我为我的客户创建统计数据时,我正在做一些相当丑陋的事情:

CaracteristicA caracteristicA = null;
CaracteristicB caracteristicB = null;
CaracteristicC caracteristicC = null;

for (Caracteristic currentCaracteristic : listOfCaracteristics) {
    if (currentCaracteristic instanceof CaracteristicA) {
        caracteristicA = (CaracteristicA)currentCaracteristic;
    } if (currentCaracteristic instanceof CaracteristicB) {
        caracteristicB = (CaracteristicB)currentCaracteristic;
    } if (currentCaracteristic instanceof CaracteristicA) {
        caracteristicC = (CaracteristicC)currentCaracteristic;
    } else {
        throw new IllegalArgumentException("Unknown caracteristic !");
    }
}

// "client.addStatistic" expects a "Statistic".
// "StatisticA"'s constructor expects an instance of "caracteristicA" and "caracteristicB".
// "StatisticB"'s constructor expects an instance of "caractersiticC".
client.addStatistic(new StatisticA(caracteristicA, caracteristicB));
client.addStatistic(new StatisticB(caracteristicC));

问题:如果我想添加一个依赖于CaracteristicAStatisticCCaracteristic 的新子代(我们称之为CaracteristicD),我需要添加一个变量来存储适当的特征,在循环中添加另一个else if 以找到它,然后我终于能够创建具有适当特征的所需统计数据。

我相当肯定我可以简单地处理这整个混乱,我想知道我该怎么做。在 Google 和 Stack Overflow 上搜索似乎将我指向“Factory”和“Visitor”设计模式,但我不确定它们是否会帮助我或让情况变得更糟.

我还尝试将Object getType() 抽象方法添加到Caracteristic 中,孩子们会用他们的类型覆盖它(例如:CaracteristicA getType()),但我意识到如果我提前知道listOfCaracteristics 的哪个孩子,这会起作用正在迭代中。

更新: listOfCaracteristics 通过执行 listOfCaracteristics.add(new CaracteristicA());listOfCaracteristics.add(new CaracteristicB()); 等来填充。这是抽象类Caracteristic的代码:

abstract class Caracteristic {
    private final String name;
    private int level;

    Caracteristique(String name, int level) {
        this.name = name;
        this.level = level;
    }

    public int getLevel() {
        return this.level;
    }

    @Override
    public String toString() {
        return this.name;
    }
}

这是其中一个孩子 (CaracteristicA) 的代码:

class CaracteristicA extends Caracteristic {
    public CaracteristicA(int level) {
        super("Hello World!", level);
    }

    public CaracteristicA() {
        super("Hello World!", 1);
    }
}

【问题讨论】:

  • listOfCaracteristics 是如何创建和填充的?能不能把每个Caracteristic的代码也显示一下?
  • 我用请求的信息更新了问题。其他 CaracteristicX 子代与我提供的示例相同。

标签: java class oop abstract-class


【解决方案1】:

不要为每个Caracteristic 创建一个实例并将它们放入列表中以随后分解列表并将它们分配给变量,只需使用正确的Caracteristic 实例化变量即可。这大大简化了代码:

CaracteristicA caracteristicA = new CaracteristicA();
CaracteristicB caracteristicB = new CaracteristicB();
CaracteristicC caracteristicC = new CaracteristicC();
CaracteristicD caracteristicD = new CaracteristicD(); //hypothetic
client.addStatistic(new StatisticA(caracteristicA, caracteristicB));
client.addStatistic(new StatisticB(caracteristicC));
client.addStatistic(new StatisticC(caracteristicA, caracteristicD)); //hypothetic

还有一些我想补充的。 Caracteristic 是一个抽象类但没有抽象成员这一事实表明设计不佳。最后,我怀疑您唯一需要的是控制Statistic 中允许哪些值对(名称和级别)。 整个Caracteristic 层次结构可以简化为

public enum Caracteristic {
    A("Hello World!", 1), B("easy", 2), C("medium", 3), D("hard", 4);

    private final String name;
    private final int level;
    Caracteristic(String name, int level)
    {
        this.name = name;
        this.level = level;
    }
    @Override
    public String toString() { return name; }
    public int getLevel() { return level; }
}

这再次简化了您的代码,如下所示:

client.addStatistic(new StatisticA(Caracteristic.A, Caracteristic.B));
client.addStatistic(new StatisticB(Caracteristic.C));
client.addStatistic(new StatisticC(Caracteristic.A, Caracteristic.D)); //hypothetic

现在将Caracteristic 作为构造函数参数确实没有任何好处,因为Caracteristic.A 只存在一个实例,因此它们可以直接在StatisticX 中使用!

再一次,一个更简化的版本将变为:

client.addStatistic(new StatisticA());
client.addStatistic(new StatisticB());
client.addStatistic(new StatisticC()); //hypothetic

例如,StatisticAbeeing

public final class StatisticA {
    public void DoSomeWork() {
        int differenceLevel = Caracteristic.B.getLevel() - Caracteristic.A.getLevel();
        string bothLevels = Caracteristic.A.toString() + " " + Caracteristic.B.toString();
        System.out.PrintLn(bothLevels + ": " + differenceLevel);
    }
}

为了更进一步,我将假设每个Statistic 都做“相同”的工作(除了它使用不同的特征)。这将允许重构为一个类Statistic

public final class Statistic {
    private final Iterable<Integer> caracs;
    public Statistic(Caracteristic... caracs) {
        this.caracs = Arrays.asList(caracs);
    }
    public void DoSomeWork() {
        System.out.PrintLn(caracs.stream()
                                 .map(Caracteristic::toString)
                                 .Collect(Collectors.joining(",")));
        System.out.PrintLn(caracs.stream()
                                 .map(Caracteristic::getLevel)
                                 .sum());
    }
}

随着用法的变化(最终的代码简化)

client.addStatistic(new Statistic(Caracteristic.A, Caracteristic.B));
client.addStatistic(new Statistic(Caracteristic.C));
client.addStatistic(new Statistic(Caracteristic.A, Caracteristic.D)); //hypothetic

一开始会有:

  • 7 类

所有的简化:

  • 1 类
  • 1 个枚举

【讨论】:

  • 假设我修改我的抽象类现在有一个抽象方法,孩子们正在覆盖它,这仍然可行吗?
  • @Shepard62700FR 绝对可以,然后您可以按照我在第一个代码 sn-p 中的描述简化您的代码。
【解决方案2】:

也许我错了,因为我不是很确定理解这个问题,但是为什么不使用Composite Pattern 来表示Carateristic 的层次结构呢? 这样,您将能够创建一个新的 Composite 特征(例如 CaracteristicAB ),您可以将其传递给需要多个 CarateristicStatistic

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-19
    • 2013-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多