none none
none none

Reputation: 203

Java Enum or other collection

We currently have two customer codes "CUSTA" and "CUSTB". CUSTA can perform operations A1, A2, A3, A4, A5, A6. CUSTB can perform operations B1, B2, B3, B4, B5, B6 based on some conditions. Currently they are not expecting any more customer codes but I would like the design to be flexible. These can be stored in database but as I mentioned because it is not likely for a long time to have another customer code, it needs to be represented in code.

The application logic basic algorithm looks like

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

............... .................

I can write switch statements etc..to clean up the algorithm but my main concern is how to represent "CUSTA", "CUSTB", "A1","A2","A3","A4"..."A6","B1","B2"..."B6". Does the customer code be enum like

public enum CustomerCode { CUSTA, CUSTB }
public enum OperationsForA{ A1, A2, A3,...A6 }
public enum OperationsForB{ B1, B2, B3...B6}

Shall I create a Map where key is CustomerCode and add respective operations as values.

What would be the best solution to address this problem. Also it should be flexible to add "CUSTC" , for example, in the future.

Thanks

Upvotes: 5

Views: 244

Answers (5)

UFL1138
UFL1138

Reputation: 642

Assuming A1 is similar to B1, etc., I would implement it like this and giving them the same operation names:

public enum Customer {
    CUSTA, CUSTB;

    public void operation1(){
        switch(this){
        case CUSTA:
            // do A thing;
            break;
        case CUSTB:
            // do it the B way
            break;
        }
    }
}

Could that fit into what you're trying to do?

Of course if you're going to use an enum then you'll have to update the code when you get a new Customer type, but you'll probably have to update code anyway. One other and possibly better approach is to define an interface and use a Factory pattern; it might come out cleaner:

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 */
    }
}

One last thing I would add that would let you add implementations of Customer that will load with no code changes so long as they are on your classpath is to use reflection and pass in class names to the Factory:

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!");
    }
}

Upvotes: 0

Bohemian
Bohemian

Reputation: 424983

Enum is no good, because all instances of an enum must have the same methods.

Even using an enum for the type feels probably wrong, because you'll effectively be doing instanceof, which is usually a design smell.

I think you've got a classic case of inheritance:

public abstract class Customer {
    // common code
}
public class CustomerA extends Customer {
    // specific/decorated behaviour 
}
public class CustomerB extends Customer {
    // specific/decorated behaviour 
}

Try to abstract your methods to a common abstract behaviour in the base class.

Upvotes: 0

Nicole
Nicole

Reputation: 33197

If A1 corresponds to B1, A2 corresponds to B2, and so on, then you need polymorphism.

This means that you will have a generic CustomerOperations interface. Each customer code would create a concrete implementation of the CustomerOperations interface and return the correct operation corresopnding to Condition X, Condition Y, etc.

Set up your interfaces:

interface CustomerOperations {
  Operation operationForX();
  Operation operationForY();
}

interface Operation {
  Result someMethod();
}

Set up your enums and implement the interfaces:

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

Example usage: (this is where the polymorphism happens)

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

Upvotes: 4

Ted Hopp
Ted Hopp

Reputation: 234795

Since enum values are classes in Java, you can do something like this:

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.
}

Then your application logic could look like:

CustomerCode customerCode = . . .;
if (conditionX()} {
    customerCode.op1();
} else if (conditionY()) {
    customerCode.op2();
} // etc.

If it makes sense architecturally, you could move the if...else chain inside the enum codes; in which case you might only need a single method: doApplicableOperation().

A very different approach would be to define the operations as an interface (e.g., Runnable or Callable) and have an EnumMap from CustomerCode to instances of the interface. (There may be one EnumMap for each operation.)

Upvotes: 1

user207421
user207421

Reputation: 310869

I would make the whole thing a lookup table in a database, and write as little code as possible. It may or may not change, but even if it doesn't it will make more sense to everybody as a table.

Upvotes: 0

Related Questions