0%

使用预训练 LLM

模型命名规范:

  • 无 instruct:基础模型,进行了预训练
  • 含 instruct:还额外进行了指令对齐等操作

模型结构

示例: “Qwen/Qwen2.5-0.5B-Instruct”

  • Model 是一个嵌入模型+若干 layers
  • lm_head 将模型输出映射回词表
 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
Qwen2ForCausalLM(
  (model): Qwen2Model(
    (embed_tokens): Embedding(151936, 896)
    (layers): ModuleList(
      (0-23): 24 x Qwen2DecoderLayer(
        (self_attn): Qwen2SdpaAttention(
          (q_proj): Linear(in_features=896, out_features=896, bias=True)
          (k_proj): Linear(in_features=896, out_features=128, bias=True)
          (v_proj): Linear(in_features=896, out_features=128, bias=True)
          (o_proj): Linear(in_features=896, out_features=896, bias=False)
          (rotary_emb): Qwen2RotaryEmbedding()
        )
        (mlp): Qwen2MLP(
          (gate_proj): Linear(in_features=896, out_features=4864, bias=False)
          (up_proj): Linear(in_features=896, out_features=4864, bias=False)
          (down_proj): Linear(in_features=4864, out_features=896, bias=False)
          (act_fn): SiLU()
        )
        (input_layernorm): Qwen2RMSNorm()
        (post_attention_layernorm): Qwen2RMSNorm()
      )
    )
    (norm): Qwen2RMSNorm()
  )
  (lm_head): Linear(in_features=896, out_features=151936, bias=False)
)

特殊符号

每一个模型的特殊符号均有差异,一般而言:

一、基础知识

Linux 系统的文件结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/bin        二进制文件,系统常规命令
/boot       系统启动分区,系统启动时读取的文件
/dev        设备文件
/etc        大多数配置文件
/home       普通用户的家目录
/lib        32位函数库
/lib64      64位库
/media      手动临时挂载点
/mnt        手动临时挂载点
/opt        第三方软件安装位置。用户级的程序目录,可以理解为 D:/Sof                                ware opt有可选的意思,这里可以用于放置第三方大型软件(或游戏),当你不需要时,直接rm -rf掉即可
/proc       进程信息及硬件信息
/root       系统管理员,也称作超级权限者的用户主目录。
/sbin       s即Super Usersbin 存放系统管理员使用的系统管理程序。
/srv        一些服务启动之后需要提取的数据
/var        var  variable(变量) 的缩写,习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件。
/sys        内核相关信息
/tmp        临时文件
/run        临时文件系统,存储系统启动以来的信息。当系统重启时,这个目录下的文件应该被清除。
            如果你的系统上有 /var/run 目录,应该让它指向 run
/usr        用户相关设定
/usr/bin    系统用户使用的应用程序。
/usr/sbin   超级用户使用的比较高级的管理程序和系统守护程序。
/usr/src    内核源代码默认的放置目录。
/usr/local :用户级的程序目录,可以理解为 C:/Progrem Files/  用户自己编译的软件默认会安装到这个目录下。

Shell 基础

Shell 快捷键

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
ctrl+a: 将光标跳到行首

ctrl+e : 将光标跳到行尾

ctrl+k : 删除从光标到行尾的部分(还有剪切功能)

ctrl+u : 删除从光标到行首的部分(还有剪切功能)

ctrl+d: 删除从光标到当前单词结尾的部分

ctrl+w: 删除从光标到当前单词开头的部分

ctrl+y:粘贴使用 ctrl+w,ctrl+u 和 ctrl+k 快捷键擦除的文本。如果你删除了错误的文本或需要在某处使用已擦除的文本,这将派上用场。

ctrl+r:使用该快捷键来搜索历史命令

ctrl + l:清屏

先单击 Esc 键,然后再按 b 键: 往回(左)移动一个单词,backward
先单击 Esc 键,然后再按 f 键: 往后(右)移动一个单词,forward

shell 标头的含义

1
2
3
4
5
6
示例:root@app00:~# 
root    //用户名,root 为超级用户
@       //分隔符
app00   //主机名称
~       //当前所在目录,默认用户目录为~
##      //表示当前用户是超级用户,普通用户为 $

Shell 输入/输出重定向

  • n > file 将文件描述符为 n 的文件重定向到 file。
  • n » file 将文件描述符为 n 的文件以追加的方式重定向到 file。
  • n >& m 将输出文件 m 和 n 合并。
  • n <& m 将输入文件 m 和 n 合并。
  • « tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入。

程序输出重定向

#课题 Eclipse Xtext 是用于开发编程语言和领域特定语言的框架,包括解析器、链接器、类型检查器、编译器以及对 Eclipse、任何支持语言服务器(LSP)协议的编辑器和您最喜欢的 web 浏览器的编辑支持。

快速入门

创建项目

选项导航:File → New → Project… → Xtext → Xtext project 填入如下值

keyvalue
Project name:org.example.domainmodel
Language name:org.example.domainmodel.Domainmodel
DSL-File extensions:dmodel

会自动生成如下目录结构

org.example.domainmodelThe grammar definition and all language-specific components (parser, lexer, linker, validation, etc.)
org.example.domainmodel.idePlatform-independent IDE functionality (e.g. services for content assist)
org.example.domainmodel.testsUnit tests for the language
org.example.domainmodel.uiThe Eclipse editor and other workbench related functionality
org.example.domainmodel.ui.testsUI tests for the Eclipse editor

一些概念

Inject

Xtext 框架使用 com.google.inject 库进行依赖注入,注册不同的语言服务(如 GrammarAccess,Formatter)

原文地址 zhuanlan.zhihu.com

UML 图有很多种,但是并非必须掌握所有的 UML 图,才能完整系统分析和设计工作。一般说来,在 UML 图中,只要掌握类图、用例图、时序图的使用,就能完成大部分的工作。也就是说,掌握 UML 的 20%,就能做 80% 的事情。对于程序员来说,最频繁使用的莫过于类图。因此,这里我们只讲解 UML 类图。至于其它 UML 图,请在以后的工作中参阅更多 UML 学习资料继续学习。

类图是面向对象系统建模中最常用和最重要的图,是定义其它图的基础。类图主要是用来显示系统中的类、接口以及它们之间的静态结构和关系的一种静态模型。类图中最基本的元素是类、接口。软件设计师设计出类图后,程序员就可以用代码实现类图中包含的内容。

类图中具体类、抽象、接口和包的表示法


UML 类图中具体类、抽象类、接口和包有不同的表示方法。

1)在 UML 类图中表示具体类

具体类在类图中用矩形框表示,矩形框分为三层:第一层是类名字。第二层是类的成员变量;第三层是类的方法。成员变量以及方法前的访问修饰符用符号来表示:

  • “+” 表示 public
  • “-” 表示 private
  • “#” 表示 protected
  • 不带符号表示 default

600

2)在 UML 类图中表示抽象类

抽象类在 UML 类图中同样用矩形框表示,但是抽象类的类名以及抽象方法的名字都用斜体字表示,如图 2 所示。

600

3)在 UML 类图中表示接口

接口在类图中也是用矩形框表示,但是与类的表示法不同的是,接口在类图中的第一层顶端用构造型 <<interface>> 表示,下面是接口的名字,第二层是方法,如图 3 所示。此外,接口还有另一种表示法,俗称棒棒糖表示法,就是类上面的一根棒棒糖(圆圈 + 实线)。圆圈旁为接口名称,接口方法在实现类中出现。

600

容器化

容器化:将一个程序所需的运行库、依赖等打包到一起,以便可以到处运行

  • 容器化 vs 虚拟机:容器化不含操作系统,且资源占用小
  • 容器化案例:一个网站,前端 Vue 后端 Spring+MySQL,Docker 可以将 NodeJS、Java、Spring、MySQL 等通通打包到一个镜像中直接运行

用途:

  • 解决开发环境的配置问题
  • 实现一键部署

Docker 体系结构

  • 镜像:相当于类,只读
  • 容器:镜像的实例
  • docker-daemon:处理客户端的请求,并返回结果 L|800

镜像

镜像中包含以下内容:

  • 精简版操作系统
  • 运行时环境:如 NodeJS、Java
  • 应用程序、应用程序的第三方依赖库
  • 环境变量

镜像数据持久化

  • 默认不会储存数据,容器关闭时会删除容器运行时创建的所有文件
  • 指定容器卷 DockerVolume,将容器目录映射到宿主目录,从而实现持久化

Docker Compose

概述:一个程序可能需要用到一组相互关联的应用程序,如前后端、数据库。这些程序分别部署在不同的 Docker 镜像中,而 DockerCompose 用于解决关联启动的问题,实现一键启动所有服务

  • Docker Compose 用于定义和运行多容器应用程序
  • 使用 docker-compose.yml 配置

使用 Docker

安装与配置

配置镜像

  1. Linux 环境下安装的 docker:
1
$ sudo vi /etc/docker/daemon.json

添加 registry-mirrors,这里只使用了 Docker 中国区的镜像,若没有上述文件新建一个即可:

1
2
3
{
 "registry-mirrors": ["https://registry.docker-cn.com"]
}

Docker Desktop:找到 Docker Destop -> Preferences -> Docker Engine,添加镜像配置即可 L|800

目的

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

示例

GUI 中的组件 Tips:用户界面对象中会有一个对象来处理帮助请求,至于是哪一个对象则取决于上下文以及可用的帮助具体到何种程度。

  • 问题:提交帮助请求的对象(如按钮)并不明确知道谁是最终提供帮助的对象,需要有一种办法将提交帮助请求的对象与可能提供帮助信息的对象解耦(decouple)
  • 职责链:将可选的处理对象排成一条链,链中对象收到请求的对象要么亲自处理它,要么转发给链中的下一个候选者。
    • 要求:每个在链上的对象都有一致的处理请求访问链上后继者的接口 按钮、对话框和应用类都使用 HelpHandler 操作来处理帮助请求。 HelpHandler 的 HandleHelp 操作默认是将请求转发给后继。

适用性

  • 有多个对象可以处理一个请求,哪个对象处理该请求运行时自动确定。
  • 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  • 可处理一个请求的对象集合应被动态指定。

结构

  • Handler:定义处理请求的接口
    • 可选:实现后继链(successor)
  • ConcreteHandler:处理请求,访问后继对象
    • 若能处理就处理,否则就将请求转发给后继
  • Client:向职责链上的 ConcreteHandler 提交请求 当客户提交一个请求时,请求沿链传递直至有一个 ConcreteHandler 对象负责处理它。

请求传递过程可能如下

优缺点

  • 降低耦合度:该模式使得一个对象无须知道是其他哪一个对象处理其请求。
    • 接收者和发送者都没有对方的明确信息,且链中的对象不需要知道链的结构。
  • 增强了给对象指派职责的灵活性:可以通过在运行时对该链进行动态的增加或修改来增加或改变处理一个请求的那些职责。
  • 不保证处理:既然一个请求没有明确的接收者,那么就不能保证它一定会被处理(可能一直到链尾都没有对象处理请求)

实现

实现后继者链的方法

  • 使用已有的链接:例如,在一个部分–整体层次结构中,父构件引用可定义一个部件的后继者。窗口组件(widget)结构可能早已有这样的链接。
  • 由 Handler 实现:如果没有已有的引用可定义一个链,则必须构建新的链。这种情况下 Handler 不仅定义该请求的接口,通常也维护后继者

表示请求

  1. 请求是一个硬编码的(hard-coded)操作调用(如上述 HelpHandler)
  2. 使用一个处理函数,这个函数以一个请求码(如一个整型常数或一个字符串)为参数
    1. 无法用类型安全的方法来传递请求参数,因此它们必须被手工打包和解包。显然,相对于直接调用一个操作来说它不太安全。

解决上述参数传递问题

  • 使用独立的请求对象来封装请求参数
  • Request 可定义一个访问器(accessor)函数以返回该类的标识符(对于支持的语言,可使用运行时的类型信息)
  • 子类可通过重定义 HandleRequest 扩展该分派函数。子类只处理它感兴趣的请求,其他的请求被转发给父类。

相关模式

职责链常与 Composite(4.3)一起使用。这种情况下,一个构件的父构件可作为它的后继。

55. 跳跃游戏

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。

核心策略:看覆盖范围,覆盖范围内⼀定是可以跳过来的,不⽤管是怎么跳的。问题就转化为跳跃覆盖范围究竟可不可以覆盖到终点。

45. 跳跃游戏 II

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

  • 0 <= j <= nums[i] 
  • i + j < n

返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例保证可以到达 nums[n - 1]

核心策略:同样看覆盖范围,当前下标达到了上次跳跃的最大位置,且未达到终点,则需要跳跃一次。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
if (nums.size() == 1)
  return 0;
int max_pos = 0, last_max_pos = 0, cnt = 0, target = nums.size() - 1;

for (int i = 0; i <= target; ++i) {
  // 跳跃的情况:当前下标达到了上次跳跃的最大位置,且未达到终点,则需要跳跃一次
  max_pos = max(max_pos, i + nums[i]);
  if (i == last_max_pos) {
    ++cnt;
    last_max_pos = max_pos;
    if (last_max_pos >= target)
      break;
  }
}

134. 加油站

1
2
3
情况⼀:如果 gas 的总和⼩于 cost 总和,那么⽆论从哪⾥出发,⼀定是跑不了⼀圈的
情况⼆:rest[i] = gas[i]-cost[i]为⼀天剩下的油,i 从 0 开始计算累加到最后⼀站,如果累加没有出现负数,说明从 0 出发,油就没有断过,那么 0 就是起点。
情况三:如果累加的最⼩值是负数,汽⻋就要从⾮0 节点出发,从后向前,看哪个节点能把这个负数填平,能把这个负数填平的节点就是出发节点。

376. 摆动序列

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

用途

运用共享技术支持大量细粒度的对象节约空间

示例

面向对象中创建一个对象的开销是相对较大的,有时这是难以承受的。例如文本编辑器若将每个字符均采用一个相应的对象描述将产生巨大的内存负担

Flyweight 模式描述了如何共享对象,使得可以细粒度地使用它们而不需要高昂的代价

  • 说明:flyweight 是一个共享对象,它可以同时在多个场景/上下文(context)中使用,并且在每个场景中 flyweight 都可以作为一个独立的对象
    • 关于内部与外部状态:
      • flyweight 存储独立于上下文的信息,在每个场景中使用
      • 有关上下文的外部状态由用户在合适的时候提供
  • Flyweight 模式对那些通常由于数量太大而难以用对象来表示的概念或实体进行建模
    • 例如文本编辑器中:
      • 逻辑上每个字符对应一个对象
      • 物理上每种字符共享同一个 Flyweight 对象(存储在 Flyweight 对象池),只存储字符编码,不存储相应的图元位置和字体等信息
      • 行对象绘制字符对象时传递位置、字体等信息

适用性

条件全部成立时使用:

  • 一个应用程序使用了大量的对象,完全由于使用大量的对象造成很大的存储开销。
  • 对象的大多数状态都可变为外部状态。
  • 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
  • 应用程序不依赖于对象标识(共享对象 a equal b 会返回真值)。

结构

  • FlyweightFactory:创建、管理、共享 Flyweight 对象。(采用懒加载创建)
  • Flyweight(示例中的 Glyph):描述接口,通过这个接口 flyweight 可以接受并作用于外部状态。
  • ConcreteFlyweight(示例中的 Character):(该对象必须是可共享的)实现 Flyweight 接口,并为内部状态(如果有的话)分配存储空间。
  • UnsharedConcreteFlyweight(示例中的 Row、Column):并非所有的 Flyweight 子类都需要被共享
  • Client
    • 引用 Flyweight
    • 计算/存储 Flyweight 的外部状态
    • 用户不应直接对 ConcreteFlyweight 类进行实例化,而只能从 FlyweightFactory 对象得到 ConcreteFlyweight 对象,这可以保证对它们适当地进行共享。

优缺点

  • 增大时间开销:传输、查找和/或计算外部状态都会产生运行时开销
  • 减少空间开销:
    • 共享的 flyweight 越多,存储节约也就越多。
    • 节约量随着共享状态的增多而增大。
    • 当对象使用大量的内部及外部状态,并且外部状态是计算出来的而非存储的时候,节约量将达到最大。

Flyweight 模式经常和 Composite(4.3)模式结合起来表示一个层次式结构,这一层次式结构是一个共享叶结点的图。

目的

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

示例

考虑解析简单的正则表达式,包含如下文法:

  • expression ::= literal | alternation | sequence | repretition | ’ (’ expression ‘)’
  • alternation ::= expression ‘|’ expression
  • sequence ::= expression ‘&’ expression
  • repetition ::= expression ‘*’
  • literal ::= ‘a’ | ‘b’ | ‘c’… ( ‘a’ | ‘b’ | ‘c’…)*

使用类去描述上述文法:

  • 每个用这个文法定义的正则表达式都被表示为一个由这些类的实例构成的抽象语法树 AST
  • 如果我们为 RegularExpression 的每一子类都定义解释(Interpret) 操作,那么就得到了这些正则表达式的一个解释器。

适用性

当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。

最好具备以下特性:

  • 文法简单。对于复杂的文法,文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无须构建抽象语法树即可解释表达式,这样可以节省空间而且还可能节省时间。
  • 不太在意效率。最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下,转换器也可用解释器模式实现,该模式仍是有用的。

结构

  • AbstractExpression:定义抽象的解释操作
  • TerminalExpression:(终结符表达式,如 LiteralExpression)
    • 定义终结符的解释操作
    • 终结符:指的是文法中的运算单元,也就是不可再分的最小元素。
    • 文法中的每一个终结符都有一个具体终结表达式与之相对应。
  • NonterminalExpression(非终结符表达式,如 AlternationExpression、Repetition-Expression、SequenceExpressions):
    • 文法中的每一条规则 R::=R1 R2…Rn 都需要一个 NonterminalExpression 类
    • 为从 R1 到 Rn 的每个符号都维护一个 AbstractExpression 类型的实例变量。
  • Context(上下文):解释器之外的全局信息
  • Client(客户):构建(或被给定)表示该文法定义的语言中一个特定的句子的抽象语法树。
    • 最后调用解释操作

工作流程:Client 构建(或被给定)一个句子,它是 NonterminalExpression 和 TerminalExpression 的实例的一个抽象语法树。然后初始化上下文并调用解释操作。

定义和组成要素

什么是模式:每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。

模式的组成要素

  • 模式名
  • 问题:何时使用该模式
  • 解决方案:描述设计的组成成分、相互关系和协作方式
  • 影响:描述模式应当权衡的问题,对系统的灵活性、扩充性或可移植性的影响。如时空复杂度、语言和实现

由 MVC, Model-View-Controller 理解“模式”:

  • 什么是 MVC:
    • 模型:应用对象
    • 视图:对象在屏幕上的显示
    • 控制器:定义用户界面对用户输入的响应方式
    • MVC 通过建立订阅-通知协议来分离视图和模型。一旦模型的数据发生变化,模型将通知有关的视图,每个视图相应地得到刷新自己的机会。允许为一个模型提供不同的多个视图表现形式
  • 推广 1:模型和视图分离→订阅对象和发布对象分离
    • 将对象分离,使得一个对象的改变能够影响另一些对象,而这个对象并不需要知道那些被影响的对象的细节。这个更一般的设计被描述成 Observer
  • 推广 2:MVC 允许视图嵌套,将组合 View 与单个 View 平等对待→将对象组视为一般对象
    • 将一些对象划为一组,并将该组对象当作一个对象来使用。这个设计被描述为 Composite

设计模式的组成要素

  • 模式名、别名和分类
  • 意图:解决什么问题
  • 动机:说明一个设计问题以及如何用模式中的类、对象来解决该问题的特定情景
  • 适用性
  • 结构:类图和交互图(描述对象之间的请求序列和协作关系)
  • 参与者:涉及的类和/或对象以及它们各自的职责。
  • 协作:模式的参与者怎样协作以实现它们的职责。
  • 影响
  • 实现:实现时的注意事项
  • 实际应用案例
  • 相关模式

设计模式目录

  • 创建型模式与对象的创建有关;

  • 结构型模式处理类或对象的组合;

  • 行为型模式对类或对象怎样交互和怎样分配职责进行描述。

  • 类模式:处理类和子类的关系

  • 对象模式:处理对象间的关系

设计模式中可变的部分

  1. Abstract Factory(3.1):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
  2. Adapter(4.1):将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
  3. Bridge(4.2):将抽象部分与它的实现部分分离,使它们都可以独立地变化。
  4. Builder(3.2):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
  5. Chain of Responsibility(5.1):解除请求的发送者和接收者之间的耦合,使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。Command(5.2):将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
  6. Composite(4.3):将对象组合成树形结构以表示“部分–整体”的层次结构。Composite 使得客户对单个对象和组合对象的使用具有一致性。
  7. Decorator(4.4):动态地给一个对象添加一些额外的职责。就扩展功能而言,Decorator 模式比生成子类方式更为灵活。
  8. Facade(4.5):为子系统中的一组接口提供一个一致的界面, Facade 模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
  9. Factory Method(3.3):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method 使一个类的实例化延迟到其子类。
  10. Flyweight(4.6):运用共享技术有效地支持大量细粒度的对象。
  11. Interpreter(5.3):给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。Iterator(5.4):提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
  12. Mediator(5.5):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
  13. Memento(5.6):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。
  14. Observer(5.7):定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
  15. Prototype(3.4):用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
  16. Proxy(4.7):为其他对象提供一个代理以控制对这个对象的访问。
  17. Singleton(3.5):保证一个类仅有一个实例,并提供一个访问它的全局访问点。State(5.8):允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。
  18. Strategy(5.9):定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。
  19. Template Method(5.10):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method 使得子类不改变一个算法的结构即可重定义该算法的某些特定步骤。
  20. Visitor(5.11):表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

面向对象设计的步骤

设计原则

对接口编程,而不是对实现编程。这可以极大地减少子系统实现之间的相互依赖关系: