站长信息
jeffery.xu
jeffery.xu

软件工程师

欢迎访问我的个人笔记网站!我是一名热爱技术的开发者,专注于Web开发和技术分享。

811495111@qq.com
18521510875
筛选

个人笔记

开闭原则(Open/Closed Principle, OCP)
设计模式
开闭原则(Open/Closed Principle, OCP)是面向对象设计的核心原则之一,由 Bertrand Meyer 提出。其核心思想是:软件实体(类、模块、函数等)应当对扩展开放,对修改关闭。这意味着在添加新功能时,应该通过扩展现有代码而非修改它来实现。

核心动机

  • 减少风险:修改现有代码可能引入新的错误,影响原有功能。
  • 提高可维护性:代码无需频繁修改,降低维护成本。
  • 促进复用:通过抽象和多态,可复用现有设计框架。

实现方式

  1. 抽象化:通过接口或抽象类定义稳定的契约。
  2. 多态:利用子类实现行为的扩展。
  3. 依赖注入:通过依赖倒置,使高层模块不依赖具体实现。

C# 示例:报表生成器

假设你正在开发一个报表生成器,初始需求是生成 PDF 报表。随着业务发展,需要支持 Excel 和 CSV 格式。
public class ReportGenerator
{
    public void GenerateReport(string format, ReportData data)
    {
        if (format == "PDF")
        {
            // 生成 PDF 报表的具体实现
            Console.WriteLine("生成 PDF 报表");
        }
        else if (format == "Excel")
        {
            // 生成 Excel 报表的具体实现
            Console.WriteLine("生成 Excel 报表");
        }
        // 问题:每次新增格式都需要修改此方法
    }
}

遵循开闭原则的重构

通过抽象化和多态,将报表生成逻辑封装到独立的类中:
// 定义报表生成器接口(抽象)
public interface IReportGenerator
{
    void Generate(ReportData data);
}

// 具体实现:PDF 报表生成器
public class PdfReportGenerator : IReportGenerator
{
    public void Generate(ReportData data)
    {
        Console.WriteLine("生成 PDF 报表");
    }
}

// 具体实现:Excel 报表生成器
public class ExcelReportGenerator : IReportGenerator
{
    public void Generate(ReportData data)
    {
        Console.WriteLine("生成 Excel 报表");
    }
}

// 新增格式:CSV 报表生成器(无需修改原有代码)
public class CsvReportGenerator : IReportGenerator
{
    public void Generate(ReportData data)
    {
        Console.WriteLine("生成 CSV 报表");
    }
}

// 报表服务:依赖抽象接口
public class ReportService
{
    private readonly IReportGenerator _generator;

    public ReportService(IReportGenerator generator)
    {
        _generator = generator; // 通过构造函数注入依赖
    }

    public void CreateReport(ReportData data)
    {
        _generator.Generate(data);
    }
}

// 使用示例
public class Program
{
    public static void Main()
    {
        var data = new ReportData();
        
        // 需要 PDF 报表时
        var pdfService = new ReportService(new PdfReportGenerator());
        pdfService.CreateReport(data);

        // 需要 Excel 报表时
        var excelService = new ReportService(new ExcelReportGenerator());
        excelService.CreateReport(data);

        // 需要 CSV 报表时(扩展无需修改原有代码)
        var csvService = new ReportService(new CsvReportGenerator());
        csvService.CreateReport(data);
    }
}

关键改进点

  1. 抽象化:通过 IReportGenerator 接口定义稳定的报表生成契约。
  2. 多态:每个报表格式(PDF/Excel/CSV)都实现该接口,行为由具体子类决定。
  3. 依赖注入ReportService 依赖接口而非具体实现,支持动态切换报表生成器。

新增需求:添加 HTML 报表

若需新增 HTML 报表,只需:
  1. 创建 HtmlReportGenerator 类实现 IReportGenerator
  2. 在调用处注入新的生成器,无需修改现有类。
  3. public class HtmlReportGenerator : IReportGenerator
    {
        public void Generate(ReportData data)
        {
            Console.WriteLine("生成 HTML 报表");
        }
    }
    
    // 使用时直接注入新实现
    var htmlService = new ReportService(new HtmlReportGenerator());
    htmlService.CreateReport(data);

    开闭原则的其他应用场景

    1. 插件系统:通过接口定义插件规范,新插件只需实现接口。
    2. 策略模式:将算法封装为策略类,运行时动态切换。
    3. 事件驱动架构:通过事件和监听器实现功能扩展。

    注意事项

    • 过度抽象风险:不要为未来可能的需求过度设计,遵循 YAGNI(You Aren't Gonna Need It)原则。
    • 平衡点:对可能变化的部分应用 OCP,对稳定部分无需过度抽象。
    通过开闭原则,代码可以优雅地应对变化,同时保持稳定性和可维护性。
单例模式
软考

单例模式是一种对象创建型模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。GoF对单例模式的定义是:保证一个类、只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。

为什么使用单例模式

在应用系统开发中,我们常常有以下需求:

-在多个线程之间,比如初始化一次socket资源;比如servlet环境,共享同一个资源或者操作同一个对象

-在整个程序空间使用全局变量,共享资源

-大规模系统中,为了性能的考虑,需要节省对象的创建时间等等。

因为Singleton模式可以保证为一个类只生成唯一的实例对象,所以这些情况,Singleton模式就派上用场了。

实现单例步骤常用步骤

a)构造函数私有化

b)提供一个全局的静态方法(全局访问点)

c)在类中定义一个静态指针,指向本类的变量的静态变量指针

单一职责原则
软考

修改一个类的原因只能有一个。

      这条原则的主要目的是减少复杂度。 你不需要费尽心机地去构思如何仅用 200 行代码来实现复杂设计, 实际上完全可以使用十几个清晰的方法。
      当程序规模不断扩大、 变更不断增加后, 真实问题才会逐渐显现出来。 到了某个时候, 类会变得过于庞大, 以至于你无法记住其细节。 查找代码将变得非常缓慢, 你必须浏览整个类, 甚至整个程序才能找到需要的东西。 程序中实体的数量会让你的大脑堆栈过载,你会感觉自己对代码失去了控制。
      还有一点: 如果类负责的东西太多, 那么当其中任何一件事发生改变时, 你都必须对类进行修改。 而在进行修改时, 你就有可能改动类中自己并不希望改动的部分。如果你开始感觉在同时关注程序特定方面的内容时有些困难
的话, 请回忆单一职责原则并考虑现在是否应将某些类分割为几个部分。

 

操作系统-1
软考

计算机组成与体系结构-1
软考

1、进制转换
(1)其他进制转十进制:按权展开法。以数码*位权形式求和即可。
(2)十进制转其他进制:短除法/除基取余法。以数值除基数,余数从下向上记录即可。
(3)十进制转二进制:凑位权法或减法。所需位权位置记作1,无需位权位置记作0,即为二进制结果。
(4)二进制转十六进制互转:分组法。4位二进制1组,转为十六进制数码即可。
(5)十六进制转二进制:逆分组法。每位数码转为4位二进制即可。
2、码制
(1)原码/反码/补码/移码转换规则:
原码:最高位是符号位,其余低位表示数值的绝对值(0表示正数,1表示负数)
反码:正数的反码与原码相同,负数的反码是其绝对值按位取反(符号位不变)
补码:正数的补码与原码相同,负数的补码是其反码末位补1(符号位不变)
移码:补码的符号位取反
(2)n位二进制表示范围

3、浮点数的表示
(1)表示格式:N=尾数*基数指数

(2)特点:
一般尾数用补码,阶码用移码
阶码的位数决定数的表示范围,位数越多范围越大
尾数的位数决定数的有效精度,位数越多精度越高
对阶时,小数向大数看齐
对阶是通过较小数的尾数右移实现的
(3)表示范围:结合码制判断对应二进制长度的表示范围即可。

4、逻辑运算符
(1)关系运算符及其优先次序

计算机组成与体系结构-2
软考

1、校验码

2、计算机组成
(1)CPU主要由运算器、控制器、寄存器组和内部总线等部件组成。
(2)运算器
①算术逻辑单元ALU:数据的算术运算和逻辑运算
②累加寄存器AC:通用寄存器,为ALU提供一个工作区,用在暂存数据
③数据缓冲寄存器DR:写内存时,暂存指令或数据
④状态条件寄存器PSW:存状态标志与控制标志
(争议:也有将其归为控制器的)
(3)控制器
①程序计数器PC:存储下一条要执行指令的地址
②指令寄存器IR:存储即将执行的指令
③指令译码器D:对指令中的操作码字段进行分析解释
④地址寄存器AR:保存当前CPU访问内存单元的地址
⑤时序部件:提供时序控制信号
3、存储系统
(1)层次化存储结构

(2)存储器分类
存储器位置:
内存&外存
存取方式:
①按内容存取:相联存储器(如Cache)。
②按地址存取:随机存取存储器(如内存);顺序存取存储器(如磁带);直接存取存储器(如磁盘)。
工作方式:随机存取存储器RAM(如内存DRAM);只读存储器ROM(如BIOS)。
(3)Cache
在计算机的存储系统体系中,Cache是访问速度最快的层次(若有寄存器,则寄存器最快)。
②使用Cache改善系统性能的依据是程序的局部性原理。
地址相连映像
直接相联映像:硬件电路较简单,但冲突率很高。
全相联映像:电路难于设计和实现,只适用于小容量的cache,冲突率较低。
组相联映像:直接相联与全相联的折中。
注:主存与Cache之间的地址映射由硬件直接完成。
(4)主存编址计算
存储单元:存储单元个数=最大地址-最小地址+1
编址内容
按字编址:存储体的存储单元是字存储单元,即最小寻址单位是一个字。
按字节编址:存储体的存储单元是字节存储单元,即最小寻址单位是一个字节。
③总容量=存储单元个数*编址内容
④总片数=总容量/侮片的容量

计算机组成与体系结构-3
软考

2、总线
(1)并行&串行总线
并行总线:是含有多条双向数据线的总线,它可以实现一个数据的多位同时传输,总线中数据线的数量决定了可传输一个数据的最大位数(一般为8的倍数)。由于可以同时传输数据的各位,所以并行总线具有数据传输率高的有点;但是由于各条数据线的传输特性不可能完全一致,当数据线较长时,数据各位到达接收端时的延迟可能不一致,会造成传输错误,所以并行总线不宜过长【成本较高】,适合近距离传输。大多数总线属于并行总线。
串行总线:只含有一条双向数据线或两条单向数据线的总线,可以实现一个数据的各位按照一定的速度和顺序依次传输。由于按位串行传输数据对数据线传输特性的要求不高,在长距离连线情况下仍然可以有效地传送数据,所以串行总线的优势在于远距离通信【成本较低】。但由于数据是按位顺序传送的,所以在相同的时钟控制下,数据传输速率低于并行总线。大多数的通信总线属于串行总线。
(2)数据总线/地址总线/控制总线

3、寻址方式
(1)立即寻址方式
特点:操作数直接在指令中,速度快,灵活性差
(2)直接寻址方式
特点:指令中存放的是操作数的地址
(3)间接寻址方式
特点:指令中存放了一个地址,这个地址对应的内容是操作数的地址。
(4)寄存器寻址方式
特点:寄存器存放操作数
(5)寄存器间接寻址方式
特点:寄存器内存放的是操作数的地址
4、CISC与RISC

5、流水线技术
1条指令执行时间+(指令条数-1)*流水线周期
理论公式:(t1+t2+..+tk)+(n-1)*tmax
实践公式:k*tmax+(n-1)*tmax
计算流水线吞吐率的最基本的公式如下:

6、可靠性
(1)指标
可靠性可以用MTTF/(1+MTTF)来度量。
可用性可以用MTBF/(I+MTBF)来度量。
可维护性可以用1/(1+MTTR)来度量。
(2)可靠性计算模型
串联:R=R1*R2*...*Rn
并联:R=1-(1-R1)*(1-R2)*...*(1-Rn)
(3)参数计算
【失效率】
比如:假设统一型号的1000台计算机,在规定的条件下工作1000小时,其中10台故障。
其失效率λ=10/(1000*1000)=1*10-5
【千小时可靠性】R(t)=1-t*λ=1-1000*(1*10-5)=1-0.01=0.99
【平均故障间隔时间】MTBF=1/λ=105小时