Java 如何解决以下设计问题?
我有一节课Java 如何解决以下设计问题?,java,oop,enums,Java,Oop,Enums,我有一节课 public class MyCompleteObject { private MyEnumA enumA; private MyObjectB objectB; } 编辑(使其更清晰):enumA和objectB是相互依赖的。MyObjectB有3个子类。MyEnumA的每个值只与MyObjectB的一个子类的object相关 因为MyObjectB有3个子类,所以objectB可以有3种类型。MyEnumA有很多值(~30,因为我只需要它们的文本值,除了en
public class MyCompleteObject {
private MyEnumA enumA;
private MyObjectB objectB;
}
编辑(使其更清晰):enumA和objectB是相互依赖的。MyObjectB有3个子类。MyEnumA的每个值只与MyObjectB的一个子类的object相关
因为MyObjectB有3个子类,所以objectB可以有3种类型。MyEnumA有很多值(~30,因为我只需要它们的文本值,除了enum,我想不出任何其他选项,如果enum不应该有这么多值,请纠正我)
因此,可以有30x3=90个髓鞘和肌瘤B的组合,但不是所有的90个组合都有效。这就是我被困的地方
解决这个问题的最佳方法是什么?我能想到的两个选择是:
1) 每当创建MyCompleteObject的实例时,我都应该检查enumA和objectB是否相互一致,如果它们不一致,则抛出异常(使用这种方法感觉不太舒服)
2) 使用enumA和objectB的不同组合生成许多子类。同样,这似乎不是一个有前途的解决方案,因为将根据enumA和objectB的不同组合创建许多子类
更新1)
在阅读了各种答案后,我能想到的第三种方法是:
3) 首先,按照@scottb的回答,创建可扩展的rick枚举类型。然后我想我可以在MyCompleteObject类中创建3个不同的构造函数,如下所示:
EnumA、EnumB和EnumC是我的enums,它们根据有效性进行分类,有3个子类MyObjectB(@scottb's answer)。MyObjectBFirst、MyObjectBSecond和MyObjectBThird是MyObjectB的三个亚类
public MyCompleteObject(EnumA enumA, MyObjectBFirst objectB){//}
public MyCompleteObject(EnumB enumB, MyObjectBSecond objectB){//}
public MyCompleteObject(EnumC enumC, MyObjectBThird objectB){//}
这可以确保编译时检查。但是,有3个不同的构造函数。我还研究了构建器模式,但无法将其应用于此处。它是面向添加可选参数的,但这里我有条件参数,所有参数都是必需的
谢谢 使类中的EnumA值不可变(无setter),并强制用户在构造函数中(或通过工厂)提供它。这将强制构造的类始终使用相同的EnumA值。然后可以在构造函数或其setter中对ObjectB强制类类型
public class MyCompleteObject {
private MyEnumA enumA;
private MyObjectB objectB;
public MyCompleteObject(MyEnumA enumA) { this.enumA = enumA; }
public void setObjectB(MyObjectB objectB) {
if(objectB.getClass() == enumA.getValidClassName()) {
this.objectB = objectB;
} else {
throw new InvallidArgumentException();
}
}
}
如果您决定使用子类,我建议使用一个基本接口,由三个抽象类来实现它——每种可能的ObjectB类型一个抽象类。然后,可以为表示给定ObjectB的每个EnumA值扩展特定的抽象类
public class MyObjectB {};
//Three sub classes of MyObjectB follows
public class MyObjectBType1 extends MyObjectB {};
public class MyObjectBType2 extends MyObjectB {};
public class MyObjectBType3 extends MyObjectB {};
// enum with constructor parameter that tells
// which enum value is compatible with which subclass of MyObjectB
public enum MyEnum {
A (MyObjectBType1.class),
B (MyObjectBType1.class),
C (MyObjectBType2.class),
D (MyObjectBType3.class),
E (MyObjectBType3.class),
;
private Class<? extends MyObjectB> validClassName;
MyEnum(Class<? extends MyObjectB> cls) {
this.validClassName = cls;
}
public Class<? extends MyObjectB> getValidClassName() {
return validClassName;
}
}
公共类MyObjectB{};
//MyObjectB的三个子类如下
公共类MyObjectBType1扩展了MyObjectB{};
公共类MyObjectBType2扩展了MyObjectB{};
公共类MyObjectBType3扩展了MyObjectB{};
//带有构造函数参数的枚举,该参数告诉
//哪个枚举值与MyObjectB的哪个子类兼容
公共枚举髓鞘{
A(MyObjectBType1.class),
B(MyObjectBType1.class),
C(MyObjectBType2.class),
D(MyObjectBType3.class),
E(MyObjectBType3.class),
;
私有类对于可扩展的富枚举类型来说,这听起来是一个很好的用例:
public enum EnumA implements MyEnumType {
COMMON_TO_A_1,
:
:
COMMON_TO_A_N;
@Override public void commonMethod1() { ... }
:
}
public enum EnumB implements MyEnumType {
COMMON_TO_B_1,
:
:
COMMON_TO_B_N;
@Override public void commonMethod1() { ... }
:
}
public interface MyEnumType {
void commonMethod1();
:
:
int commonMethodN(String myParam);
}
通过使用MyEnumType作为类型名称,您将能够传入任何枚举组,并对它们执行常见的类型安全操作。Java中的枚举功能强大且类型安全,通常优于滚动您自己的枚举类。我建议尽可能使用枚举功能
缺点是,这不是一种继承模式,并且没有公共枚举的超集。有时,可以根据需要在代码中对其进行模拟,而不会遇到太多麻烦。例如,您可以定义另一个枚举来提供“基本枚举”的类文字因此,您可以在执行的所有操作中始终引用这些常量,以及使用接口类型传递的任何枚举组
另一个缺点是,当您按接口类型传递枚举常量时,它将失去其作为Java枚举功能成员的身份。我的意思是,您将无法将EnumMap和EnumSet之类的东西用于接口类型常量。对于此限制,有一些解决方法,但它们可能并不总是干净的实现。 既然您提到您想要编译时检查解决方案,下面是我可以想到的一种方法
我建议您将MyOBjectB的一个子类注入MyEnumA定义中。
假设髓鞘在现实生活中可能是动物,而MyObjectB在现实生活中是动物的一种类型。因此,某些动物将是特定类型的,任何其他组合都是无效的
public class Test {
enum Animal {
PIGEON(new Flyers()), EAGLE(new Flyers()), //flyers
SNAKE(new Crawlers()), CROCODILE(new Crawlers()), //crawlers
COW(new Walkers()), DOG(new Walkers()); //walkers
private AnimalCharacteristics characteristics;
private Animal(AnimalCharacteristics characteristics) {
this.characteristics = characteristics;
}
public AnimalCharacteristics getCharacteristics() {
return characteristics;
}
}
interface AnimalCharacteristics {
void setWeight(double kgs);
};
public static class Flyers implements AnimalCharacteristics {
@Override
public void setWeight(double kgs) {
// do something
}
}
public static class Crawlers implements AnimalCharacteristics {
@Override
public void setWeight(double kgs) {
// do something
}
}
public static class Walkers implements AnimalCharacteristics {
@Override
public void setWeight(double kgs) {
// do something
}
}
public static void main(String[] args) {
System.out.println(Animal.PIGEON.getCharacteristics() instanceof Flyers); //true
System.out.println(Animal.PIGEON.getCharacteristics() instanceof Crawlers); //false
//Make updates
Animal.PIGEON.getCharacteristics().setWeight(0.75);
Animal.COW.getCharacteristics().setWeight(240.00);
}
}
更新
更新了下面的代码,因为OP留下了一条评论,说最好使用一个新的实例,而不是重复使用动物特征的实例
public class Test {
enum Animal {
PIGEON(Flyers.class), EAGLE(Flyers.class), //flyers
SNAKE(Crawlers.class), CROCODILE(Crawlers.class), //crawlers
COW(Walkers.class), DOG(Walkers.class); //walkers
private Class<? extends AnimalCharacteristics> characteristicsClass;
private Animal(Class<? extends AnimalCharacteristics> characteristicsClass) {
this.characteristicsClass = characteristicsClass;
}
public AnimalCharacteristics getCharacteristics() {
try {
System.out.println(" ~~~ Creating new instance of: " +
characteristicsClass.getCanonicalName());
return characteristicsClass.newInstance();
} catch (Exception e) {
System.out.println(" ~~~ Exception while creating instance: "
+ e.getMessage());
return null;
}
}
}
interface AnimalCharacteristics {
AnimalCharacteristics setWeight(double kgs);
};
public static class Flyers implements AnimalCharacteristics {
@Override
public AnimalCharacteristics setWeight(double kgs) {
return this;
}
}
public static class Crawlers implements AnimalCharacteristics {
@Override
public AnimalCharacteristics setWeight(double kgs) {
return this;
}
}
public static class Walkers implements AnimalCharacteristics {
@Override
public AnimalCharacteristics setWeight(double kgs) {
return this;
}
}
public static void main(String[] args) {
AnimalCharacteristics pigeon = Animal.PIGEON.getCharacteristics();
System.out.println("Is pigeon a flyer => "
+ (pigeon instanceof Flyers)); //true
System.out.println("Is pigeon a crawler => "
+ (pigeon instanceof Crawlers)); //false
//Make updates
pigeon.setWeight(0.75);
AnimalCharacteristics cow = Animal.COW.getCharacteristics().setWeight(240.00);
System.out.println("Cow is of type:" + cow.getClass().getCanonicalName());
}
}
1) 听起来是正确的选择。是什么让你感到不舒服?我的意思是为什么让别人尝试做一个无效的组合。错误组合的概率很高。在总共90个组合中,大约有一半是无效的。这就是为什么我感到不舒服。“每个脊髓瘤只有一个有效的MyObjectB”-这是否意味着MyObjectB在本质上也是常量-有三个常量?”…为什么让某人尝试进行无效的组合?“谁是某人?此人如何构造新的(可能无效的)实例?您是否考虑过使用生成器模式?“使用enumA和objectB的不同组合生成许多子类"--同意。每当组合爆炸的可能性出现时,你都应该开始寻找不同的解决方案。我认为Stack Overflow希望将建议作为注释放置。你的答案看起来像是建议尝试几件事。你可能希望将其移动到注释以避免向下移动votes@WandMaker看起来是个答案ome@Arkady如果给出例子,可能会更清楚。粗略地看,它看起来像是建议清理了措辞并添加了一个例子:)提示,这种方法的问题是它可以
~~~ Creating new instance of: Test.Flyers
Is pigeon a flyer => true
Is pigeon a crawler => false
~~~ Creating new instance of: Test.Walkers
Cow is of type:Test.Walkers