面向对象设计原则编程:开闭原则、接口隔离原则、合成复用原则和迪米特法则

本文将通过具体的代码示例来解释面向对象设计原则中的开闭原则、接口隔离原则、合成复用原则和迪米特法则。

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()用于显示文字报表。

CustomerDataDisplay接口

在实际使用过程中发现该接口很不灵活,例如:如果一个具体的数据显示类无须进行数据转换(源文件本身就是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 studentCouncilMembers;

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 studentCouncilMembers;

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 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录