面向对象设计原则编程:开闭原则、接口隔离原则、合成复用原则和迪米特法则
面向对象设计原则编程:开闭原则、接口隔离原则、合成复用原则和迪米特法则
本文将通过具体的代码示例来解释面向对象设计原则中的开闭原则、接口隔离原则、合成复用原则和迪米特法则。
1. 开闭原则编程
问题:
在某CRM中,可以使用不同的方式显示图表,如饼状图和柱状图等。原始设计方案如图所示:

为了支持多种图表显示方式,在类ChartDisplay的方法display(String type)中存在如上图代码片段。现使用开闭原则对其进行重构,重构类图参考如下:

分析说明:
由于在ChartDisplay类的display()方法中针对每一个图表类编程,因此增加新的图表类不得不修改源代码。可以通过抽象化的方式对系统进行重构,增加新的图表类时无须修改源代码,满足开闭原则。
结论:
引入抽象图表类AbstractChart,且ChartDisplay针对抽象图表类进行编程,并通过setChart()方法由客户端来设计实例化的具体图表对象,在ChartDisplay的display()方法中调用chart对象的display()方法显示图表。如果需要增加一种新的图表,如折线图LineChart,只需将LineChart也作为AbstractChart的子类,在客户端中向ChartDisplay注入一个LineChart对象即可,无需修改现有类库的源代码。
2. 接口隔离原则编程
问题:
某软件公司开发人员针对CRM系统的客户数据显示模块设计了如图所示的CustomerDataDisplay接口,其中方法readData()用于从文件中读取数据,方法transformToXML()用于将数据转换成XML格式,方法createChart()用于创建图表,方法displayChart()用于显示图表,方法createReport()用于创建文字报表,方法displayReport()用于显示文字报表。

在实际使用过程中发现该接口很不灵活,例如:如果一个具体的数据显示类无须进行数据转换(源文件本身就是XML格式),但由于实现了该接口,将不得不实现其中声明的transformToXML()方法(至少需要提供一个空实现);如果需要创建和显示图表,除了需要实现与图表相关的方法外,还需要实现创建和显示文字报表的方法,否则程序在编译时将报错。现使用接口隔离原则对其进行重构,重构类图参考如下:

分析说明:
接口CustomerDataDisplay中定义的方法太多,其承担的职责太多,导致该接口的实现类庞大,在不同的实现类中都不得不实现接口中定义的所有方法,灵活性较差。出现大量空方法,导致系统中产生大量无用代码,影响代码质量。客户端针对大接口编程,在一定程度上破坏程序的封装性,客户端看到了不该看到的方法,没有为客户端定制接口。
结论:
因此将该接口按照接口隔离原则和单一职责原则进行重构,将其中一些方法封装在不同的小接口中,确保一个接口使用起来都比较方便,并都承担某一单一角色,每个接口中只包含一个客户端所需的方法即可。
3. 合成复用原则编程
问题:
某软件公司开发人员在初期的CRM系统设计中,考虑到客户数量不多,系统采用Access作为数据库,与数据库操作有关的类,例如CustomerDAO类等都需要连接数据库,连接数据库的方法getConnection()封装在DBUtil类中,由于需要重用DBUtil类的getConnection()方法,设计人员将CustomerDAO作为DBUtil类的子类,初始设计方案结构如图所示:

随着客户数量的增加,系统决定升级为Oracle数据库,因此需要增加一个新的OracleDBUtil类来连接Oracle数据库,由于在初始设计方案中CustomerDAO和DBUtil之间是继承关系,因此在更换数据库连接方式时需要修改CustomerDAO类的源代码,将CustomerDAO作为OracleDBUtil的子类,这将违背开闭原则。当然也可以直接修改DBUtil类的源代码,这同样也违背了开闭原则。以下使用合成复用原则对其进行重构,重构类图参考如下:

分析与说明:
根据合成复用原则,在实现复用时应该多用关联,少用继承。因此在重构时使用关联复用来取代继承复用。CustomerDAO和DBUtil之间的关第由继承变为关联,采用依赖注入的方式将DBUtil对象注入到CustomerDAO中,可以使用构造注入,也可以使用设值注入。如需对DBUtil的功能进行扩展,可以通过其子类来实现。
4. 迪米特法则编程
问题:
有校学生会成员和院学生会成员,现在需要编程打印所有学生会成。初始方案如图所示的结构:

以上方式解决了问题,但是却违反了迪米特法则。
SchoolManager中的printAllStudentCouncilMember()方法中以局部变量的方式引用了CollegeStudentCouncilMember类,所以在这里CollegeStudentCouncilMember不是SchoolManager的直接朋友。
解决方式:
将CollegeStudentCouncilMember变成SchoolManager的直接朋友,可以通过构造器传入、setter方法设置、参数传入等方式解决。
重构类图:

**说明:**类图和原来的差别不大,只是将院学生会成员的打印细节封装到了院管理者内部,对外只暴露出一个printCollegeStudentCouncilMembl()方法。
**代码示例:**java// 抽象学生会成员类public abstract class StudentCouncilMember { private String name; private int id;
// 构造方法、getter和setter方法省略
public abstract void print();}
// 校学生会成员类public class SchoolStudentCouncilMember extends StudentCouncilMember { @Override public void print() { System.out.println('Printing school student council member: ' + getName()); // 打印校学生会成员的具体信息 }}
// 院学生会成员类public class CollegeStudentCouncilMember extends StudentCouncilMember { @Override public void print() { System.out.println('Printing college student council member: ' + getName()); // 打印院学生会成员的具体信息 }}
// 学校管理者类public class SchoolManager { private List
public SchoolManager() { studentCouncilMembers = new ArrayList<>(); }
// 添加学生会成员 public void addStudentCouncilMember(StudentCouncilMember member) { studentCouncilMembers.add(member); }
// 打印所有学生会成员 public void printAllStudentCouncilMembers() { for (StudentCouncilMember member : studentCouncilMembers) { member.print(); } }}
// 院管理者类public class CollegeManager { private List
public CollegeManager() { studentCouncilMembers = new ArrayList<>(); }
// 添加学生会成员 public void addStudentCouncilMember(StudentCouncilMember member) { studentCouncilMembers.add(member); }
// 打印所有学院学生会成员 public void printCollegeStudentCouncilMembers() { for (StudentCouncilMember member : studentCouncilMembers) { member.print(); } }}
// 客户端代码public class Main { public static void main(String[] args) { SchoolManager schoolManager = new SchoolManager(); CollegeManager collegeManager = new CollegeManager();
// 添加校学生会成员 schoolManager.addStudentCouncilMember(new SchoolStudentCouncilMember('John', 1)); schoolManager.addStudentCouncilMember(new SchoolStudentCouncilMember('Emma', 2));
// 添加院学生会成员 collegeManager.addStudentCouncilMember(new CollegeStudentCouncilMember('Tom', 3)); collegeManager.addStudentCouncilMember(new CollegeStudentCouncilMember('Lucy', 4));
// 打印所有学生会成员 schoolManager.printAllStudentCouncilMembers(); collegeManager.printCollegeStudentCouncilMembers(); }}
通过上述代码的重构,我们将学院学生会成员的打印细节封装在了院管理者类内部,对外只暴露出一个printCollegeStudentCouncilMembers()方法。这样,SchoolManager类不再直接依赖于CollegeStudentCouncilMember,而是通过CollegeManager来间接操作。这符合迪米特法则,减少了类之间的耦合性,提高了代码的灵活性和可维护性
原文地址: https://www.cveoy.top/t/topic/PFt 著作权归作者所有。请勿转载和采集!