设计模式学习
Christopher Alexander将设计模式描述为一种经过验证的、可重复使用的解决特定设计问题的解决方案。
抽象思维:向上,如何将我们的周围
世界抽象为程序代码
面向对象
组件封装
设计模式
**底层思维:**向下,如何把握机器底层
从微观理解对象构造
语言构造
编译转换
·内存模型
运行时机制
建筑商从来不会去想给一栋已建好的100层高的楼房底下再新修一个小地下室-这样做花费
极大而且注定要失败。然而令人惊奇的是,软件
-Object-Oriented Analysis and Design
with Applications
分装
保护私有变量不被外部直接访问,受到保护
继承
子类可以继承父类public 函数和变量
父类的私有变量和函数在子类不能被使用
多态
同名函数具有不同的状态
发生继承 父类需要有虚函数 子类继承父函数 重写虚函数
通过父类指针/引用 调用子类重写实现的虚函数
**纯虚函数:**子类必须重新实现,父类中纯虚函数只有一个空壳定义
**抽象类:**类中定义有纯虚函数,不能直接进行实例化,子类实现继承的时候,必须实例化定义纯虚函数
UML(统一建模语言)
用一种语言,把类和类之间的关系表示出来
类:封装了数据和行为,是面向对象的重要组成部分,它是具有相同 属性,操作,关系 的对象集合的总称。
类和类之间的关系包括:
继承
关联
聚合
组合
依赖
可以看到该图分为上中下三部分:上层是类名,中间层是展性(类的成员交量),下层是方法(类的成员函数)
可见性 : +表示 public、"#"表示 protected、- 表示 private、_(下划线) 表示 static
属性的表示方式:【可见性】【属性名称】:【类型】=(缺省值,可选)
方法的表示方式:【可见性】【方法名称】(【参数名:参数类型,一】):【返回值类型】
如果我们定义的类是一个 抽象类(类中有纯虚函数),在画UML类图的时候,其名称需要使用斜休显示。
聚合关系(Aggregation)
聚合关系表示一个类(整体)包含另一个类(部分),但部分对象可以独立于整体对象存在。聚合是一种弱“拥有”关系,部分对象的生命周期不依赖于整体对象的生命周期。 UML中用带空心的菱形
组合关系(Composition)
组合关系表示一个类(整体)严格地包含另一个类(部分),并且部分对象的生命周期完全依赖于整体对象。组合是一种强“拥有”关系,整体对象被销毁时,部分对象也会随之销毁。 UML中用带实心的菱形
依赖关系是面向对象编程中的一种关系,用于描述一个类使用另一个类的情况。它表示一个类的变化可能会影响另一个类的行为或功能。这种关系通常是最弱的类之间的关系。
特点:
临时性:依赖关系通常是短暂的,一个类只是暂时地使用另一个类,并不保存对它的长期引用。
方向性:依赖关系是单向的,即一个类依赖于另一个类,而被依赖的类并不需要知道依赖它的类。
易变性:由于依赖关系是弱关系,所以当依赖的类发生变化时,依赖它的类可能需要更新代码来适应这些变化。
表示法:
在 UML 图中,依赖关系用虚线带箭头表示,箭头指向被依赖的类。
继承、组合、聚合、关联和依赖五种类与类之间关系的总结表格:
关系类型 | 描述 | 强度 | 生命周期 | UML 表示法 | 示例 |
---|---|---|---|---|---|
继承 | 一个类从另一个类继承属性和行为,实现代码重用 | 很强 | 子类随父类存在 | 实线三角形箭头 | Bird类继承自Animal类,Bird类获得了Animal类的属性和方法 |
组合 | 一个类完全拥有另一个类,且后者依赖于前者 | 很强 | 依赖 | 实心菱形 | Car类组合了Engine类,汽车销毁时引擎也随之销毁 |
聚合 | 一个类包含另一个类,但后者可以独立存在 | 较强 | 独立 | 空心菱形 | Team类聚合了Player类,球队解散,球员仍然存在 |
关联 | 一个类知道另一个类,并且可以与之交互 | 中 | 独立 | 实线箭头 | Teacher类关联了Student类,教师知道其学生的信息 |
依赖 | 一个类使用另一个类的功能或资源 | 弱 | 临时 | 虚线箭头 | Printer类依赖Document类来打印文件,但只在打印时需要 |
解释
- 继承:父类与子类之间的关系,子类继承父类的属性和方法,形成“是一个”的关系。
- 组合:整体与部分的关系,部分依赖于整体而存在,整体销毁时,部分也会被销毁。
- 聚合:整体与部分的关系,部分可以独立于整体而存在,整体是部分的拥有者。
- 关联:两个类之间的关系,它们可以长期存在,并且通常有明确的联系。
- 依赖:两个类之间是临时的、弱关系,一个类在方法或操作中使用另一个类。
设计类的原则:
单一职责原则:让类的功能尽可能单一
开放封闭原则:
开放:类的外部是开放的,已经定义好的类以外的空间
封闭:类自己是专一原则 ,封闭已经编写好的类
依赖倒转原则:
1:高层模块不应该依赖低层模块,两个都应该依赖抽象
2:抽象不应该依赖细节,细节应该依赖抽象
细节是通过多态在子类重写父类虚函数的时候 实现的
里氏代换原则
:子类类型必须能够替换掉他们的父类类型
父类定义的所用变量和方法,在子类实现的过程中完全适用,全部能够实现和替换掉
依赖转换原则:
1:抽象类中提供的接口是固定不变的
2:低层模块是抽象类的子类,子类继承了抽象类的接口,并且可以重写这些接口
3:高层模块想要实现某些功能,调用的是抽象类中的函数接口,并且是通过抽象类中的父类资政引用其子类的实例对象
三种设计原则:
设计原则 | 定义 | 目的 | 示例 |
---|---|---|---|
单一职责原则(SRP) | 一个类应该只有一个引起它变化的原因,类只负责一个功能领域中的职责 | 降低类的复杂性,使其更易于理解、维护和扩展 | 将订单处理功能与通知功能分离到不同的类中:Order类负责订单处理,Notification类负责通知 |
开闭原则(OCP) | 软件实体应该对扩展开放,对修改关闭 | 通过扩展已有代码增加新功能,而不修改已有代码,减少引入错误的风险,提升系统的可维护性和扩展性 | 在已有Shape类的基础上,继承并扩展出新的Triangle类,而不修改Shape类的代码 |
依赖倒置原则(DIP) | 1.高层模块不依赖于低层模块,两者都依赖于抽象 2:抽象不应该依赖细节,细节应该依赖抽象 | 降低模块之间的耦合,使系统更加灵活、易于扩展和测试 | PaymentProcessor类依赖于PaymentMethod接口,而非具体支付方式类(如CreditCardPayment、PaypalPayment) |
解释
- 单一职责原则(SRP):通过让类只承担一个职责,简化类结构,降低变更引发的风险。
- 开闭原则(OCP):通过扩展新功能,避免修改已有代码,从而减少引入新问题的可能性。
- 依赖倒置原则(DIP):通过依赖接口或抽象类,而不是具体实现类,增强系统的灵活性和可维护性。
模式的定义: 核心:稳定中有变化
定义一系列算法,把它们一个个封装起来,并且使它们可相互替换(变化),该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。
设计模式的核心:在稳定和变化中,寻找隔离点,来分离他们
**代码复用性 :**是二进制层面的代码复用,而非源代码拷贝粘贴
软件建模体现了软件设计的思想,在需求和实现之间架起了一座桥梁,通过模型指导软件系统的具体实现。
模型并不是软件系统的一个完备表示,而是所研究的系统的一种抽象。
对一张图的理解与执行 远大于 对一大段话的理解与执行
4+1视图是一组相关联模型的集合,从不同的视角,反映不同利益干系人的关注点:
逻辑视图:描述系统的业务上下文、系统的逻辑分解,以及分解出的逻辑元素间的关系;
实现视图:描述系统代码结构,构建结构;
行为视图:描述系统启动过程、运行期交互;
部署视图:描述系统的交付、安装、部署;
用例视图:以用例作为驱动元素,驱动和验证其他四个视图的设计;
软件建模体现了软件设计的思想,在需求和实现之间架起了一座桥梁,通过模型指导软件系统的具体实现。
模型并不是软件系统的一个完备表示,而是所研究的系统的一种抽象。
软件重构
16字真言:
旧的不变 新的创建
一步切换 旧的再见
小步修改,及时反馈
重构不改变代码的功能,以最小的原子操作进行重构
在重构的步骤中,最好不要定位问题,遇到问题直接回退
重构的方法:
提炼函数:
动机:提取功能,将部分代码提取到独立函数中,方便维护和复用
提升可读性:提炼到独立函数并命令,代码自注释
内联函数:
动机:消除多余的间接性,将函数和被调用的代码合并,方便理解
重新组织函数,将多个函数代码内联到一个函数中,方便后续重构
将查询函数和修改函数相分离
一个函数内只是给一个返回值,而没有任何副作用,这个函数就是查询函数
我们希望有副作用的函数直接调用 分离出来的查询函数
拆分变量:
动机:如果变量被多次赋值,且赋值意义不同,意味在函数中承担了多个职责,容易造成理解困难
提炼类:
动机: