[学习][笔记]设计模式(基于C/C++实现)之 设计基础(一)

简介: [学习][笔记]设计模式(基于C/C++实现)之 设计基础

前言

设计原则

单一职责原则(Single Responsibility Principle)

定义

不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。

问题由来

类 T 负责两个不同的职责:职责 P1,职责 P2。

当由于职责 P1 需求发生改变而需 要修改类 T 时,有可能会导致原本运行正常的职责 P2 功能发生故障。

解决方案

将类 T 分成两个不同的类来实现。比如 C 语言中会将头文件分类 string.h stdio.h, Qt 中 QLable QButton 等。C++中会有 string fstream 类,就是单一原则的体现。

开闭原则(Open Closed Principle)

定义

一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

问题由来

在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改 时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需 要原有代码经过重新测试。

解决方案

当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已 有的代码来实现变化。

例如:

man 可以做doAction,

不考虑开闭,就是man添加 jump,run,read方法。直接使用

但是这样每次都会修改 影响代码的维护。

class Man{
public:
  void jump();
  void read();
  void run();
}
void main(){
  Man *aman = new Man;
  man.jump();
  man.read();
  man.run();
}

遵循开闭原则的话,man就通过调用Ido的do()来实现动作 也不需要每个man把三个对象都调用一遍

class Ido{
public:
   virtual void do() = 0;
}
class Read :public Ido{
    void do(){
       //read();
    }
}
class Run:public Ido{
    void do(){
       //run();
    }
}
class Jump:public Ido{
    void do(){
       //jump();
    }
}
class Man{
public:
  void doAction(Ido *doptr){ doptr.do()}
}
void main(){
  Man *aman = new Man;
  Ido* it= new Read();
  man.doAction(it);
    Ido* it2= new Run();
  man.doAction(it2);
    Ido* it3= new Jump();
  man.doAction(it3);
}

依赖倒置原则(Dependence Inversion Principle)

定义

高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细 节应该依赖抽象。

问题由来

类 A 直接依赖类 B,假如要将类 A 改为依赖类 C,则必须通过修改类 A 的代码来达成。这种场景下,类 A 一般是高层模块,负责复杂的业务逻辑;类 B 和类 C 是低层模 块,负责基本的原子操作;假如修改类 A,会给程序带来不必要的风险。

解决办法

接口分离原则(Interface Segregation Principle)

定义

客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接 口上。

问题由来

类 A 通过接口 I 依赖类 B,类 C 通过接口 I 依赖类 D,如果接口 I 对于类 A 和类 B 来说不是最小接口,则类 B 和类 D 必须去实现他们不需要的方法。

解决办法

将臃肿的接口 I 拆分为独立的几个接口,类 A 和类 C 分别与他们需要的接口建立依 赖关系。也就是采用接口隔离原则。

迪米特法则(Law of Demeter)

定义

面向对象有一个概念是迪米特法则,其规则如下:

每个对象对其他对象的认识必须限制,只能与自己最近的对象

每个对象应当只和它的朋友联系,而不是陌生人

每个对象应当只和他的直接朋友联系。

问题由来

在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。

解决方案

迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度。

设计模式的门面模式(Facade)和中介模式(Mediator),都是迪米特法则应用的例子。

里氏替换原则(Liskov Substitution Principle)

定义

如果对每一个类型为 T1 的对象 o1,都有类型为 T2 的对象 o2,使得以 T1 定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那 么类型 T2 是类型 T1 的子类型。 所有引用基类的地方必须能透明地使用其子类的对象

问题由来

当使用继承时,遵循里氏替换原则。类 B 继承类 A 时,除添加新的方法完成新增 功能 P2 外,尽量不要 shadow(遮盖)父类 A 的方法。 继承作为面向对象三大特性之一,在给程序设计带来巨大便利的同时,也带来了弊 端。

比如使用继承会给程序带来侵入性,程序的可移植性降低,增加了对象间的耦合性, 如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且 父类修改后,所有涉及到子类的功能都有可能会产生故障。

优点:

1. 提高代码的重用性,子类拥有父类的方法和属性;

2. 提高代码的可扩展性,子类可形似于父类,但异于父类,保留自我的特性;

缺点:侵入性、不够灵活、高耦合

1. 继承是侵入性的,只要继承就必须拥有父类的所有方法和属性,在一定程度上约束了子类,降低了代码的灵活性;

2. 增加了耦合,当父类的常量、变量或者方法被修改了,需要考虑子类的修改,所以一旦父类有了变动,很可能会造成非常糟糕的结果,要重构大量的代码。

解决方案

里氏替换原则通俗的来讲就是: 子类可以扩展父类的功能,但不能改变父类原有的 功能。 它包含以下 4层含义:

1.子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。

2.子类中可以增加自己特有的方法。

3.当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。

4.当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

用我自己的话讲就是:

子类可以完全替换父类,而不影响程序编译;尽量不要重写父类。

UML图

设计视图

类的关系

类图

泛化

定义 是一种继承关系, 表示一般与特殊的关系, 它指定了子类如何特化父类的所有特征 和行为。

箭头指向 带三角箭头的实线,箭头指向父类。

类图关系:

实现

定义 是一种类与接口的关系,表示类是接口所有特征和行为的实现

箭头指向 带三角箭头的虚线,箭头指向接口。

类图关系



相关文章
|
14天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
26 2
|
7天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
24 9
|
7天前
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
|
6天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
18 2
|
16天前
|
设计模式 Java 开发者
Kotlin教程笔记(54) - 改良设计模式 - 迭代器模式
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。对于快速学习Kotlin的用户,推荐查看“简洁”系列教程。本文重点介绍迭代器模式,通过具体示例展示了如何在Kotlin中实现迭代器模式,包括使用Iterator、Iterable接口及重载iterator运算符的方法。
27 4
|
16天前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
本教程详细讲解Kotlin语法,适合深入学习。快速入门可参考“简洁”系列教程。本文通过游泳运动员的案例,介绍策略模式及其在Kotlin中的改良应用,利用高阶函数简化代码结构,提高灵活性。
27 3
|
17天前
|
设计模式 Java 开发者
Kotlin教程笔记(54) - 改良设计模式 - 迭代器模式
本教程详细讲解了Kotlin中的迭代器模式,包括如何通过实现Iterator和Iterable接口以及重载iterator运算符来实现可遍历的自定义集合。示例展示了如何创建一个图书集类,并通过不同方式使其支持遍历操作,适合希望深入了解Kotlin迭代器模式的开发者。
28 3
|
17天前
|
设计模式 监控 Java
Kotlin教程笔记(52) - 改良设计模式 - 观察者模式
本教程详细讲解Kotlin语法,适合深入学习。对于快速掌握Kotlin,推荐“简洁”系列教程。本文特别介绍了观察者模式,包括使用Java API和Kotlin委托属性(如Delegates.observable)实现的方法,旨在帮助开发者更高效地实现和优化观察者模式的应用。
30 3
|
17天前
|
设计模式 算法 Kotlin
Kotlin教程笔记(53) - 改良设计模式 - 策略模式
本教程详细讲解Kotlin语法,适合深入学习。快速入门可参考“简洁”系列教程。本文介绍策略模式在Kotlin中的应用,通过游泳运动员的例子,展示如何使用接口和高阶函数实现策略模式,使代码更简洁、灵活。
26 2
|
18天前
|
设计模式 Java Kotlin
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。对于快速学习Kotlin语法,推荐查看“简洁”系列教程。本文重点介绍了构建者模式在Kotlin中的应用与改良,包括如何使用具名可选参数简化复杂对象的创建过程,以及如何在初始化代码块中对参数进行约束和校验。
17 3