Java枚举或其他集合
我们目前有两个客户代码“CUSTA”和“CUSTB”。 CUSTA可以执行操作A1,A2,A3,A4,A5,A6。 CUSTB可以根据一些条件执行操作B1,B2,B3,B4,B5,B6。目前他们并不期待更多的客户代码,但我希望设计具有灵活性。这些可以存储在数据库中,但正如我所提到的,因为长时间不可能拥有另一个客户代码,它需要用代码表示。Java枚举或其他集合
应用逻辑基本算法看起来像
if ConditionX is true
then if customerCode is "CUSTA"
then applicableOperation = 'A1'
else
if customerCode is "CUSTB"
then applicableOperation = 'B1'
end
else
if ConditionY is true
then
if customerCode is "CUSTA"
then applicableOperation = 'A2'
else
if customerCode is "CUSTB"
then applicableOperation = 'B2'
end
else
............... ................ 。
我可以编写switch语句等来清理算法,但我主要关心的是如何表示“CUSTA”,“CUSTB”,“A1”,“A2”,“A3”,“A4”。 .. “A6”, “B1”, “B2” ... “B6”。客户是否码是枚举像
public enum CustomerCode { CUSTA, CUSTB }
public enum OperationsForA{ A1, A2, A3,...A6 }
public enum OperationsForB{ B1, B2, B3...B6}
我应当创造一个Map
,其中关键是CustomerCode,并添加相应的操作作为值。
什么是最好的解决方案来解决这个问题。例如,未来还应该灵活地添加“CUSTC”。
感谢
我会使整个事情在数据库中查找表,并编写尽可能少的代码越好。它可能会也可能不会改变,但即使它不会,每个人都会更有意义。
假设A1相似,B1等,我会实现它这样的,给他们同样的操作名称:
public enum Customer {
CUSTA, CUSTB;
public void operation1(){
switch(this){
case CUSTA:
// do A thing;
break;
case CUSTB:
// do it the B way
break;
}
}
}
能配合到你想要做什么?
当然,如果你要使用枚举,那么当你得到一个新的Customer类型时,你将不得不更新代码,但是你可能不得不更新代码。另一种可能更好的方法是定义一个接口并使用Factory模式;它可能会来了清洁:
public interface Customer {
void operation1();
}
class CustomerFactory{
public static Customer getByName(String cust){
if(cust.equals("CUSTA")){
return new CustomerA();
}
if(cust.equals("CUSTB")){
return new CustomerB();
}
throw new IllegalArgumentException(
"Customer type " + cust + " not supported.");
}
}
class CustomerA implements Customer{
@Override
public void operation1(){
/* A's implementation */
}
}
class CustomerB implements Customer{
@Override
public void operation1(){
/* B's implementation */
}
}
我想补充一点,就会让你添加客户的实现,这将不加载代码的最后一件事改变,只要它们在你的类路径是使用反射,并通过课堂名工厂:
package test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class CustomerTest{
public static void main(String[] arg){
CustomerFactory.getByName("test.CustomerA").operation1();
CustomerFactory.getByName("test.CustomerB").operation1();
CustomerFactory.getByName("test.CustomerC").operation1();
}
}
interface Customer {
void operation1();
}
class CustomerFactory{
public static Customer getByName(final String cust){
try{
Class<? extends Customer> customerSubclass
= Class.forName(cust).asSubclass(Customer.class);
Constructor<? extends Customer> constructor
= customerSubclass.getDeclaredConstructor(new Class[0]);
return constructor.newInstance(new Object[0]);
} catch(ClassNotFoundException e){
System.err.println("Trouble finding .class file for class "+cust+". "+e);
} catch(ClassCastException e){
System.err.println("Found .class file for "+cust+" but it doesn't implement Customer."+" "+e);
} catch(NoSuchMethodException e){
System.err.println("Class "+cust+" doesn't provide a no-arg constructor. :("+" "+e);
} catch(IllegalAccessException e){
System.err.println("No-arg constructor isn't accessible. :("+" "+e);
} catch(InstantiationException e){
System.err.println("Couldn't instantiate?"+" "+e);
} catch(InvocationTargetException e){
System.err.println("Umm... something else broke."+" "+e);
}
throw new IllegalArgumentException(
"Customer type " + cust + " not supported.");
}
}
class CustomerA implements Customer{
@Override
public void operation1(){
/* A's implementation */
System.out.println("I'm an A doing operation1!");
}
}
class CustomerB implements Customer{
@Override
public void operation1(){
/* B's implementation */
System.out.println("I'm a B doing operation1!");
}
}
对于第一种方法,在顶层将'operation1'声明为抽象方法,然后为每个枚举常量实现方法好得多。 –
这不是一个坏主意,但它也取决于实现的相似或不同。没有进一步的细节,很难知道采取哪种方法。 +1虽然。 – UFL1138
如果A1对应B1,A2对应B2,等等,那么你需要polymorphism。
这意味着您将拥有一个通用的CustomerOperations
接口。每个客户代码将创建CustomerOperations
接口的具体实施,并将相应的正确操作返回到Condition X
,Condition Y
等。
设置您的接口:
interface CustomerOperations {
Operation operationForX();
Operation operationForY();
}
interface Operation {
Result someMethod();
}
设置您的枚举和实现的接口:
enum OperationsForA implements Operation {
A1, A2, A3;
// Implement someMethod
}
enum OperationsForB implements Operation {
B1, B2, B3;
// Implement someMethod
}
enum CustomerCode implements CustomerOperations {
CUSTA {
Operation operationForX() {
return OperationsForA.A1;
}
Operation operationForY() {
return OperationsForA.A2;
}
},
CUSTB {
Operation operationForX() {
return OperationsForB.B1;
}
Operation operationForY() {
return OperationsForB.B2;
}
}
;
}
用法示例:(这是那里的多态性发生)
public class Main {
public static void main(String... args) {
CustomerOperations operator = CustomerOperations.CUSTA;
if (conditionX) {
// Could be inlined, but I've separated it for type readability:
// Get the appropriate operation from the operator.
Operation thingToDoForX = operator.operationForX();
// Run the operation and get the result
Result result = thingToDoForX.someMethod();
}
}
}
+1这是完美的灵活性。过去我看到过于复杂的设计,因为他们努力灵活地适应从未发生的变化。在Java中保持这种逻辑可以更容易地进行更改,但如果不更改,则很少丢失。 – Brandon
由于enum
值是Java类,你可以做这样的事情:
public enum CustomerCode {
CUSTA {
@Override
public void op1() {
// operation A1 implementation
}
@Override
public void op2() {
// operation A2 implementation
}
// etc.
},
CUSTB {
@Override
public void op1() {
// operation B1 implementation
}
// etc.
};
public abstract void op1();
public abstract void op2();
// etc.
}
那么你的应用程序逻辑可能是这样的:
CustomerCode customerCode = . . .;
if (conditionX()} {
customerCode.op1();
} else if (conditionY()) {
customerCode.op2();
} // etc.
如果这是有道理的建筑,你可以移动if...else
链内枚举代码;在这种情况下,您可能只需要一种方法:doApplicableOperation()
。
甲非常不同的方法是定义的操作的接口(例如,Runnable
或Callable
),并具有从一个CustomerCode
EnumMap
到接口的实例。 (每个操作可能有一个EnumMap
。)
枚举不好,因为枚举的所有实例都必须有相同的方法。
即使使用类型的枚举感觉可能是错误的,因为你将有效地做instanceof,这通常是一种设计气味。
我想你已经有了传承的经典案例:
public abstract class Customer {
// common code
}
public class CustomerA extends Customer {
// specific/decorated behaviour
}
public class CustomerB extends Customer {
// specific/decorated behaviour
}
尝试抽象的方法,在基类常见的抽象行为。
我同意..但我正在寻找Java的替代品。 –
如果你真的同意,*为什么*你在寻找Java的替代品? – EJP