Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/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_Oop_Enums - Fatal编程技术网

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