在不知道抽象类的类型的情况下,根据另一个抽象类的子类创建抽象子类

时间:2018-07-10 12:59:17

标签: java class oop abstract-class

请考虑以下情况:

  • 我有一个名为Caracteristic的抽象类,它可以具有名称和级别。它还会覆盖Object.toString()
  • 我有3个Caracteristic的子代,分别是CaracteristicACaracteristicBCaracteristicC,这些子代覆盖了名称(并可能覆盖级别)。
  • 我有一个名为Statistic的抽象类,它具有2个抽象函数: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));

问题:如果我想添加一个依赖于StatisticC的{​​{1}}和一个新子集CaracteristicA(我们将其命名为{{1} }),我需要添加一个变量来存储适当的特征,在循环中添加另一个Caracteristic来找到它,然后我终于能够使用适当的特征来创建所需的统计信息。

我相当确定我可以把整个事情搞得一团糟,我想知道我该怎么做。在Google上搜索和Stack Overflow似乎使我指向了“ 工厂”和“ 访客”设计模式,但我不确定它们是否会对我有所帮助或使情况更糟

我还尝试向CaracteristicD添加一个else if抽象方法,使孩子们可以用其类型覆盖它(例如: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;
    }
}

2 个答案:

答案 0 :(得分: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

例如,使用StatisticA蜜蜂

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个枚举

答案 1 :(得分:0)

也许我错了,因为我不太确定是否理解这个问题,但是为什么不使用Composite Pattern来表示Carateristic的层次结构呢? 这样,您将能够创建新的Composite特性(例如CaracteristicAB)并将其传递给需要多个Statistic的{​​{1}}。