用多态替换switch-case

时间:2018-02-17 09:24:10

标签: java design-patterns polymorphism visitor

我知道已经有类似的问题,但看着它们我仍然对如何设计我的代码有一些疑问。我有一项服务允许User 注册/登录/更新/删除。问题是User是一个抽象类型,它包含数据typeOfUser,基于该数据应该调用实际的注册/更新/删除方法,现在我在switch-case块中执行此操作。我想用更好的设计取代它。

UserController.java

public class UserController {

    public UserDto register(UserDto user) {
        switch(user.getTypeOfUser()) {
        case DRIVER: return driverService.register(user);
        case CUSTOMER: return customerService.register(user);
        // ...
        }
    } 

    public UserDto update(UserDto user) {
        switch(user.getTypeOfUser) {
        case DRIVER: return driverService.update((DriverDto) user);
        case CUSTOMER: return customerService.update((CustomerDto) user);
        // ...
        }
    }

    public UserDto login(long userId) {
        loginService.login(userId);

        UserBO user = userService.readById(userId);

        switch(user.getTypeOfUser) {
        case DRIVER: return DriverDto.fromBO((DriverBO) user);
        case CUSTOMER: return CustomerDto.fromBO((CustomerBO) user);
        // ...
        }
    }

    // ...
}

我理解可以使用Visitor模式之类的东西,但我真的需要在Enum本身添加 registration / login / update / delete 的方法吗? ?我对如何做到这一点并不清楚,任何帮助都表示赞赏。

4 个答案:

答案 0 :(得分:2)

  

我想用更好的设计取而代之。

替换switch语句并利用 Polymorphism 的第一步是确保每个操作都有一个合同(读取方法签名),而不管用户类型。以下步骤将说明如何实现此目的:

步骤1:定义执行所有操作的通用界面

interface UserService {
    public UserDto register(UserDto user);
    public UserDto update(UserDto user);
    public UserDto login(UserDto user)
}

第2步:让UserController将UserService作为依赖项

public class UserController {

    private UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    public UserDto register(UserDto user) {
       userService.register(user);
    } 

    public UserDto update(UserDto user) {
        userService.update(user);

    }

    public UserDto login(long userId) {
        userService.login(user);
    }

}

步骤3:创建子类以处理将CustomerDto和CustomerBO作为依赖关系的不同类型的用户

class CustomerService implements UserService {
    private CustomerDto userDto;
    private CustomerBO userBO;

    public CustomerService(UserDto userDto,UserBO userBo) {
          this.userDto = (CustomerDto)userDto;
          this.userBO= (CustomerBO)userBo;
    }

    //implement register,login and update methods to operate on userDto and userBo
}

以类似的方式实现DriverService类,分别依赖DriverBoDriverDto个对象。

步骤4:实现一个运行时工厂,决定将哪个服务传递给UserController

public UserControllerFactory {
    public static void createUserController(UserDto user) {
       if(user.getTypeOfUser().equlas(CUSTOMER)) { 
           return new UserController(new CustomerService(user));
       } else if(user.getTypeOfUser().equlas(DRIVER)) {
           return new UserController(new DriverService(user));
       }
    }

}

步骤5致电工厂以创建用户控制器

UserDto userDto = someMethodThatCreatesUserDto(();
UserController controller = UserControllerFactory.createUserController(user);
controller.register();
controller.update();
controller.login();

上述方法的优点是switch / if-else语句会一直移动到单个类,即工厂。

答案 1 :(得分:1)

你想要这样的东西:

public abstract class User {
    abstract void register();
    abstract void update();
    abstract void login();

    // maybe some more common non-abstract methods
}

任何类型的User都有一个扩展此抽象类的类,因此必须实现其所有抽象方法,如下所示:

public class Driver extends User {
    public void register() {
        // do whatever a driver does when register...
    }
    public void update() {
        // do whatever a driver does when update...
    }
    public void login() {
        // do whatever a driver does when login...
    }
}

public class Customer extends User {
    public void register() {
        // do whatever a customer does when register...
    }
    public void update() {
        // do whatever a customer does when update...
    }
    public void login() {
        // do whatever a customer does when login...
    }
}

这样,您就可以避免任何开关案例代码。例如,您可以拥有User个数组,每个数组都将使用new Driver()new Customer()进行实例化。然后,例如,如果您正在迭代此数组并执行所有User s login()方法,则将根据其特定类型调用每个用户的login()==>无需开关箱,无需铸造!

答案 2 :(得分:0)

非常简单的示例(仅适用于DriverDto和CustomerDto的不同登录逻辑) - 我已经从字段typeOfUser辞职(因为在我的解决方案中没有必要) - 我不确定这是否可行你的解决方案:

public abstract class UserDto {
    // put some generic data & methods here
}

public class CustomerDto extends UserDto {

    private String customerName;

    public String getCustomerName() {
        return customerName;
    }

    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }
}

public class DriverDto extends UserDto {

    private String driverName;

    public String getDriverName() {
        return driverName;
    }

    public void setDriverName(String driverName) {
        this.driverName = driverName;
    }
}

public class ThisIsServiceOrDelegateToOtherServices {

    public void login(CustomerDto customer) {
        String name = customer.getCustomerName();
        System.out.println(name);
        // work on name here
    }

    public void login(DriverDto customer) {
        String name = customer.getDriverName();
        System.out.println(name);
        // work on name here
    }

}

用法:

public static void main(String... args) {
    //demo data
    CustomerDto customer = new CustomerDto();
    customer.setCustomerName("customerName");
    DriverDto driver = new DriverDto();
    driver.setDriverName("driverName");
    // usage
    ThisIsServiceOrDelegateToOtherServices service = new ThisIsServiceOrDelegateToOtherServices();
    service.login(customer);
    service.login(driver);
}

答案 3 :(得分:0)

如果你真的需要在你的UserDTO中使用TypeOfUser - 枚举,那么你可以用一个服务扩展你的枚举。因此,您创建一个TypeOfUserService接口。 CustomerSerivce和DriverService将继承该服务:

    public interface TypeOfUserService {
       public void register(UserDTO user);
       // ...
    }

    public class CustomerService implements TypeOfUserService {
       @Override
       public void register(UserDTO user) {
         // ...
       }
    }

    public class DriverService implements TypeOfUserService {
       @Override
       public void register(UserDTO user) {
         // ...
       }
    }

然后在TypeOfUser枚举中创建注册,更新等方法:

public enum TypeOfUser {

  DRIVER(new DriverService()), 
  CUSTOMER(new CustomerService());

  private TypeOfUserService typeOfUserService;

  TypeOfUser(TypeOfUserService typeOfUserService) {
    this.typeOfUserService = typeOfUserService;
  }

  public static void register(String typeOfUser, UserDTO user) {
     TypeOfUser.valueOf(typeOfUser).typeOfUserService.register(user);
  } 
  // ...

 }

然后您可以通过以下方式调用注册方法:

    class UserController() {
      public UserDto register(UserDto user) { 
        TypeOfUser.register(user.getTypeOfUser, user);
      }
    }