如何在不重复代码的情况下扩展抽象 class 的 java 功能?

How to extend java functionality of an abstract class without duplicating code?

我有一个问题,我使用的是许多其他应用程序用来标准化某些逻辑并减少重复代码的通用 class。大多数使用此 Operations class 的 classes 只需要 CRUD 方法。但是,在这种特定情况下,我需要调用第 5 种方法 execute。虽然,我不想重复 on 方法中定义的逻辑。

您建议我如何通过同一流程调用 onExecute 方法?如果我能。

理想情况下,我希望能够执行以下操作:

  1. Operations.on 调用 x 方法(如果存在)
  2. 像 UserOperations 一样扩展 classes 可以定义任意数量的自定义方法,这些方法将以与调用 MyOperation 方法相同的方式调用。

我在下面分享了 class 结构。我不得不隐藏代码以保护隐私协议。如果您需要对此问题进行更多说明,请告诉我,我可以尝试进一步阐述。

// Common Code Package
public abstract class Operations {

    public enum MyOperation {
        CREATE, RETRIEVE, UPDATE, DELETE
    }


    protected MyCustomObject custom;
    protected MyOperation operation;


    public void on(MyOperation operation, Object instance, MyCustomObject custom){

        this.custom = custom;
        this.operation = operation;

        if(operation == null){
            throw new IllegalArgumentException("'operation' is invalid");
        }
        if(instance == null){
            throw new IllegalArgumentException("'instance' is invalid");
        }
        if(custom == null){
            throw new IllegalArgumentException("'custom' is invalid");
        }

        /* Other Logic */

        switch(operation){
        case CREATE: 
            onCreate(instance);
            break;
        case RETRIEVE: 
            onRetrieve(instance);
            break;
        case UPDATE: 
            onUpdate(instance);
            break;
        case DELETE: 
            onDelete(instance);
            break;
        default: 
            throw new UnsupportedOperationException(String.format("'%s' Operation Not Supported!", operation));
        }

        /* More Logic */


        if(!custom.isOkay()){
            throw new IllegalStateException("Not Okay");
        }
    }    


    protected void onCreate(Object instance) {
        throw new UnsupportedOperationException("Create Operation Not Supported!");
    }

    protected void onRetrieve(Object instance) {
        throw new UnsupportedOperationException("Retrieve Operation Not Supported!");
    }

    protected void onUpdate(Object instance) {
        throw new UnsupportedOperationException("Update Operation Not Supported!");
    }

    protected void onDelete(Object instance) {
        throw new UnsupportedOperationException("Delete Operation Not Supported!");
    }
}

--

// Local Code Package
@Service
public class UserOperations extends Operations {


    public void onExecute(Object instance){

        /* Different Logic */

        SomeType type = (SomeType) instance;

        if(type.someOtherField == null){
            custom.setOkay(false);
        }
    }


    @Override
    protected void onCreate(Object instance){

        /* Create Logic */

        SomeType type = (SomeType) instance;

        if(type.someField == null){
            custom.setOkay(false);
        }
    }
}

--

// Local Code Package
@RestController
@RequestMapping("/user")
public class UserController {


    @Autowired
    private UserService userService;
    @Autowired
    private UserOperations userOperations;


    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public Object create(SomeType instance){

        MyCustomObject custom = new MyCustomObject();
        userOperations.on(MyOperation.CREATE, instance, custom);

        return userService.create(instance);
    }

    @RequestMapping(value = "/execute", method = RequestMethod.POST)
    public Object execute(){

        MyCustomObject custom = new MyCustomObject();

        // How??? MyOperation doesn't include EXECUTE
        // userOperations.onExecute(instance); doesn't include logic inside `on` method
        // userOperations.on(MyOperation.EXECUTE, instance, custom);

        return userService.execute(instance);
    }
}

Although, I don't want to repeat the logic that is defined inside the on method.

最简单的情况:将 /* Other Logic *//* More Logic */ 移动到它们自己的 protected 方法中 Operations class 以便您可以自由复制其中的内容on 在扩展时的边缘情况。在您的服务方法中,您最终会得到:

@Service
public class UserOperations extends Operations {


    public void onExecute(Object instance){

        otherLogic(); // defined in abstract Operations.class

        SomeType type = (SomeType) instance;
        if(type.someOtherField == null){
            custom.setOkay(false);
        }

        moreLogic(); // defined in abstract Operations.class
    }

}

否则,您无论如何都需要扩展 enum 可用操作,或者引入一些新方法,例如 MyOperation[] supportedOperations(),然后需要为每个可能的操作连同接口一起实现。 MyOperation.enum 可以为每个枚举值定义一个接口,例如:

enum MyOperation {
    CREATE(HasCreate.class), DELETE(HasDelete.class);
    public final Class clazz;
    MyOperation(Class clazz) {
        this.clazz = clazz;
    }
}

我整晚都在考虑这个问题,并决定如果我按名称查找方法,我可以使用反射来完成我想要的。我在两边都做了一些小的调整。我为枚举器添加了向后支持,以免干扰使用相同底层公共代码的其他应用程序。枚举器方法肯定会存在,而任何其他方法可能存在也可能不存在,具体取决于扩展 class。

我不喜欢没有其他类型 "type-safe",但我认为它仍然不错。如果您有更好的想法,请告诉我。

* 更新代码 *

// Common Code Package
public abstract class Operations {

    private static final Logger LOGGER = LoggerFactory.getLogger(Operations.class);

    public enum MyOperation {
        CREATE, RETRIEVE, UPDATE, DELETE
    }


    protected MyCustomObject custom;
    protected String operation; 

    public void on(MyOperation operation, Object instance, MyCustomObject custom){

        // Change 'operation' to Standard Case e.g. CREATE => Create
        String lower = operation.name().toLowerCase();
        String value = lower.substring(0, 1).toUpperCase() + lower.substring(1);

        // Delegate Invocation
        on(value, obj, custom);
    }
    public void on(String operation, Object instance, MyCustomObject custom){

        this.custom = custom;
        this.operation = operation;

        if(operation == null){
            throw new IllegalArgumentException("'operation' is invalid");
        }
        if(instance == null){
            throw new IllegalArgumentException("'instance' is invalid");
        }
        if(custom == null){
            throw new IllegalArgumentException("'custom' is invalid");
        }

        /* Other Logic */

        try {
            String methodName = String.format("on%s", operation);
            this.getClass().getDeclaredMethod(methodName, Object.class).invoke(this, obj);

        } catch(IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException e){
            LOGGER.error("ERROR: {}", e);
        }


        /* More Logic */


        if(!custom.isOkay()){
            throw new IllegalStateException("Not Okay");
        }
    }    


    public void onCreate(Object instance) {
        throw new UnsupportedOperationException("Create Operation Not Supported!");
    }

    public void onRetrieve(Object instance) {
        throw new UnsupportedOperationException("Retrieve Operation Not Supported!");
    }

    public void onUpdate(Object instance) {
        throw new UnsupportedOperationException("Update Operation Not Supported!");
    }

    public void onDelete(Object instance) {
        throw new UnsupportedOperationException("Delete Operation Not Supported!");
    }
}

--

// Local Code Package
@Service
public class UserOperations extends Operations {


    public void onExecute(Object instance){

        /* Different Logic */

        SomeType type = (SomeType) instance;

        if(type.someOtherField == null){
            custom.setOkay(false);
        }
    }


    @Override
    public void onCreate(Object instance){

        /* Create Logic */

        SomeType type = (SomeType) instance;

        if(type.someField == null){
            custom.setOkay(false);
        }
    }
}

--

// Local Code Package
@RestController
@RequestMapping("/user")
public class UserController {


    @Autowired
    private UserService userService;
    @Autowired
    private UserOperations userOperations;


    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public Object create(SomeType instance){

        MyCustomObject custom = new MyCustomObject();
        userOperations.on(MyOperation.CREATE, instance, custom);

        return userService.create(instance);
    }

    @RequestMapping(value = "/execute", method = RequestMethod.POST)
    public Object execute(SomeType instance){

        MyCustomObject custom = new MyCustomObject();
        userOperations.on("Execute", instance, custom);

        return userService.execute(instance);
    }
}