MST

星途 面试题库

面试题:Java组合模式在复杂业务逻辑中的深度应用与设计权衡

假设你正在开发一个企业级工作流系统,其中任务有不同的类型,有的任务可以包含子任务,类似于组合模式的结构。但业务要求任务之间有复杂的依赖关系、权限控制以及动态的任务调度策略。请详细阐述如何运用Java组合模式来设计这个系统,并且说明在设计过程中,针对依赖关系处理、权限控制和任务调度策略,你会做出哪些设计选择以及相应的权衡。
18.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. Java组合模式设计系统

  • 定义抽象任务类(Component)
    public abstract class Task {
        protected String name;
        protected Task parent;
    
        public Task(String name) {
            this.name = name;
        }
    
        public void setParent(Task parent) {
            this.parent = parent;
        }
    
        public Task getParent() {
            return parent;
        }
    
        public abstract void execute();
    
        public abstract void addTask(Task task);
    
        public abstract void removeTask(Task task);
    
        public abstract Task getTask(int index);
    }
    
  • 定义叶子任务类(Leaf)
    public class LeafTask extends Task {
        public LeafTask(String name) {
            super(name);
        }
    
        @Override
        public void execute() {
            System.out.println("执行叶子任务: " + name);
        }
    
        @Override
        public void addTask(Task task) {
            throw new UnsupportedOperationException("叶子任务不能添加子任务");
        }
    
        @Override
        public void removeTask(Task task) {
            throw new UnsupportedOperationException("叶子任务不能移除子任务");
        }
    
        @Override
        public Task getTask(int index) {
            throw new UnsupportedOperationException("叶子任务没有子任务");
        }
    }
    
  • 定义组合任务类(Composite)
    import java.util.ArrayList;
    import java.util.List;
    
    public class CompositeTask extends Task {
        private List<Task> children = new ArrayList<>();
    
        public CompositeTask(String name) {
            super(name);
        }
    
        @Override
        public void execute() {
            System.out.println("执行组合任务: " + name);
            for (Task task : children) {
                task.execute();
            }
        }
    
        @Override
        public void addTask(Task task) {
            task.setParent(this);
            children.add(task);
        }
    
        @Override
        public void removeTask(Task task) {
            children.remove(task);
        }
    
        @Override
        public Task getTask(int index) {
            return children.get(index);
        }
    }
    

2. 依赖关系处理

  • 设计选择
    • Task类中添加一个List<Task>类型的dependencies字段,用于存储该任务所依赖的其他任务。
    • 在执行任务前,先检查其依赖任务是否已完成。可以在Task类中添加一个isDependenciesMet()方法,遍历dependencies列表,检查每个依赖任务的执行状态。
    public abstract class Task {
        // 其他代码...
        protected List<Task> dependencies = new ArrayList<>();
    
        public void addDependency(Task task) {
            dependencies.add(task);
        }
    
        public boolean isDependenciesMet() {
            // 假设任务执行后有一个isExecuted标志
            for (Task task : dependencies) {
                if (!task.isExecuted()) {
                    return false;
                }
            }
            return true;
        }
    
        public abstract boolean isExecuted();
    }
    
    • execute()方法中,先调用isDependenciesMet()方法,只有当依赖满足时才执行任务。
    public class LeafTask extends Task {
        private boolean executed = false;
        // 其他代码...
        @Override
        public void execute() {
            if (isDependenciesMet()) {
                System.out.println("执行叶子任务: " + name);
                executed = true;
            } else {
                System.out.println("叶子任务 " + name + " 的依赖未满足,无法执行");
            }
        }
    
        @Override
        public boolean isExecuted() {
            return executed;
        }
    }
    
    public class CompositeTask extends Task {
        private boolean executed = false;
        // 其他代码...
        @Override
        public void execute() {
            if (isDependenciesMet()) {
                System.out.println("执行组合任务: " + name);
                for (Task task : children) {
                    task.execute();
                }
                executed = true;
            } else {
                System.out.println("组合任务 " + name + " 的依赖未满足,无法执行");
            }
        }
    
        @Override
        public boolean isExecuted() {
            return executed;
        }
    }
    
  • 权衡
    • 优点:这种方式直观简单,易于理解和实现,能够清晰地表达任务之间的依赖关系。
    • 缺点:可能会导致任务间的耦合度增加,特别是在依赖关系复杂时,维护和修改依赖关系可能会变得困难。而且如果依赖任务的执行状态判断逻辑复杂,会使isDependenciesMet()方法变得臃肿。

3. 权限控制

  • 设计选择
    • Task类中添加一个UserRole类型的requiredRole字段,用于表示执行该任务所需的用户角色。
    • 在执行任务前,检查当前用户的角色是否满足任务的requiredRole。可以在Task类中添加一个hasPermission(User user)方法,根据用户的角色判断是否有权限执行任务。
    public enum UserRole {
        ADMIN, USER, GUEST
    }
    
    public abstract class Task {
        // 其他代码...
        protected UserRole requiredRole;
    
        public Task(String name, UserRole requiredRole) {
            this.name = name;
            this.requiredRole = requiredRole;
        }
    
        public boolean hasPermission(User user) {
            return user.getRole().ordinal() >= requiredRole.ordinal();
        }
    }
    
    public class User {
        private UserRole role;
    
        public User(UserRole role) {
            this.role = role;
        }
    
        public UserRole getRole() {
            return role;
        }
    }
    
    • execute()方法中,先调用hasPermission(User user)方法,只有当用户有权限时才执行任务。
    public class LeafTask extends Task {
        // 其他代码...
        @Override
        public void execute(User user) {
            if (hasPermission(user)) {
                System.out.println("执行叶子任务: " + name);
            } else {
                System.out.println("用户 " + user.getRole() + " 没有权限执行叶子任务 " + name);
            }
        }
    }
    
    public class CompositeTask extends Task {
        // 其他代码...
        @Override
        public void execute(User user) {
            if (hasPermission(user)) {
                System.out.println("执行组合任务: " + name);
                for (Task task : children) {
                    task.execute(user);
                }
            } else {
                System.out.println("用户 " + user.getRole() + " 没有权限执行组合任务 " + name);
            }
        }
    }
    
  • 权衡
    • 优点:将权限控制逻辑集中在Task类中,易于管理和维护。通过枚举来表示用户角色,使得权限判断逻辑清晰明了。
    • 缺点:这种方式假设用户角色是一种简单的层次结构,对于复杂的权限模型(如基于资源的权限控制、多维度权限等)可能不太适用,扩展性有限。

4. 任务调度策略

  • 设计选择
    • 定义一个TaskScheduler类,负责管理任务的调度。可以在TaskScheduler类中维护一个任务队列,根据不同的调度策略将任务添加到队列中,并按策略执行任务。
    • 例如,实现一个简单的先进先出(FIFO)调度策略:
    import java.util.LinkedList;
    import java.util.Queue;
    
    public class TaskScheduler {
        private Queue<Task> taskQueue = new LinkedList<>();
    
        public void addTask(Task task) {
            taskQueue.add(task);
        }
    
        public void executeTasks(User user) {
            while (!taskQueue.isEmpty()) {
                Task task = taskQueue.poll();
                if (task.hasPermission(user) && task.isDependenciesMet()) {
                    task.execute(user);
                }
            }
        }
    }
    
    • 对于动态调度策略,可以在Task类中添加一个Priority字段表示任务优先级,在TaskScheduler类中根据优先级对任务队列进行排序。
    public abstract class Task {
        // 其他代码...
        protected int priority;
    
        public Task(String name, UserRole requiredRole, int priority) {
            this.name = name;
            this.requiredRole = requiredRole;
            this.priority = priority;
        }
    
        public int getPriority() {
            return priority;
        }
    }
    
    import java.util.PriorityQueue;
    import java.util.Queue;
    
    public class TaskScheduler {
        private Queue<Task> taskQueue = new PriorityQueue<>((t1, t2) -> t2.getPriority() - t1.getPriority());
    
        public void addTask(Task task) {
            taskQueue.add(task);
        }
    
        public void executeTasks(User user) {
            while (!taskQueue.isEmpty()) {
                Task task = taskQueue.poll();
                if (task.hasPermission(user) && task.isDependenciesMet()) {
                    task.execute(user);
                }
            }
        }
    }
    
  • 权衡
    • 优点:将任务调度逻辑封装在TaskScheduler类中,使得系统的任务调度部分易于扩展和维护。通过不同的数据结构(如队列、优先队列)可以方便地实现不同的调度策略。
    • 缺点:如果调度策略非常复杂,可能会导致TaskScheduler类变得庞大和复杂,需要进行良好的模块化设计来避免代码混乱。同时,动态调度策略可能会增加系统的运行时开销,例如优先队列的排序操作。