• 对组合模式的理解


    一、场景

    设计模式很依赖场景,通过场景也能更好理解这种模式解决了什么问题。

    • 无论是学校还是公司,都存在组织结构。而且,这种结构多半是树状结构。
    • 当类与类之间的结构也需要表达成树状结构时,组合模式就来大显身手了。

    1、题目描述 【案例来源

    小明所在的公司内部有多个部门,每个部门下可能有不同的子部门或者员工。
    请你设计一个组合模式来管理这些部门和员工,实现对公司组织结构的统一操作。部门和员工都具有一个通用的接口,可以获取他们的名称以及展示公司组织结构。

    2、输入描述

    第一行是一个整数 N(1 <= N <= 100),表示后面有 N 行输入。
    接下来的 N 行,每行描述一个部门或员工的信息。部门的信息格式为 D 部门名称,员工的信息格式为 E 员工名称,其中 D 或 E 表示部门或员工。

    3、输出描述

    输出公司的组织结构,展示每个部门下的子部门和员工

    4、输入示例

    MyCompany
    8
    D HR
    E HRManager
    D Finance
    E AccountantA
    E AccountantB
    D IT
    E DeveloperA
    E DeveloperB

    5、输出示例

    Company Structure:
    MyCompany
    HR
    HRManager
    Finance
    AccountantA
    AccountantB
    IT
    DeveloperA
    DeveloperB

    二、实现(假的组合模式)

    1、代码

    public interface Component {
        String showName();
    }
    
    public class Company implements Component {
        private final String name;
    
        @Setter
        private List<Department> departments;
    
        public Company(String name) {
            this.name = name;
        }
    
        public void addDepartment(Department department) {
            if (CollectionUtils.isEmpty(departments)) {
                departments = new ArrayList<>();
                departments.add(department);
    
            } else {
                departments.add(department);
            }
        }
    
        @Nullable
        public Department gotLastDepartment() {
            if (CollectionUtils.isEmpty(departments)) {
                return null;
            } else {
                return departments.get(departments.size() - 1);
            }
        }
    
        @Override
        public String showName() {
            return name;
        }
    
        public String showCompanyStructure() {
            StringBuilder sb = new StringBuilder("Company Structure:\n").append(showName()).append("\n");
    
            Optional.ofNullable(departments).ifPresent(departments -> departments.stream().filter(Objects::nonNull).forEach(department -> {
                sb.append("  ").append(department.showName()).append("\n");
                Optional.ofNullable(department.getEmployees()).ifPresent(employees -> {
                    employees.stream().forEach(
                            employee -> sb.append("    ").append(employee.showName()).append("\n")
                    );
                });
            }));
    
            return sb.toString();
        }
    }
    
    @Data
    public class Department implements Component {
        private String name;
        private List<Employee> employees;
    
        public Department(String name) {
            this.name = name;
        }
    
        @Override
        public String showName() {
            return this.name;
        }
    
        public void addEmployee(Employee employee) {
            if (CollectionUtils.isEmpty(employees)) {
                employees = new ArrayList<>();
                employees.add(employee);
            } else {
                employees.add(employee);
            }
        }
    }
    
    @AllArgsConstructor
    public class Employee implements Component {
        private String name;
    
        @Override
        public String showName() {
            return name;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    public class Application {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            String companyName = scanner.nextLine();
            Company company = new Company(companyName);
            int n = scanner.nextInt();
            for (int i = 0; i < n; i++) {
                String type = scanner.next();
                String name = scanner.next();
    
                if (StringUtils.equals(type, "D")) {
                    company.addDepartment(new Department(name));
                } else if (StringUtils.equals(type, "E")) {
                    Department department = company.gotLastDepartment();
                    department.addEmployee(new Employee(name));
                }
            }
            System.out.printf(company.showCompanyStructure());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2、为什么上面的写法是假的组合模式?

    • 场景中的公司结构:
      在这里插入图片描述
      • 对这个结构进行建模,不应该是:
    public class Company implements Component {
        private final String name;
    
        @Setter
        private List<Department> departments;
    }
    
    public class Department implements Component {
        private String name;
        private List<Employee> employees;
    }
    
    public class Employee implements Component {
        private String name;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 因为上图实际上有2个基本结构:
    // Company-Department (1 对 n)、Department-Employee(1对n)
    class Composite implements Component {
    	private String name;
    	private List<Component> children;
    }
    
    // 这个便是Employee
    class Leaf implements Component {
    	private String name;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 所以,没有Get到这一点,就容易写出假的组合模式。(写的时候也会发现写的很累)

    三、实现(真的组合模式)

    1、案例来源的实现

    • 场景中题目的提供方,也给出了相应的实现:
    public interface Component {
        void display(int depth);
    }
    
    public class Department implements Component {
        private String name;
        private List<Component> children;
    
        public Department(String name) {
            this.name = name;
            this.children = new ArrayList<>();
        }
    
        public void add(Component component) {
            children.add(component);
        }
    
        @Override
        public void display(int depth) {
            StringBuilder indent = new StringBuilder();
            for (int i = 0; i < depth; i++) {
                indent.append("  ");
            }
    
            System.out.println(indent + name);
            for (Component component : children) {
                component.display(depth + 1);
            }
        }
    }
    
    public class Employee implements Component {
        private String name;
    
        public Employee(String name) {
            this.name = name;
        }
    
        @Override
        public void display(int depth) {
            StringBuilder indent = new StringBuilder();
            for (int i = 0; i < depth; i++) {
                indent.append("  ");
            }
            System.out.println(indent + "  " + name);
        }
    }
    
    public class Company {
        private String name;
        private Department root;
    
        public Company(String name) {
            this.name = name;
            this.root = new Department(name);
        }
    
        public void add(Component component) {
            root.add(component);
        }
    
        public void display() {
            System.out.println("Company Structure:");
            root.display(0); // 从 1 开始,以适配指定的缩进格式
        }
    
    
    public class Main {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
    // 读取公司名称
            String companyName = scanner.nextLine();
            Company company = new Company(companyName);
    // 读取部⻔和员工数量
            int n = scanner.nextInt();
            scanner.nextLine(); // 消耗换行符
    // 读取部⻔和员工信息
            for (int i = 0; i < n; i++) {
                String type = scanner.next();
                String name = scanner.nextLine().trim();
                if ("D".equals(type)) {
    
                    Department department = new Department(name);
                    company.add(department);
                } else if ("E".equals(type)) {
                    Employee employee = new Employee(name);
                    company.add(employee);
                }
            }
    // 输出公司组织结构
            company.display();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • debug会发现,这并没有正确表示company的结构:
      在这里插入图片描述
    • 那为啥能输出这样的结果呢?
    MyCompany
      HR
        HRManager
      Finance
        AccountantA
        AccountantB
      IT
        DeveloperA
        DeveloperB
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 原因在于:
    public class Employee implements Component {
        ...
    
        @Override
        public void display(int depth) {
            StringBuilder indent = new StringBuilder();
            for (int i = 0; i < depth; i++) {
                indent.append("  ");
            }
            System.out.println(indent + "  " + name); // 这里的depth实际上是1,因此作者多加了"  "
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    显然,这并不是正确的实现。

    2、我的实现

    public interface Component {
        void showName(int depth);
    }
    
    public class Composite implements Component {
        private String name;
        private List<Component> children;
    
        public Composite(String name) {
            this.name = name;
            children = new ArrayList<>();
        }
    
        public void addComponent(Component component) {
            children.add(component);
        }
    
        public Composite gotLastComposite() {
            if (!children.isEmpty()) {
                Component component = children.get(children.size() - 1);
                if (component instanceof Composite) {
                    return (Composite) component;
                } else {
                    throw new RuntimeException("last component is not composite");
                }
    
            } else {
                throw new RuntimeException("doesn't exist last component");
            }
        }
    
        @Override
        public void showName(int depth) {
            System.out.println(StringUtils.repeat("  ", depth) + name);
            for (Component child : children) {
                child.showName(depth + 1);
            }
        }
    }
    
    @AllArgsConstructor
    public class Employee implements Component {
        private String name;
    
        @Override
        public void showName(int depth) {
            System.out.println(StringUtils.repeat("  ", depth) + name);
        }
    }
    
    public class Company extends Composite {
    
        public Company(String name) {
            super(name);
        }
    
        @Override
        public void showName(int depth) {
            System.out.println("Company Structure:");
            super.showName(depth);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    public class Application {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            String companyName = scanner.nextLine();
            Company company = new Company(companyName);
    
            int n = scanner.nextInt();
            for (int i = 0; i < n; i++) {
                String type = scanner.next();
                String name = scanner.next();
    
                if (StringUtils.equals(type, "D")) {
                    company.addComponent(new Composite(name));
                } else if (StringUtils.equals(type, "E")) {
                    Composite department = company.gotLastComposite();
                    department.addComponent(new Employee(name));
                }
            }
    
            company.showName(0);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • Company的结构:
      在这里插入图片描述

    四、个人思考

    • 对真实场景建模后,类呈现树状结构,那么可以尝试使用组合模式设计代码:
    class Composite implements Component {
    	private String name;
    	private List<Component> children;
    }
    
    // 这个便是Employee
    class Leaf implements Component {
    	private String name;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    组合模式:实现接口A + 组合多个接口A

    • 装饰模式、桥接模式都用到了组合的理念,但都是一对一的形式:
    • 装饰模式:实现接口A + 组合接口A
    public class EncryptDataSourceImpl implements DataSource {
        private DataSource dataSource;
    }
    
    • 1
    • 2
    • 3
    • 桥接模式:实现接口A + 组合接口B
    public class Circle implements Shape {
        private Color color;
    }
    
    • 1
    • 2
    • 3
  • 相关阅读:
    Python dcm转jpg与jpg转dcm
    8.spring框架-----AOP基本知识
    介绍Node.js中fs模块 代码和注释。
    泰迪智能科技携手广西科技大学理学院共建“上进双创工作室”
    C++【智能指针】
    17-JavaSE基础巩固练习:Math类API两道数学算法水题
    跨境电商影响搜索排名的因素有哪些
    数字经济才是未来经济发展的核心
    大数据日志可视化分析(Hadoop+SparkSQL)
    弹出x-rite color assistant出现错误
  • 原文地址:https://blog.csdn.net/weixin_37477009/article/details/137892511