目录
- 一、场景
- 1、题目描述 【[案例来源](https://kamacoder.com/problempage.php?pid=1090)】
- 2、输入描述
- 3、输出描述
- 4、输入示例
- 5、输出示例
- 二、实现(假的组合模式)
- 1、代码
- 2、为什么上面的写法是假的组合模式?
- 三、实现(真的组合模式)
- 1、案例来源的实现
- 2、我的实现
- 四、个人思考
一、场景
设计模式很依赖场景,通过场景也能更好理解这种模式解决了什么问题。
- 无论是学校还是公司,都存在组织结构。而且,这种结构多半是树状结构。
- 当类与类之间的结构也需要表达成树状结构时,组合模式就来大显身手了。
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;@Setterprivate 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);}}@Nullablepublic Department gotLastDepartment() {if (CollectionUtils.isEmpty(departments)) {return null;} else {return departments.get(departments.size() - 1);}}@Overridepublic 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;}@Overridepublic 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;@Overridepublic String showName() {return name;}
}
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());}
}
2、为什么上面的写法是假的组合模式?
- 场景中的公司结构:
- 对这个结构进行建模,不应该是:
public class Company implements Component {private final String name;@Setterprivate List<Department> departments;
}public class Department implements Component {private String name;private List<Employee> employees;
}public class Employee implements Component {private String name;
}
- 因为上图实际上有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;
}
- 所以,没有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);}@Overridepublic 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;}@Overridepublic 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();}
}
- debug会发现,这并没有正确表示company的结构:
- 那为啥能输出这样的结果呢?
MyCompanyHRHRManagerFinanceAccountantAAccountantBITDeveloperADeveloperB
- 原因在于:
public class Employee implements Component {...@Overridepublic void display(int depth) {StringBuilder indent = new StringBuilder();for (int i = 0; i < depth; i++) {indent.append(" ");}System.out.println(indent + " " + name); // 这里的depth实际上是1,因此作者多加了" "}
}
显然,这并不是正确的实现。
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");}}@Overridepublic 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;@Overridepublic void showName(int depth) {System.out.println(StringUtils.repeat(" ", depth) + name);}
}public class Company extends Composite {public Company(String name) {super(name);}@Overridepublic void showName(int depth) {System.out.println("Company Structure:");super.showName(depth);}
}
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);}
}
- Company的结构:
四、个人思考
- 对真实场景建模后,类呈现树状结构,那么可以尝试使用组合模式设计代码:
class Composite implements Component {private String name;private List<Component> children;
}// 这个便是Employee
class Leaf implements Component {private String name;
}
组合模式:实现接口A + 组合多个接口A
- 装饰模式、桥接模式都用到了组合的理念,但都是一对一的形式:
- 装饰模式:实现接口A + 组合接口A
public class EncryptDataSourceImpl implements DataSource {private DataSource dataSource;
}
- 桥接模式:实现接口A + 组合接口B
public class Circle implements Shape {private Color color;
}