面向对象

1 特性

1.1 封装

封装是将对象的属性(数据)和方法(操作)包装在一起,并隐藏对象的内部实现细节,只暴露必要的接口给外部使用。

1.2 继承

继承是指一个类(子类)可以继承另一个类(父类)的属性和方法,从而实现代码复用和功能扩展。

1.3 多态

多态指相同的接口或方法调用,可以表现出不同的行为,具体行为取决于对象的实际类型。

2 SOLID 原则

2.1 开闭原则 OCP

定义:软件实体(类、模块、函数)应当对扩展开放,对修改关闭。

简单理解:不要修改已有代码,而是增加代码来扩展行为。

2.2 单一职责原则 SRP

定义:一个类只有一个引起他变化的原因。

简单理解:一个类或者模块应该仅做一件事,并且将这件事情做好。

典型代码实例如下:

 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
// 职责1: 只负责生成报告核心内容
public class ReportGenerator {
    private String data;

    public ReportGenerator(String data) {
        this.data = data;
    }

    public String generate() {
        // ... 核心生成逻辑 ...
        return reportContent;
    }
}

// 职责2: 只负责报告格式转换
public interface ReportFormatter {
    String format(String content);
}

public class HtmlFormatter implements ReportFormatter {
    @Override
    public String format(String content) {
        // ... HTML格式化逻辑 ...
        return formattedContent;
    }
}

public class PdfFormatter implements ReportFormatter {
    @Override
    public String format(String content) {
        // ... PDF格式化逻辑 (可能调用外部库) ...
        return formattedContent;
    }
}

// 职责3: 只负责报告持久化
public interface ReportRepository {
    void save(String content, String filename);
}

public class FileSystemRepository implements ReportRepository {
    @Override
    public void save(String content, String filename) {
        // ... 文件I/O操作 ...
    }
}

// 职责4: 只负责报告发送
public interface ReportSender {
    void send(String content, String recipient);
}

public class EmailReportSender implements ReportSender {
    @Override
    public void send(String content, String recipient) {
        // ... 邮件发送逻辑 ...
    }
}

// 高层模块/客户端代码 (组合各个职责)
public class ReportService {
    private ReportGenerator generator;
    private ReportFormatter formatter;
    private ReportRepository repository;
    private ReportSender sender;

    // 通过构造器或Setter注入依赖
    public ReportService(ReportGenerator generator, ReportFormatter formatter,
                         ReportRepository repository, ReportSender sender) {
        this.generator = generator;
        this.formatter = formatter;
        this.repository = repository;
        this.sender = sender;
    }

    public void createAndSendReport(String filename, String recipient, String formatType) {
        // 1. 生成内容
        String content = generator.generate();

        // 2. 格式化 (根据formatType选择Formatter, 这里简化)
        String formattedContent = formatter.format(content);

        // 3. 保存
        repository.save(formattedContent, filename);

        // 4. 发送
        sender.send(formattedContent, recipient);
    }
}

2.3 依赖倒置原则 DIP

定义:

  1. 高层模块不应该依赖于低层模块,两者都应该依赖于抽象。
  2. 抽象不应该依赖于细节,细节应该依赖于抽象。
 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
// 步骤1: 定义抽象接口 (高层和低层都将依赖它)
public interface DataRepository {
    void save(String data);
}

// 步骤2: 低层模块 - 具体实现1: MySQL
public class MySQLDatabase implements DataRepository {
    @Override
    public void save(String data) {
        // ... 具体的 MySQL 保存逻辑 ...
        System.out.println("Saving data to MySQL: " + data);
    }
}

// 步骤2: 低层模块 - 具体实现2: PostgreSQL (新增实现很容易)
public class PostgreSQLDatabase implements DataRepository {
    @Override
    public void save(String data) {
        // ... 具体的 PostgreSQL 保存逻辑 ...
        System.out.println("Saving data to PostgreSQL: " + data);
    }
}

// 步骤2: 低层模块 - 具体实现3: 文件系统 (新增实现很容易)
public class FileSystemRepository implements DataRepository {
    @Override
    public void save(String data) {
        // ... 具体的文件保存逻辑 ...
        System.out.println("Saving data to file: " + data);
    }
}

// 步骤3: 修改高层模块 - 只依赖抽象接口 DataRepository
public class ReportService {
    private DataRepository repository; // 依赖抽象!

    // 步骤5: 依赖注入 (通过构造函数)
    public ReportService(DataRepository repository) {
        this.repository = repository; // 接收注入的具体实现
    }

    public void generateReport(String reportData) {
        // ... 生成报告的复杂业务逻辑 (核心策略) 不变 ...
        repository.save(reportData); // 通过接口调用
    }
}

// 客户端使用 (负责组装对象,决定具体实现)
public class Client {
    public static void main(String[] args) {
        // 选择并创建具体的低层实现
        DataRepository mySqlRepo = new MySQLDatabase();
        // DataRepository pgRepo = new PostgreSQLDatabase();
        // DataRepository fsRepo = new FileSystemRepository();

        // 将依赖注入高层模块
        ReportService reportService = new ReportService(mySqlRepo); // 注入 MySQL
        // ReportService reportService = new ReportService(pgRepo); // 注入 PostgreSQL
        // ReportService reportService = new ReportService(fsRepo); // 注入文件系统

        reportService.generateReport("Important Report Data");
        // 输出取决于注入的具体实现:
        //  注入 MySQL: Saving data to MySQL: Important Report Data
        //  注入 PostgreSQL: Saving data to PostgreSQL: Important Report Data
        //  注入 FileSystem: Saving data to file: Important Report Data
    }
}

2.4 里氏替换原则

定义:子类型必须能够替换掉他们的父类型,程序的行为仍保持正确。

通俗理解:子类不能违背父类的语义和契约。

2.5 接口隔离原则

定义:客户端不应该被迫依赖它不使用的方法。

通俗理解:一个接口只包含调用者真正需要的方法。