一. UML 类图

UML(Unified Modeling Language),即统一建模语言,用于帮助系统开发人员阐明、设计和构建软件系统。

UML的这一套图被分为两组,一组叫结构性图,包括类图、组件图、部署图、对象图、包图、组合结构图、轮廓图。一组叫行为性图,包含用例图、流程图、状态机图、序列图、通信图、交互图、时序图。

类图是 “面向对象建模中” 最常用和最重要的图,主要用来展示系统中的类、接口以及它们之间的结构和关系的静态模型

1.1 类

类在类图中用矩形表示,分为 3 层,第 1 层展示类的名称,第 2 层展示类的字段(属性),第 3 层展示类的方法。

注意前面的符号:‘+’ 表示 public、‘-’ 表示 private、‘#’ 表示 protected

抽象类,类名和方法名用斜体表示

1.2 关系

1.2.1 泛化

泛化是一种继承关系,表示一般与特殊的关系,它指定了子类如何特化父类的的属性和方法

如:哺乳动物具有恒温、胎生等生理特征,猫是哺乳动物,也都具有这些特征,但除此之外,猫还会捉老鼠

1.2.2 实现

实现是一种类与接口的关系,表示类是接口属性和方法的实现

如:蝙蝠也是哺乳动物,它具有哺乳动物的一般特征,此外它还会飞,我们可以定义 “飞行” 的接口,让蝙蝠实现这个接口

1.2.3 组合

组合表示整体与部分的关系,但部分离开整体后无法单独存在

如:电脑由CPU、硬盘、内存组成,电脑与CPU、硬盘、内存是整体与部分的关系。但如果让CPU、硬盘、内存等组件单独存在,就无法工作

1.2.4 聚合

聚合也表示整体与部分的关系,但部分离开整体可以单独存在

如:一个公司有多个员工,但员工离开公司可以单独存在,离职了依然可以好好活着

1.2.5 关联

关联是一种拥有关系,它使一个类知道另一个类的属性和方法。关联可以是双向的,也可以是单向的。

如:一本书会有多个读者,一个读者也可能会有多本书,书和读者是一种双向的关系(多对多的关系);但一本书通常只会有一个作者,是一种单向的关系(一对一或一对多的关系,因为一个作者可能会写多本书)

1.2.6 依赖

依赖是一种使用的关系,即一个类的实现需要另一个类的协助,尽量不要使用双向的相互依赖

如:所有的动物都需要吃东西才能活,动物与食物就是一种依赖关系,动物依赖食物生存

二. 设计模式

2.1 设计模式的起源

设计模式是软件设计中常见问题的典型解决方案。 它们就像能根据需求进行调整的预制蓝图, 可用于解决代码中反复出现的设计问题。

模式是面向对象设计中常见问题的典型解决方案。 同样的解决方案在各种项目中得到了反复使用, 所以最终有人给它们起了名字, 并对其进行了详细描述。 这基本上就是模式被发现的历程了

模式的概念是由克里斯托佛·亚历山大在其著作《建筑模式语言》中首次提出的。 本书介绍了城市设计的 “语言”, 而此类 “语言” 的基本单元就是模式。 模式中可能会包含对窗户应该在多高、 一座建筑应该有多少层以及一片街区应该有多大面积的植被等信息的描述。

埃里希·伽玛、 约翰·弗利赛德斯、 拉尔夫·约翰逊和理查德·赫尔姆这四位作者接受了模式的概念。1994 年, 他们出版了《设计模式:可复用面向对象软件的基础》 一书,将设计模式的概念应用到程序开发领域中。 该书提供了 23 个模式来解决面向对象程序设计中的各种问题, 很快便成为了畅销书。 由于书名太长, 人们将其简称为 “四人组 (Gang of Four, GoF) 的书”, 并且很快进一步简化为 “GoF 的书”。

2.2 为什么学习设计模式

设计模式是针对软件设计中常见问题的工具箱, 其中的工具就是各种经过实践验证的解决方案。 即使你从未遇到过这些问题, 了解模式仍然非常有用, 因为它能指导你如何使用面向对象的设计原则来解决各种问题。

设计模式定义了一种让你和团队成员能够更高效沟通的通用语言。 你只需说 “哦, 这里用单例就可以了”, 所有人都会理解这条建议背后的想法。 只要知晓模式及其名称, 你就无需解释什么是单例。

重要的不是你将来会不会用到这些模式,而是通过这些模式让你找到“封装变化”,“对象间松散耦合”、“针对接口编程”的感觉,从而设计出易维护、易扩展、易复用、灵活性好的程序。

就像一个诗人可能不需要刻意按照某种模式去创作,但成为诗人前他们一定是认真地研究过成百上千的唐诗宋词、古今名句

2.3 设计模式分类

设计模式分为创建型模式、结构型模式、行为模式

创建型模式提供创建对象的机制,增加已有代码的灵活性和可复用性。包括工厂方法模式、抽象工厂模式、生成器模式、原型模式、单例模式。

结构型模式介绍如何将对象和类组装成较大的结构,并同时保持结构的灵活和高效。包括适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式、代理模式。

行为型模式负责对象间的高效沟通和职责委派。包括责任链模式、命令模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式。

三. 简单工厂模式

简单工厂模式的作用是创建一个工厂类来创建其它类的实例,至于类是怎样创建的对用户来说不可见

假设我们使用面向对象实现一个计算器,要求输入两个数和运算符号,得到结果

include <cstdio>                                                                                                                                       
#include <string>
#include <memory>

class Operation{
protected:
    double num_1;
    double num_2;

public:
    virtual ~Operation() = default;

public:
    void set(double num_1, double num_2){
        this->num_1 = num_1;
        this->num_2 = num_2;
        return;
    }

    virtual double result(void) const = 0;
};

class Add : public Operation{
public:
    double result(void) const override{
        return num_1 + num_2;
    }
};

class Sub : public Operation{
public:
    double result(void) const override{
        return num_1 - num_2;
    }
};

class FactoryOperation{
public:
    std::unique_ptr<Operation> create(const std::string &op){
        if(op == "+")
            return std::make_unique<Add>();
        else if(op == "-")
            return std::make_unique<Sub>();
        else
            return nullptr;
    }    
};

int main(void){
    FactoryOperation factory;
    std::shared_ptr<Operation> add = factory.create("+");
    add->set(1, 2);
    printf("Res: %lf\n", add->result());
    
    std::shared_ptr<Operation> sub = factory.create("-");
    sub->set(1, 2);
    printf("Res: %lf\n", sub->result());
    return 0;
}

简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。

四. 开放封闭原则

开放-封闭原则,是说软件实体(函数、类、模块等等)应该可以扩展,但是不可以修改

即对扩展开放,对更改封闭。

我们在做任何系统时,需求一定是会变化的,那么需求变化时,如何让设计的软件相对容易修改,不至于整个程序推倒重来,使得系统推出第一个版本以后不断推出新的版本。

上文中的简单工厂模式就违背了开放-封闭原则,为了添加其它运算类,我们不得不在工厂类中添加分支条件。为了解决这个问题,引入工厂方法模式。

开放-封闭原则是面向对象设计的核心所在,遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护、可扩展、可复用、灵活性好。我们应仅对程序中呈现出频繁变化的那些部分做出抽象,对程序中每个部分都刻意抽象并不是一个好主意。

作业

1. 求出函数 f 在范围 a 和 b 之间的定积分的近似值,可以用下面公式完成 $\int_{a}^{b}f=[f(a+\frac{dx}{2})+f(a+dx+\frac{dx}{2})+f(a+2dx+\frac{dx}{2})+\dots]dx$