在面向对象编程中,继承关系是一种重要的关系类型,它允许一个类(子类)继承另一个类(父类)的属性和方法。然而,随着软件系统的复杂性不断增加,传统的继承关系可能会导致代码的臃肿和耦合度的增加。为了解决这一问题,我们可以使用组合关系来取代继承关系,将对象的功能模块化,并通过接口定义角色行为,从而实现更加灵活和可维护的设计。本文将探讨面向对象编程中继承关系与组合关系的区别,以及如何合理运用组合关系来优化设计,提高代码的质量和可维护性。
//面向对象(精髓)变继承关系为组和关系 public Employee(String name, int salary) {this.name = name;this.salary = salary;}public void doWork() {
// 更具需求实现功能}....省略...
public class Manager extends Employee {private final List<Employee> reporters;//下属public void doWork() {
// 项目领导工作给下属分配需求工作}Employee employee1 = new Employee("John", 10000);Employee employee2 = new Employee("Mary", 20000);Employee employee3 = new Employee("John", 10000);//employee2 升级成领导Manager 工资增长5w 并管理其他下属
//打印员工晋升为领导前后的工作内容。
思考如果莫员工(employee2 )升级成领导Manager 怎么优化设计
不推荐写法(继承关系)
public class Employee {private String name;private int salary;// 构造函数public Employee(String name, int salary) {this.name = name;this.salary = salary;}// 工作方法public void doWork() {// 员工的工作逻辑}
}public class Manager extends Employee {// 管理员属性在这里private List<Employee> reporters;// 管理员的工作方法@Overridepublic void doWork() {// 项目领导的工作逻辑}// 获取下属public List<Employee> getReporters() {return reporters;}
}
在不推荐的继承关系写法中,Manager
类继承自 Employee
类。虽然从某种程度上看,Manager
是一种特殊的 Employee
,但这种继承关系存在一些缺点:
-
僵化的层次结构:使用继承关系会将
Manager
和Employee
类紧密耦合在一起,形成了僵化的层次结构。如果未来需要引入其他类型的员工,如实习生或兼职员工,就需要修改现有的继承结构,导致系统的扩展性变差。 -
不符合"IS-A"关系:继承关系应该满足 “IS-A” 关系,即子类对象可以替代父类对象使用。但
Manager
和Employee
之间的关系不是严格的 “IS-A” 关系。Manager
是一种特殊的员工,但并不是所有员工都是经理。 -
职责混淆:
Manager
类继承了Employee
类中的一些属性和方法,如name
和salary
,但与之相应的doWork()
方法的实现完全不同。这导致了职责的混淆,使得代码难以理解和维护。 -
困难的扩展:在继承关系中,子类继承了父类的全部行为,包括一些可能并不适用于子类的行为。这样就限制了子类的灵活性和可扩展性,使得后续对于子类的修改和扩展变得困难。
综上所述,尽管继承关系在某些情况下很有用,但在这个场景下使用继承关系存在着上述的缺点,因此不推荐使用继承关系来表示 Manager
和 Employee
之间的关系。
推荐写法(组合关系)
// 角色接口
interface Role {void doWork();
}// 工程师角色
class Engineer implements Role {@Overridepublic void doWork() {// 工程师的工作逻辑}
}// 经理角色
class Manager implements Role {private List<Employee> reporters;// 构造函数public Manager(List<Employee> reporters) {this.reporters = reporters;}@Overridepublic void doWork() {// 项目领导的工作逻辑}// 获取下属public List<Employee> getReporters() {return reporters;}
}public class Employee {private String name;private int salary;private Role role; // 当前角色职位// 构造函数public Employee(String name, int salary, Role role) {this.name = name;this.salary = salary;this.role = role;}// 获取当前角色public Role getRole() {return role;}// 执行工作public void doWork() {role.doWork();}}
class Tester{public static void main(String[] args) {Employee employee1 = new Employee("John", 10000);Employee employee2 = new Employee("Mary", 20000);Employee employee3 = new Employee("John", 10000);employee2.doWork();//output 更具需求实现功能...employee2.setRole(new Manager(Arrays.asList(employee1,employee3)));employee2.doWork();//output 项目领导工作给下属分配需求工作...}
}
推荐的组合关系写法采用了 State 模式,相比不推荐的继承关系写法,具有以下优点:
-
灵活性和可扩展性:组合关系将角色和功能分离开来,使得系统更加灵活和可扩展。在组合关系中,
Employee
类可以根据需要动态地切换不同的角色,而不需要修改现有的代码结构。这样一来,系统可以更加容易地适应需求的变化,保持良好的扩展性。 -
降低耦合度:组合关系降低了对象之间的耦合度,使得系统更加灵活和可维护。在组合关系中,
Employee
类与具体的角色实现类之间仅通过接口进行交互,彼此之间相互独立。这样一来,系统的各个部分之间的依赖关系更加清晰,代码的耦合度更低,使得系统更加易于理解和维护。 -
符合"HAS-A"关系:组合关系符合 “HAS-A” 关系,即一个对象包含另一个对象作为其组成部分。在组合关系中,
Employee
类包含了一个Role
类的引用,表示员工拥有某种角色。这种关系更加贴近现实世界的对象之间的关系,使得系统的设计更加自然和直观。 -
清晰的责任分配:组合关系将不同的职责分配到了不同的类中,使得每个类都专注于自己的核心职责。在组合关系中,
Employee
类负责管理员工的基本信息,而Role
接口负责定义员工的角色和行为。这样一来,每个类的职责更加清晰明确,代码更加易于理解和维护。
综上所述,推荐的组合关系写法采用了 State 模式,相比不推荐的继承关系写法,具有更高的灵活性、可扩展性、低耦合度和清晰的责任分配。因此,在设计类和对象时,应该优先考虑使用组合关系,以提高代码的质量和可维护性。
总结
在面向对象设计中,推荐使用组合关系而不是继承关系。组合关系将对象的功能和角色分离,使得对象更加灵活和可复用。通过组合关系,可以将不同的角色和功能组合起来,从而实现更加复杂的行为。同时,组合关系也降低了对象之间的耦合度,使得系统更加易于维护和扩展。因此,在设计类和对象时,应该优先考虑使用组合关系,从而提高代码的质量和可维护性。
扩展阅读
面向对象主题 | 链接 |
---|---|
类与对象 | 链接 |
接口与抽象类 | 链接 |
不可变性 | 链接 |
变继承为组合(精髓) | 链接 |
完整代码示例
// 定义状态接口
interface Role {void doWork();
}// 具体状态:普通员工
class Engineer implements Role {@Overridepublic void doWork() {System.out.println("更具需求实现功能...");}
}// 具体状态:经理
class Manager implements Role {private List<Employee> reporters;public Manager(List<Employee> reporters) {this.reporters = reporters;}@Overridepublic void doWork() {System.out.println("项目领导工作给下属分配需求工作...");}
}
@Data
@AllArgsConstructor
// 上下文类:员工class Employee {private String name;private int salary;private Role role; // 当前角色状态public Employee(String name, int salary) {this.name = name;this.salary = salary;this.role = new Engineer(); // 默认状态为普通员工}// 设置角色状态public void setRole(Role role) {this.role = role;}// 执行工作public void doWork() {role.doWork();}}class Tester{public static void main(String[] args) {Employee employee1 = new Employee("John", 10000);Employee employee2 = new Employee("Mary", 20000);Employee employee3 = new Employee("John", 10000);employee2.doWork();//output 更具需求实现功能...employee2.setRole(new Manager(Arrays.asList(employee1,employee3)));employee2.doWork();//output 项目领导工作给下属分配需求工作...}
}