设计模式

设计模式

1、概述

1.1、设计模式的发展

模式起源于建筑业,模式之父—Christopher Alexander,亚历山大是当代举世闻名的建筑大师,他1977年出版的《建筑模式语言》包含了253哥建筑喝城市规划模式,对建筑业和计算机科学领域钟的设计模式产生了巨大的影响。

在软件业中的模式最开始是由四人帮(gang of four)提出的,他们在1994年发表了23种在软件开发钟使用频率较高的设计模式。这都是基于7个面向对象设计原则

在1995年出版《Design Patterns》一书,在计算机领域首次提出设计模式的概念

模式:在特定环境下,解决某类重复出现问题的一套可行有效的解决方案

模式的作用:模式是一种指导性的方针,在可行有效的既定方案下找寻解决现有问题的最佳办法 ,可以事半功倍

每个领域成熟之后都会有相应的模式

//运用设计模式有助于统一团队的设计习惯,使用更加优秀简洁的设计模式使得新手更加轻松上手

1.2、设计模式的概念

1.3、软件模式

软件模式由四个部分组成:

  • 问题描述

  • 前提条件(环境或约束条件)

  • 解法

  • 效果

如何称为软件模式:

​ 软件模式与具体的应用领域无关,在模式发现过程中需要遵循三大律,即只有经过三个以上不同类型(或不同领域)的系统的校验,一个解决方案才能从候选模式升格为模式

1.4、GRASP设计原则

​ GRASP,全称General Responsibility Assignment Software Pattern,即通用职责分配软件模式,它由《UML和模式应用》一书作者Craig Larman提出

Grasp称为设计原则而不是设计模式,是因为它站在面向对象设计的角度,告诉我们怎样设计问题空间中的类与分配他们的行为职责,以及明确类之间的相互关系。

GoF模式是针对特定问题而提出的解决方案

GRASP站在更高的角度看待设计,它是GoF设计模式的基础

Grasp描述了对象设计和职责分配的基本原则,其核心思想是职责分配(responsibility Assignment),用职责设计对象(Designing Objects with responsibilities)。它包含9个基本原则

  1. 创建者

  2. 信息专家

  3. 低耦合

  4. 高内聚

  5. 控制器

  6. 多态性

  7. 纯虚构

  8. 间接性

  9. 防止变异

1.5、GoF设计模式

设计模式:在特定环境下为解决某一通用软件设计问题提供的一套定制的解决方案,该方案描述了对象和类之间的互相作用

design pattern are descriptions of communicating objects and classes that are customized to solve a general design problem in a particular context

设计模式(Design Pattern)含义

  1. 一套被反复使用、多数人知晓、经过分类编目的、代码设计经验的总结

  2. 是一种用于对软件系统中不断重现的设计问题的解决方案进行文档化的技术

  3. 是一种共享专家设计经验的技术

1.6、设计模式的分类

  • 创建者模式

    用于描述”怎样创建对象“,它的主要特点是”将对象的创建与使用分离“。GoF书中提供了单例、原型、工厂方法、抽象工厂、创建者等 5 种创建型模式

  • 结构式模式

    用于描述如何将类或对象按某种布局组成更大的结构,GoF书中提供了代理、适配器、桥接、装饰、外观、享元、组合等7种结构型模式

  • 行为型模式

    用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责。GoF书中提供了模板方式、策略、命令、责任链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等11种行为型模式

2、UML

统一建模语言(Unified Modeling Language,UML)是用来设计软件的可视化建模语言。它的特点是简单、统一、图形化、能表达软件设计中的动态与静态信息

UML从目标系统的不同角度出发,定义了用例图、类图、对象图、状态图、活动图、时序图、协作图、构件图、部署图等9种图

2.1 类图概述

类图(Class diagram)是显示了模型的静态结构,特别是模型中存在的类、类的内部结构以及他们与其他类的关系等。类图不显示暂时性的信息。类图是面对对象建模的主要组成部分

2.2 类图的作用

  • 在软件工程中,类图是一种静态的结构图,描述了系统的类的集合,类的属性和类之间的关系,可以简化人们对系统的理解

  • 类图是系统分析和设计阶段的重要产物,是系统编码和测试的重要模型

2.3 类图表示法

image-20210927221031256
image-20210927220940281

使用长方形描述类的主要构成,将长方形垂直地分为三层 第1层是名字层,类名字是常规字形表明该类是具体类,类名字是斜体字形表明该类是抽象类。

​ 接口的名字是斜体字形,而且使用<>修饰名字,并且该修饰和名字分列在2行

第2层是变量层,也称属性层,列出类的成员变量及类型。 第3层是方法层,也称操作层,列出类的方法及返回类型。

image-20210927221455881
image-20210927221510944
image-20210927221520737
image-20210927221531402
image-20210927221539055

2.4 类的分类

继承(泛化):由孩子实线三角箭头指向父亲

​ 指一个类继承另一个类,子类添加新功能

​ 继承是类与类或者接口与接口之间最常见的关系

​ 继承关系也称为“is - a”关系

实现:由实现者虚线三角箭头指向被实现者

​ 类B实现接口A(可以是多个)

**双向关联:**由实线连接两者,两者之间可以互相调用和访问对方的元素

单向关联:由实线箭头连接,只有被指向者可以访问对方

​ 关联关系是对象之间的拥有关系,即“has a ”关系。例如工人拥有一辆汽车、学生拥有课本等等

**聚合:**由B实线空心菱形箭头指向A,A中拥有一个B,但B脱离于A仍然可以独立存活

​ 如果B类中某个成员变量的类型是类(接口)A,表示类A与类类B之间是整体与部分的关系,那么A和B是聚合关系

​ 例如一个球队有很多个学生队员,那么球队就是整体类,学生就是成员类

**组合:**由B实线实心菱形箭头指向A,A中拥有一个B,B脱离A后在系统中没有任何的意义

​ 组合中部分和整体具有统一的生存期。一旦整体对象不存在,部分对象也将不存在,

**依赖:**由B虚线箭头箭头指向A

​ (1)类A种某个方法的形参是类B类型

​ (2)类A种某个方法的返回类型是类B类型

​ (3)类A种某个方法的局部变量是类B类型

​ 依赖关系是一种使用关系

3、七个设计原则

软件设计七宗罪:

  1. 僵化性(Rigidity)

    ​ 如果单一改动会导致有依赖关系的模块的连锁改动,那么设计就是讲话的,需要连锁改动的模块越多,设计越僵化

  2. 脆弱性(Fragility)

    ​ 脆弱性指改动程序时,程序中有许多与改动位置并没有概念上关联的其他地方也可能出现问题,即设计易于遭受破坏

  3. 牢固性(Immobility)

    ​ 指设计中包含了对其他系统有用的部分,但要想把这些部分分离出来需要的努力和风险是巨大的,即设计难以复用

    ​ 比如,一段代码、函数、模块的功能可以在新模块或者新系统中使用,但是发现现存的代码、函数、模块依赖于一大堆其他的东西,以至于很难将它们抽取出来移动到其它地方使用

  4. 粘滞性( Viscosity)

    ​ 某种情况下,一个改动可以在保持原有设计意图和设计框架下进行,也可以在破坏原始意图和框架下进行。 第一种办法对系统的未来有利,第二种办法是权宜之计,可以解决短期问题, ​ 但会牺牲中长期利益。如果第二种办法比第一种办法更容易,程序员有可能牺牲长期利益,采取权宜之计,在一个通用的逻辑中建立一种特例,以便解决眼前的问题。 ​ 一个系统设计,如果总是使得第二种办法比第一种办法来得容易,说明粘滞性过高。一个粘滞性过高的系统会诱使维护它的程序员采取错误的维护方案。

  5. 不必要的重复( Needless Repetition)

    ​ 程序设计中,如果开发人员忽略抽象,滥用“剪切”和“粘贴”等操作,将造成大量的不必要的重复代码。这种情况不仅仅增加理解系统的难度,而且使改动系统变得困难,不利于系统维护。

  6. 不必要的复杂性( Needless Complexity)

    ​ 如果软件设计中包含了当前没有用的成分,即过度设计,将增加软件的复杂性。 ​ 比如,过度追求逻辑复杂和先进技术,导致了技术框架虽看似华丽却复杂难用。此外,如果在设计产品功能或界面交互时,过度追求体验完美和满足需求却导致实际体验下降、很多功能没人用。所以,软件设计应“有所为有所不为

  7. 晦涩性(Opacity)

    ​ 其他人以及设计人员自己难以理解所设计的软件,并且代码随时间而不断演化,往往会变得越来越晦涩、可读性差

一个好设计

(1)可维护性( Maintainability) 指软件能够被容易理解、改正、适应及扩展。 (2)可复用性( Reusability) 指软件能够被重复使用的难易程度。

面向对象设计的一个重要目标在于支持可维护性和可复用性,一方面需要实现设计方案或者源代码的复用,另一方面要确保系统能够易于扩展和修改,具有良好的可维护性

面向对象设计原则作用

(1)面向对象设计原则为支持软件可维护性和可复用性而诞生。 (2)面向对象设计原则是用于评价一个设计模式使用效果的重要指标之 7个设计原则是指导性原则,非强制性原则。每个设计模式都符合一个或多个面向对象设计原则

  1. 单一职责原则 (Single Responsibility Principle)

  2. 开放-关闭原则 (Open-Closed Principle)

  3. 里氏替换原则 (Liskov Substitution Principle)

  4. 依赖倒转原则 (Dependence Inversion Principle)

  5. 接口隔离原则 (Interface Segregation Principle)

  6. 迪米特法则(Law Of Demeter)

  7. 组合/聚合复用原则 (Composite/Aggregate Reuse Principle)

3.1 单一责任原则 SRP

3.2 开闭原则(OCP)

​ 面向修改关闭, 面向扩展开放

3.3 接口隔离原则 (ISP)

3.4 依赖倒置原则 DIP

依赖倒置原则的原始定义为:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。其核心思想是:要面向接口编程,不要面向实现编程

3.5 里氏替换原则 LSP

  1. 里氏替换原则是实现开闭原则的重要方式之一。

  2. 它克服了继承中重写父类造成的可复用性变差的缺点。

  3. 它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性

里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。

如果程序违背了里氏替换原则,则继承类的对象在基类出现的地方会出现运行错误(用继承类去代替基类运行的时候会出现运行错误)。这时其修正方法是:取消原来的继承关系,重新设计它们之间的关系。

关于里氏替换原则的例子,最有名的是“正方形不是长方形”。当然,生活中也有很多类似的例子,例如,企鹅、鸵鸟和几维鸟从生物学的角度来划分,它们属于鸟类;但从类的继承关系来看,由于它们不能继承“鸟”会飞的功能,所以它们不能定义成“鸟”的子类。同样,由于“气球鱼”不会游泳,所以不能定义成“鱼”的子类;“玩具炮”炸不了敌人,所以不能定义成“炮”的子类等。

3.6 组合复用原则 CRP

3.7 迪米特法则 LOD

迪米特法则强调不要和陌生人说话

4、创建者模式

1 简单工厂模式

最后更新于