0%

Spark入门:RDD编程(2)

4.2键值对RDD

4.2.1键值对RDD的创建

1679919962655

1
2
3
4
5
6
lines=sc.textFile("file:///usr/local/spark/mycode/rdd/word.txt")

pairRdd=lines.flatMap(lambda line:line.split(" ")).map(lambda word:(word,1))

pairRdd.foreach(print)

1679920610754

1679920024570

1679920857471

4.2.2常用键值对转换操作

1679920904927

1.reduceByKey(func)

1679920937895

1679921092076

2.groupByKey()

1679921126885

1679921148329

1679921162978

3.key

1679921231527

4.value

1679921285079

5.sortByKey()

1679921328845

6.sortBy()

1679921389676

7.mapValues(func)

1679921487925

8.join

1679921512081

4.2.3一个综合实例

1679921667681

2.1 UML图

2.1 UML类图 & UML时序图

统一建模语言(Unified Modeling Language,UML)是用来设计软件的可视化建模语言。它的特点是简单、统一、图形化、能表达软件设计中的动态与静态信息。UML是一种为面向对象系统的产品进行说明、可视化和编制文档的标准语言,独立于任何一种具体的程序设计语言。

1997 年 UML 被国际对象管理组织(OMG)采纳为面向对象的建模语言的国际标准。它的特点是简单、统一、图形化、能表达软件设计中的动态与静态信息。

应用场景

UML 能为软件开发的所有阶段提供模型化和可视化支持。而且融入了软件工程领域的新思想、新方法和新技术,使软件设计人员沟通更简明,进一步缩短了设计时间,减少开发成本。

UML 具有很宽的应用领域。其中最常用的是建立软件系统的模型,但它同样可以用于描述非软件领域的系统,如机械系统、企业机构或业务过程,以及处理复杂数据的信息系统、具有实时要求的工业系统或工业过程等。总之,UML 可以对任何具有静态结构和动态行为的系统进行建模,而且使用于从需求规格描述直至系统完成后的测试和维护等系统开发的各个阶段。

UML 模型大多以图表的方式表现出来,一份典型的建模图表通常包含几个块或框、连接线和作为模型附加信息的文本。这些虽简单却非常重要,在 UML 规则中相互联系和扩展。

语言是包括文字和图形的,有很多内容文字是无法表达的。比如建筑设计图纸吗,里面存在多图形,光用文字并不能表达清楚建筑设计。在建筑界,有一套标准来描述设计,同样道理,在软件开发界,也需要一套标准来帮助我们做好软件开发的工作。UML 就是其中的一种标准,注意这可不是唯一标准,只是 UML 是大家比较推崇的一种标准而已。UML 并不是强制性标准,没有规定在软件开发中一定要用 UML,但是我们需要包括 UML 在内的各种标准,来提高软件开发的水平。

基本构件

UML 建模的核心是模型,模型是现实的简化、真实系统的抽象。UML 提供了系统的设计蓝图。当给软件系统建模时,需要采用通用的符号语言,这种描述模型所使用的语言被称为建模语言。在 UML 中,所有的描述由事物、关系和图这些构件组成。下图完整地描述了所有构件的关系。

img

事物

事物是抽象化的最终结果,分为结构事物、行为事物、分组事物和注释事物。

  1. 结构事物

结构事物是模型中的静态部分,用以呈现概念或实体的表现元素,如下表所示。

img

  1. 行为事物

行为事物指 UML 模型中的动态部分,如下表所示。

img

  1. 分组事物

目前只有一种分组事物,即包。包纯碎是概念上的,只存在于开发阶段,结构事物、行为事物甚至分组事物都有可能放在一个包中,如下表所示。

img

  1. 注释事物

注释事物是解释 UML 模型元素的部分,如下表所示。

img

UML 从目标系统的不同角度出发,UML2.0 一共有 13 种图(UML1.5 定义了 9 种,UML2.0 增加了 4 种),别是类图、对象图、构件图、部署图、活动图、状态图、用例图、时序图、协作图 9 种,以及包图、组合结构图、时间图、交互概览图 4 种。

img

在 UML 2.0 的 13 种图中,类图(Class Diagrams)是使用频率最高的 UML 图之一。类图描述系统中的类,以及各个类之间的关系的静态视图,能够让我们在正确编写代码之前对系统有一个全面的认识。类图是一种模型类型,确切地说,是一种静态模型类型。类图表示类、接口和它们之间的协作关系,用于系统设计阶段。

2.1.1 类图概述

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

2.1.2 类图作用

  • 在软件工程中,类图是一种静态的结构图,描述了系统的类的集合,类的属性和类之间的关系,可以简化了人们对系统的理解;
  • 类图是系统分析和设计阶段的重要产物,是系统编码和测试的重要模型。

2.1.3 类图表示

1.类

​ 类(Class)是指具有相同属性、方法和关系的对象的抽象,它封装了数据和行为,是面向对象程序设计(OOP)的基础,具有封装性、继承性和多态性等三大特性。

(1) 类名(Name)是一个字符串,例如,Student。

(2) 属性(Attribute)是指类的特性,即类的成员变量。

(3) 操作(Operations)是类的任意一个实例对象都可以使用的行为,是类的成员方法。 在UML类图中,类使用包含类名、属性(field) 和方法(method) 且带有分割线的矩形来表示,比如下图表示一个Employee类,它包含name,age和address这3个属性,以及work()方法。

Employee.jpg

​ 属性/方法名称前加的加号和减号表示了这个属性/方法的可见性,UML类图中表示可见性的符号有三种:

  • +:表示public
  • -:表示private
  • #:表示protected

​ 属性的完整表示方式是: 可见性 名称 :类型 [ = 缺省值]

​ 方法的完整表示方式是: 可见性 名称(参数列表) [ : 返回类型]

注意:

1,中括号中的内容表示是可选的

2,也有将类型放在变量名前面,返回值类型放在方法名前面

举个例子:

demo.png

​ 上图Demo类定义了三个方法:

  • method()方法:修饰符为public,没有参数,没有返回值。
  • method1()方法:修饰符为private,没有参数,返回值类型为String。
  • method2()方法:修饰符为protected,接收两个参数,第一个参数类型为int,第二个参数类型为String,返回值类型是int。

类图中,需注意以下几点:

  • 抽象类或抽象方法用斜体表示
  • 如果是接口,则在类名上方加 <>
  • 字段和方法返回值的数据类型非必需
  • 静态类或静态方法加下划线

另外一个例子:

请看以下这个类图,类之间的关系是我们需要关注的:

_images/uml_class_struct.jpg

  • 车的类图结构为<>,表示车是一个抽象类;
  • 它有两个继承类:小汽车和自行车;它们之间的关系为实现关系,使用带空心箭头的虚线表示;
  • 小汽车为与SUV之间也是继承关系,它们之间的关系为泛化关系,使用带空心箭头的实线表示;
  • 小汽车与发动机之间是组合关系,使用带实心箭头的实线表示;
  • 学生与班级之间是聚合关系,使用带空心箭头的实线表示;
  • 学生与身份证之间为关联关系,使用一根实线表示;
  • 学生上学需要用到自行车,与自行车是一种依赖关系,使用带箭头的虚线表示;

2. 接口

接口(Interface)是一种特殊的类,它具有类的结构但不可被实例化,只可以被子类实现。它包含抽象操作,但不包含属性。它描述了类或组件对外可见的动作。在 UML 中,接口使用一个带有名称的小圆圈来进行表示。

如下所示是图形类接口的 UML 表示。

img

3. 类图

类图(ClassDiagram)是用来显示系统中的类、接口、协作以及它们之间的静态结构和关系的一种静态模型。它主要用于描述软件系统的结构化设计,帮助人们简化对软件系统的理解,它是系统分析与设计阶段的重要产物,也是系统编码与测试的重要模型依据。

类图中的类可以通过某种编程语言直接实现。类图在软件系统开发的整个生命周期都是有效的,它是面向对象系统的建模中最常见的图。如下所示是“计算长方形和圆形的周长与面积”的类图,图形接口有计算面积和周长的抽象方法,长方形和圆形实现这两个方法供访问类调用。

img

2.1.4 类与类之间关系的表示

  1. 关联关系(association)

关联关系是用一条直线表示的;它描述不同类的对象之间的结构关系;它是一种静态关系, 通常与运行状态无关,一般由常识等因素决定的;它一般用来定义对象之间静态的、天然的结构; 所以,关联关系是一种“强关联”的关系;

比如,乘车人和车票之间就是一种关联关系;学生和学校就是一种关联关系;

关联关系默认不强调方向,表示对象间相互知道;如果特别强调方向,如下图,表示A知道B,但 B不知道A;

uml_association.jpg

注:在最终代码中,关联对象通常是以成员变量的形式实现的;

关联关系是类与类之间最常用的一种关系,分为一般关联关系、聚合关系和组合关系。我们先介绍一般关联。关联又可以分为单向关联,双向关联,自关联。

a)单项关联

customer_address.png

在UML类图中单向关联用一个带箭头的实线表示。上图表示每个顾客都有一个地址,这通过让Customer类持有一个类型为Address的成员变量类实现。

b)双向关联

customer_product.png

从上图中我们很容易看出,所谓的双向关联就是双方各自持有对方类型的成员变量。

在UML类图中,双向关联用一个不带箭头的直线表示。上图中在Customer类中维护一个List,表示一个顾客可以购买多个商品;在Product类中维护一个Customer类型的成员变量表示这个产品被哪个顾客所购买。

c)自关联

node.png

自关联在UML类图中用一个带有箭头且指向自身的线表示。上图的意思就是Node类包含类型为Node的成员变量,也就是“自己包含自己”。

2. 聚合关系(aggregation)

聚合关系是关联关系的一种,是强关联关系,是整体和部分之间的关系。

聚合关系也是通过成员对象来实现的,其中成员对象是整体对象的一部分,但是成员对象可以脱离整体对象而独立存在。例如,学校与老师的关系,学校包含老师,但如果学校停办了,老师依然存在。

聚合关系用一条带空心菱形箭头的直线表示,如下图表示A聚合到B上,或者说B由A组成;

uml_aggregation.jpg

下图所示是大学和教师的关系图:

image-20191229173422328.png

3. 组合关系(composition)

组合表示类之间的整体与部分的关系,但它是一种更强烈的聚合关系。

在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在,部分对象也将不存在,部分对象不能脱离整体对象而存在。例如,头和嘴的关系,没有了头,嘴也就不存在了。

在 UML 类图中,组合关系用带实心菱形的实线来表示,菱形指向整体。如下图表示A组成B,或者B由A组成;

uml_composition.jpg

下图所示是头和嘴的关系图:

image-20191229173455149.png

与聚合关系一样,组合关系同样表示整体由部分构成的语义;比如公司由多个部门组成;

但组合关系是一种强依赖的特殊聚合关系,如果整体不存在了,则部分也不存在了;例如, 公司不存在了,部门也将不存在了;

4. 依赖关系(dependency)

依赖关系是一种使用关系,它是对象之间耦合度最弱的一种关联方式,是临时性的关联。在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。

在 UML 类图中,依赖关系使用带箭头的虚线来表示,箭头从使用类指向被依赖的类。如下图表示A依赖于B;他描述一个对象在运行期间会用到另一个对象的关系;

uml_dependency.jpg

下图所示是司机和汽车的关系图,司机驾驶汽车:

image-20191229173518926.png

与关联关系不同的是,它是一种临时性的关系,通常在运行期间产生,并且随着运行时的变化; 依赖关系也可能发生变化;

显然,依赖也有方向,双向依赖是一种非常糟糕的结构,我们总是应该保持单向依赖,杜绝双向依赖的产生;

注:在最终代码中,依赖关系体现为类构造方法及类方法的传入参数,箭头的指向为调用关系;依赖关系除了临时知道对方外,还是“使用”对方的方法和属性;

5. 继承关系/泛化关系(generalization)

继承关系是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系。类的继承结构表现在UML中为:泛化(generalize)与实现(realize):

继承关系为 is-a的关系;两个对象之间如果可以用 is-a 来表示,就是继承关系:(..是..)

eg:自行车是车、猫是动物

在 UML 类图中,泛化关系用带空心三角箭头的实线来表示,箭头从子类指向父类。如下图表示(A继承自B);

uml_generalization.jpg

例如,汽车在现实中有实现,可用汽车定义具体的对象;汽车与SUV之间为泛化关系;

uml_generalize.jpg

再比如,Student 类和 Teacher 类都是 Person 类的子类,其类图如下图所示:

image-20191229173539838.png

注:最终代码中,泛化关系表现为继承非抽象类;

6. 实现关系(realize)

实现关系是接口与实现类之间的关系。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。

在 UML 类图中,实现关系使用带空心三角箭头的虚线来表示,箭头从实现类指向接口。

比如:”车”为一个抽象概念,在现实中并无法直接用来定义对象;只有指明具体的子类(汽车还是自行车),才 可以用来定义对象(”车”这个类在C++中用抽象类表示,在JAVA中有接口这个概念,更容易理解)

uml_realize.jpg

再例如,汽车和船实现了交通工具,其类图如图 9 所示。

image-20191229173554296.png

注:最终代码中,实现关系表现为继承抽象类;

类关系记忆技巧如下表所示。

img

注意:UML 的标准类关系图中,没有实心箭头。有些 Java 编程的 IDE 自带类生成工具可能出现实心箭头,主要目的是降低理解难度。

下面用一个经典案例来加深和巩固对类图的理解。下图是对动物衍生关系描述的类图。这个图非常有技术含量也非常经典,大家可以好好理解一下。

img

2.1.5 时序图

为了展示对象之间的交互细节,后续对设计模式解析的章节,都会用到时序图;

时序图(Sequence Diagram)是显示对象之间交互的图,这些对象是按时间顺序排列的。时序图中显示的是参与交互的对象及其对象之间消息交互的顺序。

时序图包括的建模元素主要有:对象(Actor)、生命线(Lifeline)、控制焦点(Focus of control)、消息(Message)等等。

角色(Actor): 系统角色,可以是人、及其甚至其他的系统或者子系统。

对象(Object): 对象包括三种命名方式:

  • 第一种方式包括对象名和类名;
  • 第二中方式只显示类名不显示对象名,即表示他是一个匿名对象;
  • 第三种方式只显示对象名不显示类明。

SequenceDiagram.jpg

生命线(Lifeline): 生命线在顺序图中表示为从对象图标向下延伸的一条虚线,表示对象存在的时间,如下图

lifeline.gif

控制焦点(Focus of Control): 控制焦点是顺序图中表示时间段的符号,在这个时间段内对象将执行相应的操作。用小矩形表示,如下图。

ElementFOC.jpg

消息(Message): 消息一般分为同步消息(Synchronous Message),异步消息(Asynchronous Message)和返回消息(Return Message).如下图所示:

Message.gif

同步消息=调用消息(Synchronous Message): 消息的发送者把控制传递给消息的接收者,然后停止活动,等待消息的接收者放弃或者返回控制。用来表示同步的意义。

异步消息(Asynchronous Message): 消息发送者通过消息把信号传递给消息的接收者,然后继续自己的活动,不等待接受者返回消息或者控制。异步消息的接收者和发送者是并发工作的。

返回消息(Return Message): 返回消息表示从过程调用返回

自关联消息(Self-Message): 表示方法的自身调用以及一个对象内的一个方法调用另外一个方法。

SelfMessage.gif

组合片段示例:

CombinedFragments.gif

  • Alternative fragment(denoted “alt”) 与 if…then…else对应
  • Option fragment (denoted “opt”) 与 Switch对应
  • Parallel fragment (denoted “par”) 表示同时发生
  • Loop fragment(denoted “loop”) 与 for 或者 Foreach对应

时序图实例分析(Sequece Diagram Example Analysis)

完成课程创建功能,主要流程有:

1、请求添加课程页面,填写课程表单,点击【create】按钮

2、添加课程信息到数据库

3、向课程对象追加主题信息

4、为课程指派教师

5、完成课程创建功能

Dequence_Diagram_Example.jpg

1、序号1.0-1.3 完成页面的初始化

2、序号1.4-1.5 课程管理员填充课程表单

3、序号1.6-1.7 课程管理员点击【Create】按钮,并响应点击事件

4、序号1.8 Service层创建课程

5、序号1.9-1.10 添加课程到数据库,并返回课程编号CourseId

6、序号1.11-1.12 添加课程主题到数据库,并返回主题编号topicId

7、序号1.13 给课程指派教师

8、序号1.14 向界面抛创建课程成功与否的消息

Python类

类(class) 和 对象(object)

类:创建对象的模板,定义对象将会拥有的属性和函数

__init__函数:每个类必须定义的函数,对象创建语句时自动执行

1
2
3
4
5
6
7
8
9
class myday: #建立一个类模板

def __init__():

.....



day1 = myday() #创建一个myday类的对象
  • python类
    类名:mayday
    属性:name和emotion
    函数:init函数,wake函数,eat函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 类(class)
class myday:
def __init__(self): #每个类必须定义(def) __init__函数,每个函数第一参数都是self
self.name = "Xiao Ming" #myday对象拥有属性name和emotion
self.emotion = "happy"

def wake(self,event):
if event == "上课":
self.emotion = "still happy"

def eat(self, food):
if food == "牛肉":
self.emotion = "more happy"
#def __eq__(self,other):
#return self.name == other.name
day2 = myday()
#创建对象时会自动执行__init__方法
print(day2.emotion)
print(day2.name)

__init__函数添加参数

创建对象时传入self之后的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class  myday:
def __init__(self,name,emotion):
self.emotion = emotion #定义对象属性“self.emotion”,将这个属性赋值为函数传入的参数“emotion”
self.name = name
def wake(self,event):
if event == "上课":
self.emotion = "still happy"

def eat(self, food):
if food == "牛肉":
self.emotion = "more happy"
#def __eq__(self,other):
#return self.name == other.name
day2 = myday("me","very happy")
#创建对象时会自动执行__init__函数
print(day2.name)
print(day2.emotion)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class  myday:
def __init__(self,name,emotion):
self.emotion = emotion #定义对象属性“self.emotion”,将这个属性赋值为函数传入的参数“emotion”
self.name = name
def wake(self,event):
if event == "上课":
self.emotion = "still happy"

def eat(self, food):
if food == "牛肉":
self.emotion = "more happy"
#def __eq__(self,other):
#return self.name == other.name
day2 = myday("me","very happy")
#调用对象函数时,从self之后的参数开始传入
day2.eat("jiaozi")
print(day2.emotion)
day2.eat("牛肉")
print(day2.emotion)




任务:编写一个类继承myday类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class  myday:
def __init__(self,name,emotion):
self.emotion = emotion #定义对象属性“self.emotion”,将这个属性赋值为函数传入的参数“emotion”
self.name = name
def wake(self,event):
if event == "上课":
self.emotion = "still happy"

def eat(self, food):
if food == "牛肉":
self.emotion = "more happy"
#def __eq__(self,other):
#return self.name == other.name
day2 = myday("me","very happy")

#创建对象时,程序会自动执行__init__函数
day2.eat("jiaozi")
print(day2.emotion)
day2.eat("牛肉")
print(day2.emotion)

#类的继承 和 函数重写
class night(myday): #night类继承myday类
def __init__(self):
self.emotion = "nice"
self.name = "me"
def play(self):
self.emotion = "so nice"
night1 = night()
night1.eat("羊肉") #night继承myday类除__init__之外的函数
print(night1.emotion)
night1.eat("牛肉")
print(night1.emotion)

一个类是否等于另一个类?

1
2
3
4
5
6
7
day1 = myday("a","happy")
day2 = myday("a","happy")

day1.name == day2.name #True or False
day1 == day2 #True or False?
#(如果我们在类里定义一个__eq__函数,当我们执行 day1 == day2语句的时候,程序会自动执行__eq__函数)

1679290854192

多线程开发案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import time
import threading

class TestThread(threading.Thread):
def __init__(self, para='hi', sleep=3):
super().__init__()
self.para = para
self.sleep = sleep
def run(self):
"""线程内容"""
time.sleep(self.sleep)
print(self.para)

thread_hi = TestThread()
thread_hello = TestThread('hello', 1)
# 启动线程
thread_hi.start()
thread_hello.start()



任务!:按照如下描述定义一个类,代表一个企业

class company: init函数:定义资金(money)属性,产品(product)属性和价格(price)属性

register函数:公司注册,修改资金属性

produce函数:制造,修改资金属性,修改产品属性

sale函数:销售,修改资金属性和产品属性

research函数:研发,修改资金属性和价格属性

  • 任务!:定义另一个类,继承company类,重写research函数(子类需要添加至少一个新的函数,并重写至少一个父类的函数。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class company:
def __init__(self,money,product,price) :
self.money=money
self.product=product
self.price=price
def register(self,tol_money):#注册资金
self.money=tol_money
def produce(self,pro_money,product_num):
self.money-=pro_money#生产就减少了
self.product+=product_num#产品多了
def sale(self,sal_money,product_num):
self.money+=sal_money#钱多了
self.product-=product_num#产品减少了
def research(self,re_money,re_price):
self.money-=re_money#钱减少了
self.price+=re_price#价格上去了
company1=company("1000","机器","100/2")
company1.register("5000")
print(company1.money)

research函数:研发,修改资金属性和价格属性,产品属性

invert函数:投资,修改金钱属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class BigCompany(company):
def __init__(self,money,product,price):
super().__init__(money,product,price)

def research(self,re_money,re_price,re_prodcut):
self.money=re_money#钱减少了
self.price+=re_price#价格上去了
self.product+=re_prodcut#产品多了
def inverst(self,invstment_money):
self.money-=invstment_money

company2=BigCompany(7000,2,50)
company2.research(8000,80,90)
company2.inverst(908070)
print(company2.money)

如何在Linux系统下编译C++

方法一:

1
2
3
4
5
6
7
8
#include<iostream>
#include<cstdio>
using namespace std;
int main(){

cout<<"hello_word";
return 0;
}

1679061064013

编译

1
gcc 1.cpp -lstdc++ 

生a.out文件

1679061165889

运行a.out

1
./a.out

1679061280444

方法二:

1
2
3
4
5
6
7
8
#include<iostream>
#include<cstdio>
using namespace std;
int main(){

cout<<"hello_word222";
return 0;
}

1679061782953

编译

1
g++ 2.cpp -o 2output

运行2output

1
./2output

进程同步

  • pv 操作 互斥锁

    1
    2
    3
    4
    P --wait(信号量S){
    S<=0
    S-- //上锁
    }

    成对出现

    1
    2
    3
    V --signal(信号量){
    S++ //开锁
    }
  • 生产者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int in(),out=();
item butter[n];
semaphore mutex=1,empty=n,full=();//mutex必须为1 ,empty空位置,full有多少个
void proceducer(){
do{
producer an item nextp;
……
wait(empty); //enpty--
wait(mutex); //上锁
butter[in]=nextp; //生产一个
in:=(in+1)%n; // 生产一个
signal(mutex); //解锁
signal(full); //full++
}while(TRUE);
}
  • 消费者
1
2
3
4
5
6
7
8
9
10
11
void consumer(){
do{
wait(full); //full-1
wait(mutex); //上锁
nextc=butter[out]; //消费一个
out=(out+1)%n; //消费一个
signal(mutex); //解锁
signal(empty); //empty++
consumer the item in nextc;
}while(TRUE);
}
1
2
3
4
5
void main(){
cobegin
proceducer();consumer();
coend
}

1679017003280

第1关:生产者消费者同步控制

任务描述

本关任务:编写生产者消费者同步控制程序。

相关知识

为了完成本关任务,你需要掌握:1.理解生产者消费者同步问题,2.线程的编写方法,3.使用信号量实现线程间同步控制。

线程并发引起的同步问题

线程以并发形式运行,当并发的线程间访问共享数据时,会发后争用现象,不进行同步控制的线程运行会造成不恰当的结果。

生产者消费者同步问题

生产者消费者是典型的同步问题,他们共享了一个缓冲池(全局变量数组),当缓冲池有空位时生产者线程向缓冲池中依次赋值,如果缓冲池满则等待。当缓冲池中有数据时消费者线程从中取走数据,如果缓冲池空则等待。

如何解决线程间访问共享变量的冲突问题

当多个线程访问同一个共享变量时,共享变量成为临界资源,它需要操作系统提供同步控制机制,以保证多个线程可以依序访问,当一个线程操作临界资源时其它线程不会中断其操作,因此对临界资源的操作是安全的。 下面是linux平台使用信号量的头文件和方法。 #include <semaphore.h> int sem_init(sem_t * sem, int pshared, unsigned int value);//创建信号量变量,value是信号量的初值 int sem_destroy(sem_t *sem);//销毁信号量 int sem_post(sem_t * sem); //信号量值增加1,并激活处于等待状态的线程 int sem_wait(sem_t * sem); //信号量值减少1 为0时将调用该方法的线程被OS阻塞

编程要求

主线程序启动生产者线程和消费者线程,并等待两个线程的结束。 两个线程使用三个信号量,其中生产者线程向全局变量进行十次赋值(代表生产),每次赋值前都要检查是否有空位,有空位的情况下 需要获得互斥量sem_mutex,并对共享缓冲区进行赋值,然后释放互斥量以便消费者线程可以操作缓冲区临界资源。 消费者线程要从缓冲区取值,取值前先检查是否有可用数据,有可用数据的情况下再获得互斥量sem_mutex,再取出缓冲区的值。 要特别说明的是生产者和消费者线程工作的速率是不同的,但在同步信号控制下,两个线程节奏互相配合步调一致。在《计算机操作系统》 教材中使用了伪代码,并且循环体没有结束条件,在此用C语言改写原程序,生产者一共只生产十个数据。 ####测试说明 为简化数据操作,程序没有输入,主线程已经写好, void * producer(void * arg); void * consumer(void * arg); static sem_t sem_empty;//空位个数 static sem_t sem_full; //可用数据个数 static sem_t sem_mutex; //互斥量,用于控制两个线程互斥访问缓冲区 static int buffer[]={0,0,0}; int main(int argc,char * argv[]) { pthread_t id_prod,id_consum; sem_init(&sem_empty,0,3); //初值为3,空位为3, sem_init(&sem_full,0,0); //初值为0,可用数据个数为0, sem_init(&sem_mutex,0,1); //初值为1,用于控制两个线程互斥访问缓冲区 pthread_create(&id_prod, NULL,producer,NULL);//创建生产者线程 pthread_create(&id_consum,NULL,consumer,NULL);//创建消费者线程

pthread_join(id_prod,NULL); //主线程等待生产者线程结束 pthread_join(id_consum,NULL);//主线程等待消费者线程结束 sem_destroy(&sem_empty); sem_destroy(&sem_full); sem_destroy(&sem_mutex); return 0; } 学生需要补充producer线程与consumer中关于同步控制部分的程序。

输出:1至10 (需由学生根据程序生成正确结果) 开始你的任务吧,祝你成功!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//stu001.c 生产者消费者同步控制,由学生完成缺失代码。 
//主线程序启动生产者线程和消费者线程,全局变量初值为0
//生产者线程向全局变量进行10次赋值(代表生产),消费者线程从全局变量读取值,///并重新赋值0(代表消费了产品)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

void * producer(void * arg);
void * consumer(void * arg);
static sem_t sem_empty;//空位个数
static sem_t sem_full; //可用数据个数
static sem_t sem_mutex; //互斥量,用于控制两个线程互斥访问缓冲区
static int buffer[]={0,0,0};//共享的缓冲区
int main(int argc,char * argv[])
{
pthread_t id_prod,id_consum;
sem_init(&sem_empty,0,3); //初值为3,空位为3,
sem_init(&sem_full,0,0); //初值为0,可用数据个数为0,
sem_init(&sem_mutex,0,1); //初值为1,用于控制两个线程互斥访问缓冲区
pthread_create(&id_prod, NULL,producer,NULL);//创建生产者线程
pthread_create(&id_consum,NULL,consumer,NULL);//创建消费者线程

pthread_join(id_prod,NULL); //主线程等待生产者线程结束
pthread_join(id_consum,NULL);//主线程等待消费者线程结束
sem_destroy(&sem_empty);
sem_destroy(&sem_full);
sem_destroy(&sem_mutex);
return 0;
}
void * producer(void * arg)
{
int i,pIndex=0;
for(i=11;i<21;i++)
{
//请在begin end语句间补全程序语句实现生产者向缓冲区赋值操作
/* begin *******************程序代码约六行******************************* */
sem_wait(&sem_empty);//empty--
sem_wait(&sem_mutex); //枷锁
buffer[pIndex]=i; //缓冲区只有三个位置
pIndex=(pIndex+1)%3;// 11-- 0 12--1 13--2 14--0 15--1 16--2
sem_post(&sem_mutex);//解锁
sem_post(&sem_full);//full++
/* end ************************************************************** */
}
return NULL;
}
void * consumer(void *arg)
{
int i,cIndex=0;
for(i=11;i<21;i++)
{
//请在begin end语句间补全程序语句实现扫描算法,算出总访问磁道数存入totaltracks变量
/* begin *******************程序代码约六行******************************* */
sem_wait(&sem_full);//full--
sem_wait(&sem_mutex);//枷锁
printf("%d,",buffer[cIndex]);
cIndex=(cIndex+1)%3;//与上面对应
sem_post(&sem_mutex);//解锁
sem_post(&sem_empty);//empty++

/* end ************************************************************** */
}
return NULL;
}

参考Linux下c++的东西

https://man7.org/linux/man-pages/man3/sem_wait.3.html

1
2
3
sem_wait是一个函数,也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,将信号量的值将减到1。
如果对一个值为0的信号量调用sem_wait(),这个函数就会原地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加 一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。sem_trywait(sem_t *sem)是函数sem_wait的非阻塞版,它直接将信号量sem减1,同时返回错误代码。
(不得不说,百度确实蛮会复制粘贴翻译的,只不过没有那个网站格式做的好hhh)
1
sem_post是给信号量的值加上一个“1”,它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;而同 时对同一个文件进行读和写操作的两个程序就有可能会引起冲突。
1
sem_t  C语言中,信号量的数据类型为结构sem_t,它本质上是一个长整型的数。

https://blog.51cto.com/u_13999641/4314815

没看完【argc,argv是什么】

真的忘的差不多了0.0

1679026388457

1679026432795

Java数据类型

定义

Java语言是强类型语言,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不同大小的内存空间。

分类

  • 基本数据类型
    • 数值型
      • 整数类型(byte,short,int,long)
      • 浮点类型(float,double)
    • 字符型(char)
    • 布尔型(boolean)
  • 引用数据类型
    • 类(class)
    • 接口(interface)
    • 数组([])

计算机存储单元

  • 定义:变量是内存中的小容器,用来存储数据。那么计算机内存是怎么存储数据的呢?无论是内存还是硬盘,计算机存储设备的最小信息单元叫“位(bit)”,我们又称之为“比特位”,通常用小写的字母b表示。而计算机最小的存储单元叫“字节(byte)”,通常用大写字母B表示,字节是由连续的8个位组成。
  • 常用存储单元关系
    • 1B= 8b
      1KB = 1024B
      1MB = 1024KB
      1GB = 1024MB
      1TB = 1024GB

Java基本数据类型图

1678974709618

  • 定义

    • 数据类型的转换是在所赋值的数值类型和被变量接收的数据类型不一致时发生的,它需要从一种数据类型转换成另一种数据类型。
  • 分类

    • 隐式转换

      • 定义

        • 在运算过程中,由于不同的数据类型会转换成同一种数据类型,所以整型、浮点型以及字符型都可以参与混合运算。自动转换的规则是从低级类型数据转换成高级类型数据。
      • 转换规则

        • 数值型数据的转换:byte→short→int→long→float→double。
        • 字符型转换为整型:char→int。
      • 转换条件

        • 自动类型转换的实现需要同时满足两个条件:①两种数据类型彼此兼容,②目标类型的取值范围大于源数据类型(低级类型数据转换成高级类型数据)。例如 byte 类型向 short 类型转换时,由于 short 类型的取值范围较大,会自动将 byte 转换为 short 类型。
    • 显式转换

      • 定义
        • 当两种数据类型不兼容,或目标类型的取值范围小于源类型时,自动转换将无法进行,这时就需要进行强制类型转换。
        • 语法格式
          • 目标类型 变量名 = (目标类型) (被转换的数据);
            举例:int b = (byte)(a + b);
        • 注意
          • 如果超出了被赋值的数据类型的取值范围得到的结果会与你期望的结果不同
          • 不建议强制转换,因为会有精度的损失。

2023_Week1_Exp_Upload

1. (简答题)用 UMLet 工具对下面代码绘制类图

img

img

1678972004441

1678972695825

【关联关系】:是一种拥有的关系,它使一个类知道另一个类的属性和方法

【代码体现】:成员变量

【箭头及指向】:带普通箭头的实心线,指向被拥有者

1.上图中,课程与学生是双向关联,课程有多名学生,学生也可能有多个课程。

2.但学生与成绩间的关系为单向关联,一名学生可能要有多门成绩,成绩有学生信息,但学生类不拥有成绩变量

1678972739648

3.【组合关系】:是整体与部分的关系,但部分不能离开整体而单独存在。如公司和部门是整体和部分的关系,没有公司就不存在部门。

组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期。

【代码体现】:成员变量

【箭头及指向】:带实心菱形的实线,菱形指向整体

上图中课程为整体,成绩没有课程就不存在。

1678973083372

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

【代码表现】:局部变量、方法的参数或者对静态方法的调用

【箭头及指向】:带箭头的虚线,指向被使用者

一个宿舍类的实现需要另一个学生类的协助,宿舍是被使用者

一、单一职责原则**

就一个类而言,应该仅有一个引起它变化的原因。

如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱他的设计,当变化发生时,设计会遭受到意想不到的破坏;软件设计真正要做的许多内容就是发现职责并把那些职责相互分离。

二、开放-封闭原则

软件实体应该可以扩展,但不可修改。该原则是面向对象设计的核心所在,遵循这个原则可以带来面向对象技术所声称的可维护、可扩展、可复用、灵活性好。

设计人员必须对于他设计的模块应该对哪种变化封闭做出选择,必须先猜测出最有可能发生的变化种类,然后构造抽象来隔离那些变化。最初编写程序时假设变化不会发生,当变化发生时,就创建抽象来隔离以后发生的同类变化,拒绝不成熟的抽象。

三、里氏代换原则

子类型必须能够替换掉它们的父类型。由于子类型的可替换性才使得使用父类类型的模块在无需修改的情况下就可以扩展。

四、依赖倒转原则

高层模块不应该依赖低层模块,两个都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。

要针对接口编程,不要针对实现编程。该原则可以说是面向对象设计的标志,编写时考虑的是如何对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口。

五、迪迷特原则(最少知识原则)

如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用;如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

该原则其根本思想,是强调了类之间的松耦合;类之间的耦合越弱,越利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。在类的结构设计上,每一个类都应当尽量降低成员的访问权限。

六、合成/聚合复用原则

尽量使用合成/聚合,尽量不要使用类继承。

聚合表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。

优先使用对象的合成/聚合将有助于你保持每个类被封装,并被击中在单个任务上,这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。

七、UML例图

‘+’表示public,‘-’表示private,‘#’表示protected;

接口顶端有《interface》显示,只有两行;同时另一个表示方法为棒棒糖表示法;

聚合表示一种弱的’拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;

合成是一种强的’拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样;

img

在UML类图中,常见的有以下几种关系: 泛化(Generalization), 实现(Realization),关联(Association),聚合(Aggregation),组合(Composition),依赖(Dependency)

1. 泛化(Generalization)

【泛化关系】:是一种继承关系,表示一般与特殊的关系,它指定了子类如何特化父类的所有特征和行为。例如:老虎是动物的一种,即有老虎的特性也有动物的共性。

【箭头指向】:带三角箭头的实线,箭头指向父类

img

2. 实现(Realization)

【实现关系】:是一种类与接口的关系,表示类是接口所有特征和行为的实现.

【箭头指向】:带三角箭头的虚线,箭头指向接口

img

3. 关联(Association)

【关联关系】:是一种拥有的关系,它使一个类知道另一个类的属性和方法;如:老师与学生,丈夫与妻子关联可以是双向的,也可以是单向的。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头。

【代码体现】:成员变量

【箭头及指向】:带普通箭头的实心线,指向被拥有者

img

上图中,老师与学生是双向关联,老师有多名学生,学生也可能有多名老师。但学生与某课程间的关系为单向关联,一名学生可能要上多门课程,课程是个抽象的东西他不拥有学生。

下图为自身关联:

img

4. 聚合(Aggregation)

【聚合关系】:是整体与部分的关系,且部分可以离开整体而单独存在。如车和轮胎是整体和部分的关系,轮胎离开车仍然可以存在。

聚合关系是关联关系的一种,是强的关联关系;关联和聚合在语法上无法区分,必须考察具体的逻辑关系。

【代码体现】:成员变量

【箭头及指向】:带空心菱形的实心线,菱形指向整体

img

5. 组合(Composition)

【组合关系】:是整体与部分的关系,但部分不能离开整体而单独存在。如公司和部门是整体和部分的关系,没有公司就不存在部门。

组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期。

【代码体现】:成员变量

【箭头及指向】:带实心菱形的实线,菱形指向整体

img

6. 依赖(Dependency)

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

【代码表现】:局部变量、方法的参数或者对静态方法的调用

【箭头及指向】:带箭头的虚线,指向被使用者

img

各种关系的强弱顺序:

泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖

下面这张UML图,比较形象地展示了各种类图关系:

img

创建型

1. Factory Method(工厂方法)

img

意图:

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。

适用性:

当一个类不知道它所必须创建的对象的类的时候。

当一个类希望由它的子类来指定它所创建的对象的时候。

当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

详解:设计模式是什么鬼(工厂方法)

2. Abstract Factory(抽象工厂)

img

意图:

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

适用性:

一个系统要独立于它的产品的创建、组合和表示时。

一个系统要由多个产品系列中的一个来配置时。

当你要强调一系列相关的产品对象的设计以便进行联合使用时。

当你提供一个产品类库,而只想显示它们的接口而不是实现时。

详解:设计模式是什么鬼(抽象工厂)

3. Builder(建造者)

img

意图:

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

适用性:

当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。

当构造过程必须允许被构造的对象有不同的表示时。

详解:设计模式是什么鬼(建造者)

4. Prototype(原型)

img

意图:

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

适用性:

当要实例化的类是在运行时刻指定时,例如,通过动态装载;或者

为了避免创建一个与产品类层次平行的工厂类层次时;或者

当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

详解:设计模式是什么鬼(原型)

5. Singleton(单例)

img

意图:

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

适用性:

当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。

当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

详解:设计模式是什么鬼(单例)

结构型

6. Adapter Class/Object(适配器)

img

意图:

将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适用性:

你想使用一个已经存在的类,而它的接口不符合你的需求。

你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。

(仅适用于对象Adapter )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。

详解:设计模式是什么鬼(适配器)

7. Bridge(桥接)

img

意图:

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

适用性:

你不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。

类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这时Bridge 模式使你可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。

对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。

(C++)你想对客户完全隐藏抽象的实现部分。在C++中,类的表示在类接口中是可见的。

有许多类要生成。这样一种类层次结构说明你必须将一个对象分解成两个部分。Rumbaugh 称这种类层次结构为“嵌套的普化”(nested generalizations )。

你想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点。一个简单的例子便是Coplien 的String 类[ Cop92 ],在这个类中多个对象可以共享同一个字符串表示(StringRep )。

详解:设计模式是什么鬼(桥接)

8. Composite(组合)

img

意图:

将对象组合成树形结构以表示“部分-整体”的层次结构。C o m p o s i t e 使得用户对单个对象和组合对象的使用具有一致性。

适用性:

你想表示对象的部分-整体层次结构。

你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

详解:设计模式是什么鬼(组合)

9. Decorator(装饰)

img

意图:

动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活。

适用性:

在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

处理那些可以撤消的职责。

当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

详解:设计模式是什么鬼(装饰)

10. Facade(外观)

img

意图:

为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

适用性:

当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade 可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过facade层。

客户程序与抽象类的实现部分之间存在着很大的依赖性。引入facade 将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。

当你需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系。

详解:设计模式是什么鬼(门面)

11. Flyweight(享元)

img

意图:

运用共享技术有效地支持大量细粒度的对象。

适用性:

一个应用程序使用了大量的对象。

完全由于使用大量的对象,造成很大的存储开销。

对象的大多数状态都可变为外部状态。

如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。

应用程序不依赖于对象标识。由于Flyweight 对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。

详解:设计模式是什么鬼(享元)

12. Proxy(代理)

img

意图:

为其他对象提供一种代理以控制对这个对象的访问。

适用性:

在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。下面是一 些可以使用Proxy 模式常见情况:

  • 远程代理(Remote Proxy )为一个对象在不同的地址空间提供局部代表。NEXTSTEP[Add94] 使用NXProxy 类实现了这一目的。Coplien[Cop92] 称这种代理为“大使” (Ambassador )。
  • 虚代理(Virtual Proxy )根据需要创建开销很大的对象。在动机一节描述的ImageProxy 就是这样一种代理的例子。
  • 保护代理(Protection Proxy )控制对原始对象的访问。保护代理用于对象应该有不同 的访问权限的时候。例如,在Choices 操作系统[ CIRM93]中KemelProxies为操作系统对象提供 了访问保护。
  • 智能指引(Smart Reference )取代了简单的指针,它在访问对象时执行一些附加操作。它的典型用途包括:

对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它(也称为SmartPointers[Ede92 ] )。

当第一次引用一个持久对象时,将它装入内存。

在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。

详解:设计模式是什么鬼(代理)

行为型

13. Interpreter(解释器)

img

意图:

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

适用性:

当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:

该文法简单对于复杂的文法, 文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。

效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。

详解:设计模式是什么鬼(解释器)

14. Template Method(模板方法)

img

意图:

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

适用性:

一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。

各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke 和Johnson 所描述过的“重分解以一般化”的一个很好的例子[ OJ93 ]。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。

控制子类扩展。模板方法只在特定点调用“hook ”操作(参见效果一节),这样就只允许在这些点进行扩展。

详解:设计模式是什么鬼(模板方法)

15. Chain of Responsibility(责任链)

img

意图:

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

适用性:

有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。

你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。

可处理一个请求的对象集合应被动态指定。

详解:设计模式是什么鬼(责任链)

16. Command(命令)

img

意图:

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。

适用性:

抽象出待执行的动作以参数化某对象,你可用过程语言中的回调(call back)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。Command 模式是回调机制的一个面向对象的替代品。

在不同的时刻指定、排列和执行请求。一个Command对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程并在那儿实现该请求。

支持取消操作。Command的Excute 操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command 接口必须添加一个Unexecute操作,该操作取消上一次Execute调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用Unexecute和Execute来实现重数不限的“取消”和“重做”。

支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。在Command接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用Execute操作重新执行它们。

用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务( transaction)的信息系统中很常见。一个事务封装了对数据的一组变动。Command模式提供了对事务进行建模的方法。Command有一个公共的接口,使得你可以用同一种方式调用所有的事务。同时使用该模式也易于添加新事务以扩展系统。

详解:设计模式是什么鬼(命令模式)

17. Iterator(迭代器)

img

意图:

提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。

适用性:

访问一个聚合对象的内容而无需暴露它的内部表示。

支持对聚合对象的多种遍历。

为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。

详解:设计模式是什么鬼(迭代器)

18. Mediator(中介者)

img

意图:

用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

适用性:

一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。

一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。

想定制一个分布在多个类中的行为,而又不想生成太多的子类。

详解:设计模式是什么鬼(中介)

19. Memento(备忘录)

img

意图:

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

适用性:

必须保存一个对象在某一个时刻的(部分)状态, 这样以后需要时它才能恢复到先前的状态。

如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

详解:设计模式是什么鬼(备忘录)

20. Observer(观察者)

img

意图:

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。

适用性:

当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。

当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。

详解:设计模式是什么鬼(观察者)

21. State(状态)

img

意图:

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

适用性:

一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。

一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常, 有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

详解:设计模式是什么鬼(状态)

22. Strategy(策略)

img

意图:

定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

适用性:

许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。

需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时[H087] ,可以使用策略模式。

算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。

一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

详解:设计模式是什么鬼(策略)

23. Visitor(访问者)

img

意图:

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。TemplateMethod 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

适用性:

一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。

各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke和Johnson所描述过的“重分解以一般化”的一个很好的例子[OJ93]。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。

控制子类扩展。模板方法只在特定点调用“hook ”操作(参见效果一节),这样就只允许在这些点进行扩展。

详解:设计模式是什么鬼(访问者)

1
来源:blog.csdn.net/zsjlovesm521/article/details/94382666