Java 将开关盒更换为新的
我知道已经有类似的问题了,但是看着它们,我仍然对我应该如何设计我的代码有一些疑问。我有一个允许用户注册/登录/更新/删除的服务。问题是,Java 将开关盒更换为新的,java,design-patterns,polymorphism,visitor,Java,Design Patterns,Polymorphism,Visitor,我知道已经有类似的问题了,但是看着它们,我仍然对我应该如何设计我的代码有一些疑问。我有一个允许用户注册/登录/更新/删除的服务。问题是,User是一个抽象类型,它包含数据typeOfUser,实际的注册/更新/删除方法应该基于该数据调用,现在我在一个开关案例块中这样做。我想换个更好的设计 UserController.java public class UserController { public UserDto register(UserDto user) { sw
User
是一个抽象类型,它包含数据typeOfUser
,实际的注册/更新/删除方法应该基于该数据调用,现在我在一个开关案例
块中这样做。我想换个更好的设计
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
本身中添加注册/登录/更新/删除方法吗?我真的不知道该怎么做,非常感谢您的帮助。您可能需要这样的帮助:
public abstract class User {
abstract void register();
abstract void update();
abstract void login();
// maybe some more common non-abstract methods
}
任何类型的用户都将拥有一个扩展此抽象类的类,因此必须实现其所有抽象方法,如下所示:
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
s数组,每个数组都将使用newdriver()
或newcustomer()
进行实例化。然后,例如,如果您在该数组上迭代并执行所有的User
slogin()
方法,则每个用户的login()将根据其特定类型被调用==>不需要开关盒,也不需要强制转换 非常简单的示例(仅适用于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);
}
如果您确实需要UserDTO中的
TypeOfUser
-enum,那么您可以使用一个服务扩展您的enum。所以您创建了一个TypeOfUserService接口。CustomerSerivce和DriversService将从该服务继承:
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枚举中创建register、update等方法:
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);
}
// ...
}
然后,您可以通过以下方式调用register方法:
class UserController() {
public UserDto register(UserDto user) {
TypeOfUser.register(user.getTypeOfUser, user);
}
}
我想换个更好的设计
替换switch
语句并利用多态性的第一步是确保每个操作都有一个契约(读取方法签名),而不管用户类型如何。以下步骤将解释如何实现这一点:
步骤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);
}
}
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
}
步骤3:创建子类以处理将CustomerTo和CustomerBO作为依赖项的不同类型的用户
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);
}
}
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
类,分别依赖于DriverBo
和DriverDto
对象
步骤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 user = someMethodThatCreatesUserDto(();
UserController controller = UserControllerFactory.createUserController(user);
controller.register();
controller.update();
controller.login();
上述方法的优点是switch/if-else语句被移动回一个类,即factory。诀窍是使用一个通用服务,然后用一个用户作为输入调用它一次,即调用
service.register(user)
一次,而不是使用开关
语句。@TimBiegeleisen是的,但是如何实现“更新(用户)”呢?因为更新与客户端或驱动程序特定的字段一起工作。所以它不能真正使用一般服务,它必须使用特定的。我指出多态性可以帮助你的一种方法。您可能需要这样一个switch语句,尽管它不一定在这里。@wesleyy的Visitor
模式解决了一个非常特殊的问题,并且在大多数情况下(如果不是全部的话)几乎都是一个过火的问题。请参阅我的答案,了解如何用简单的多模态来解决这个问题。有用的上下文:问题是“register(UserDto user)”方法只能根据“typeOfUser”字段决定注册的用户类型。我认为为每种用户创建一个单独的注册端点是不可伸缩的,你认为我错了吗?@wesleyy在我的实现中,你根本不需要UserDto-user
参数,这正是它的优点,也是多态性的优点。该实现是特定于类型的,因此类驱动程序
将有自己的注册、更新和登录方式,客户也是如此。这正是它的可扩展性所在,一旦你有了一个新类型的用户
,你创建了一个扩展用户
的新类,实现了抽象方法,你就可以开始了,你不必接触任何其他现有类。好吧,但是当我通过网络向你发送一个用户对象json时,如何确定要调用谁的.register)方法?您必须调用具体类型的方法,并且类型信息仅存储在json@wesleyyJSON是另一回事,它与业务逻辑层无关。但是如果我理解正确,根据JSON中的typeOfUser字段,您将实例化正确的用户。因此,如果它是typeOfUser:'driver'
您将转到User u1=newdriver()
。然后,当您调用u1.register()
时,将根据u1的运行时类型调用右侧的register()
,在这种情况下,它是Driver
。@wesleyy当您解组JSON时,您必须处理使对象成为正确类型的问题。如果您使用的是JAXB,这可能会很有用。可能是我在互联网上看到的多态性的最好解释。@Liga谢谢!!这是我多年来对StackOverflow的贡献中得到的最好的赞扬;)关于这个话题,我有更多的答案1)2)3)只是一个简短的说明,你认为呢