如何解决以下设计问题?

时间:2015-07-06 17:20:33

标签: java oop enums

我有一个班级

var data = [{
    "N": "3M",
        "a": "<img src=\"/img/samples/flag_red.gif\" alt=\"red\" height=\"16\" width=\"16\" border=\"0\"/>",
        "c": "<img src=\"/img/samples/flag_red.gif\" alt=\"red\" height=\"16\" width=\"16\" border=\"0\"/>",
        "r": "<img src=\"/img/samples/flag_red.gif\" alt=\"red\" height=\"16\" width=\"16\" border=\"0\"/>",
        "p": "<img src=\"https://a.na7.visual.force.com/resource/1260007793000/a/iconset/gray.gif\" alt=\" \" height=\"16\" width=\"1\" border=\"0\"/>"

}, {
    "N": "ABC corp",
        "a": "<img src=\"/img/samples/flag_red.gif\" alt=\"red\" height=\"16\" width=\"16\" border=\"0\"/>",
        "c": "<img src=\"/img/samples/flag_red.gif\" alt=\"red\" height=\"16\" width=\"16\" border=\"0\"/>",
        "r": "<img src=\"/img/samples/flag_green.gif\" alt=\"green\" height=\"16\" width=\"16\" border=\"0\"/>",
        "p": "<img src=\"https://a.na7.visual.force.com/resource/1260007793000/a/iconset/gray.gif\" alt=\" \" height=\"16\" width=\"1\" border=\"0\"/>"

}];

var modified = data.reduce(function (outArr, entry) {
    var outObj = {};
    for (key in entry) {
        if (entry.hasOwnProperty(key)) {
            var item = entry[key];
            var node = document.createElement("div");
            node.innerHTML = item;
            var img = node.querySelector("img");
            outObj[key] = img ? img.alt : item;
        }
    }
    outArr.push(outObj);
    return outArr;
}, []); //[{"N":"3M","a":"red","c":"red","r":"red","p":" "},{"N":"ABC corp","a":"red","c":"red","r":"green","p":" "}]

编辑(更清楚):enumA和objectB是相互依赖的。 MyObjectB有3个子类。并且MyEnumA的每个值都与MyObjectB

的子类中的一个对象相关

由于MyObjectB有3个子类,因此objectB可以是3种类型。 MyEnumA有很多值(~30,因为我只需要他们的文本值,不能想到除枚举之外的任何其他选项,如果枚举不应该有这么多值,请纠正我。)

因此,MyEnumA和MyObjectB可以有30x3 = 90种组合,但并非所有90种组合都有效。这就是我被困的地方。

解决这个问题的最佳方法是什么?我能想到的几个选项是:

1)每当创建一个MyCompleteObject实例时,我应检查enumA和objectB是否彼此一致,如果它们不一致则抛出异常(对这种方法不太满意)。

2)使用enumA和objectB的不同组合创建许多子类。同样,似乎并不是一个很有希望的解决方案,因为将根据不同的组合enumA和objectB创建许多子类。

更新1)

在阅读各种答案后我能想到的第三种方法是:

3)首先制作可扩展的rick枚举类型,如@ scottb的答案所述。然后我想我可以在MyCompleteObject类中创建3个不同的构造函数: EnumA,EnumB和EnumC是我的枚举,根据它们的有效性分为3个子类MyObjectB(@ scottb&#39;答案)。 MyObjectBFirst,MyObjectBSecond和MyObjectBThird是MyObjectB的三个子类。

public class MyCompleteObject {

   private MyEnumA enumA;

   private MyObjectB objectB; 

}

这可以确保编译时检查。但是,有3个不同的构造函数。我也查看了构建器模式但不适合它。它的目的是添加可选参数,但在这里我有条件参数,所有都是必需的。

谢谢!

3 个答案:

答案 0 :(得分:1)

使您的类中的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;
    }
}

答案 1 :(得分:1)

对于可扩展丰富的枚举类型来说,这听起来是一个很好的用例:

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中的枚举工具是健壮且类型安全的,并且通常优于滚动自己的枚举类。我建议尽可能使用枚举工具。

缺点是这不是继承模式,并且没有常见枚举的超集。有时,根据您的需要,可以在代码中模拟,而不会有太多麻烦。例如,您可以定义另一个枚举,它提供“基本枚举”的类文字,以便您可以始终在您执行的所有操作中引用这些常量,以及使用接口类型传递的任何枚举组。 / p>

另一个缺点是,当您通过接口类型传递枚举常量时,它会失去其作为Java枚举工具成员的身份。这意味着你将无法使用类似EnumMap和EnumSet的东西与你的接口类型常量。有这种限制的解决方法,但它们可能并不总是干净实现。

答案 2 :(得分:1)

既然你提到了你想要编译时检查解决方案。这是我能想到的一种方式。

我建议您将MyOBjectB的子类注入MyEnumA定义。 让我们假设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留下了一条评论,说新的实例更可取而不是重新使用AnimalCharacteristics的实例

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());
    }
}

如果运行上面的代码将产生以下输出:

 ~~~ 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