用多态替换开关盒

wes*_*eyy 5 java polymorphism design-patterns 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);
        // ...
        }
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

据我所知,类似Visitor模式可以被使用,但我真的需要补充的方法注册/登录/更新/删除Enum本身?我真的不知道如何做到这一点,感谢您的帮助。

CKi*_*ing 7

我想用一些更好的设计代替它。

替换switch语句并利用多态性的第一步是确保每个操作都具有单个协定(读取方法签名),而与用户类型无关。以下步骤将说明如何实现此目的:

步骤1:定义用于执行所有操作的通用接口

interface UserService {
    public UserDto register(UserDto user);
    public UserDto update(UserDto user);
    public UserDto login(UserDto user)
}
Run Code Online (Sandbox Code Playgroud)

步骤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);
    }

}
Run Code Online (Sandbox Code Playgroud)

步骤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
}
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

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

UserDto user = someMethodThatCreatesUserDto(();
UserController controller = UserControllerFactory.createUserController(user);
controller.register();
controller.update();
controller.login();
Run Code Online (Sandbox Code Playgroud)

上述方法的优点是将switch / if-else语句完全移回了一个类,即工厂。

  • 可能是我在互联网上看到的对多态性的最好解释。 (2认同)
  • @Liga 谢谢!!这是我多年来为 StackOverflow 做出贡献以来得到的最好的赞美;)我对这个主题还有更多答案 1) [多态性与策略模式](/sf/ask/2212623171/ -vs-strategy-pattern/31609220#31609220) 2) [多态可以做什么而继承不能做什么?](/sf/ask/2944274021/​​an-polymorphism-do-that-inheritance-不能)3)[Collections.sort使用什么设计模式?](/sf/ask/2970940941/#42442744) (2认同)

SHG*_*SHG 4

你想要这样的东西:

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

    // maybe some more common non-abstract methods
}
Run Code Online (Sandbox Code Playgroud)

任何类型的 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...
    }
}
Run Code Online (Sandbox Code Playgroud)

这样,您就可以避免任何 switch case 代码。例如,您可以有一组Users,其中每个数组都将使用new Driver()或进行实例化new Customer()。然后,例如,如果您迭代此数组并执行所有Userslogin()方法,则将根据其特定类型调用每个用户的 login() ==> 不需要 switch-case,不需要转换!