mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-24 20:00:37 +00:00
feature: dyld && LD 链接器
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# 桥接模式
|
||||
|
||||
|
||||
|
||||
## 概念理解
|
||||
|
||||
桥接模式也叫作桥梁模式,英文是 Bridge Design Pattern。这个模式可以说是 23 种设计模式中最难理解的模式之一了。我查阅了比较多的书籍和资料之后发现,对于这个模式有两种不同的理解方式。
|
||||
@@ -84,5 +86,88 @@ public class DriverManager {
|
||||
实际上,JDBC 本身就相当于“抽象”。注意,这里所说的“抽象”,指的并非“抽象类”或“接口”,而是跟具体的数据库无关的、被抽象出来的一套“类库”。具体的Driver(比如,com.mysql.jdbc.Driver)就相当于“实现”。注意,这里所说的“实现”,也并非指“接口的实现类”,而是跟具体数据库相关的一套“类库”。JDBC 和 Driver 独立开发,通过对象之间的组合关系,组装在一起。JDBC 的所有逻辑操作,最终都委托给 Driver 来执行。
|
||||
|
||||
|
||||
|
||||
## 业务解耦
|
||||
|
||||
假设有这样一个场景:业务有个 TableView 列表,版本迭代的时候,后端的接口不变变化,有 V1、V2、V3,这3个版本需要共存,该怎么设计?
|
||||
|
||||
假设列表相关逻辑是 `BaseObjectA`,后端接口相关是 `BaseObjectB`
|
||||
|
||||
`BaseObject1` 是因为需要处理接口 V1、V2、V3 而产生的基类,具体子类 ObjectA1 等处理具体逻辑;
|
||||
|
||||
```objective-c
|
||||
// BaseObjectA
|
||||
@interface BaseObjectA : NSObject
|
||||
// 桥接模式的核心实现
|
||||
@property (nonatomic, strong) BaseObjectB *objB;
|
||||
// 获取数据
|
||||
- (void)handle;
|
||||
@end
|
||||
|
||||
@implementation BaseObjectA
|
||||
/*
|
||||
A1 --> B1、B2、B3 3种
|
||||
A2 --> B1、B2、B3 3种
|
||||
A3 --> B1、B2、B3 3种
|
||||
*/
|
||||
- (void)handle {
|
||||
// override to subclass
|
||||
[self.objB fetchData];
|
||||
}
|
||||
@end
|
||||
|
||||
// ObjectA1
|
||||
@interface ObjectA1 : BaseObjectA
|
||||
|
||||
@end
|
||||
|
||||
@implementation ObjectA1
|
||||
- (void)handle {
|
||||
// before 业务逻辑操作
|
||||
[super handle];
|
||||
// after 业务逻辑操作
|
||||
}
|
||||
@end
|
||||
```
|
||||
|
||||
`BaseObject2` 是处理数据接口相关的基类,`ObjectB1` 是具体 V1 的实现
|
||||
|
||||
```objective-c
|
||||
// BaseObjectB
|
||||
@interface BaseObjectB : NSObject
|
||||
- (void)fetchData;
|
||||
@end
|
||||
|
||||
@implementation BaseObjectB
|
||||
- (void)fetchData {
|
||||
// override to subclass
|
||||
}
|
||||
@end
|
||||
|
||||
@interface ObjectB1 : BaseObjectB
|
||||
|
||||
@end
|
||||
|
||||
@implementation ObjectB1
|
||||
- (void)fetchData {
|
||||
// 具体的逻辑处理
|
||||
}
|
||||
@end
|
||||
```
|
||||
|
||||
使用
|
||||
|
||||
```objective-c
|
||||
ObjectA1 *objA = [[ObjectA1 alloc] init];
|
||||
BaseObjectB *b1 = [[BaseObjectB alloc] init];
|
||||
objA.objB = b1;
|
||||
[objA handle];
|
||||
```
|
||||
|
||||
桥接模式可以解决这种业务兼容的适配问题。
|
||||
|
||||
|
||||
|
||||
## 总结
|
||||
|
||||
桥接模式有两种理解方式。第一种理解方式是“将抽象和实现解耦,让它们能独立开发”。这种理解方式比较特别,应用场景也不多。另一种理解方式更加简单,类似“组合优于继承”设计原则,这种理解方式更加通用,应用场景比较多。
|
||||
@@ -1,14 +1,48 @@
|
||||
# 适配器模式
|
||||
|
||||
主要用来解决:一个现有类需要适应变化的问题。
|
||||
|
||||
|
||||
|
||||
## 适配器模式的原理
|
||||
|
||||
适配器模式的英文翻译是 Adapter Design Pattern。顾名思义,这个模式就是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。举个现实的例子:USB 转接头充当适配器,把两种不兼容的接口,通过转接变得可以一起工作
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 适配器模式的实现
|
||||
适配器模式有两种实现方式:
|
||||
- 类适配器,使用继承关系来实现
|
||||
- 对象适配器,对象适配器使用组合关系来实现
|
||||
|
||||
|
||||
|
||||
### 对象适配器
|
||||
|
||||
<img src="./../assets/ObjectAdapter.png" style="zoom:30%" />
|
||||
|
||||
假设一个类存在年代久远,如果需要适配,则需要创建一个适配对象。然后被适配的对象以成员变量的形式集成到适配对象中。
|
||||
|
||||
|
||||
|
||||
```
|
||||
- (void)handleLogic {
|
||||
// 适配逻辑
|
||||
[被适配对象 对象方法];
|
||||
// 适配逻辑
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
举个例子:
|
||||
|
||||
- ITarget 表示要转化成的接口定义
|
||||
- Adaptee 是一组不兼容 ITarget 接口定义的接口
|
||||
- Adaptor 将 Adaptee 转化成一组符合 ITarget 接口定义的接口
|
||||
@@ -73,9 +107,12 @@ public class Adaptor implements ITarget {
|
||||
|
||||
适配器模式的应用场景是“接口不兼容”。那在实际的开发中,什么情况下才会出现接口不兼容呢?
|
||||
|
||||
|
||||
|
||||
### 封装有缺陷的接口设计
|
||||
|
||||
假设我们依赖的外部系统在接口设计方面有缺陷(比如包含大量静态方法),引入之后会影响到我们自身代码的可测试性。为了隔离设计上的缺陷,我们希望对外部系统提供的接口进行二次封装,抽象出更好的接口设计,这个时候就可以使用适配器模式了。
|
||||
```
|
||||
```java
|
||||
public class CD { //这个类来自外部sdk,我们无权修改它的代码
|
||||
//...
|
||||
public static void staticFunction1() { //... }
|
||||
@@ -108,12 +145,15 @@ public class CDAdaptor extends CD implements ITarget {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 统一多个类的接口设计
|
||||
|
||||
某个功能的实现依赖多个外部系统(或者说类)。通过适配器模式,将它们的接口适配为统一的接口定义,然后我们就可以使用多态的特性来复用代码逻辑。
|
||||
|
||||
假设我们的系统要对用户输入的文本内容做敏感词过滤,为了提高过滤的召回率,我们引入了多款第三方敏感词过滤系统,依次对用户输入的内容进行过滤,过滤掉尽可能多的敏感词。但是,每个系统提供的过滤接口都是不同的。这就意味着我们没法复用一套逻辑来调用各个系统。这个时候,我们就可以使用适配器模式,将所有系统的接口适配为统一的接口定义,这样我们可以复用调用敏感词过滤的代码。
|
||||
|
||||
```
|
||||
```java
|
||||
public class ASensitiveWordsFilter { // A敏感词过滤系统提供的接口
|
||||
//text是原始文本,函数输出用***替换敏感词之后的文本
|
||||
public String filterSexyWords(String text) {
|
||||
@@ -178,10 +218,13 @@ public class RiskManagement {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 替换依赖的外部系统
|
||||
|
||||
当我们把项目中依赖的一个外部系统替换为另一个外部系统的时候,利用适配器模式,可以减少对代码的改动。
|
||||
|
||||
```
|
||||
```java
|
||||
// 外部系统A
|
||||
public interface IA {
|
||||
//...
|
||||
@@ -218,17 +261,26 @@ public class BAdaptor implemnts IA {
|
||||
Demo d = new Demo(new BAdaptor(new B()));
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 兼容老版本
|
||||
|
||||
在做版本升级的时候,对于一些要废弃的接口,我们不直接将其删除,而是暂时保留,并且标注为 deprecated,并将内部实现逻辑委托为新的接口实现。这样做的好处是,让使用它的项目有个过渡期,而不是强制进行代码修改。这也可以粗略地看作适配器模式的一个应用场景
|
||||
|
||||
|
||||
|
||||
### 适配不同格式的数据
|
||||
|
||||
前面我们讲到,适配器模式主要用于接口的适配,实际上,它还可以用在不同格式的数据之间的适配。比如,把从不同征信系统拉取的不同格式的征信数据,统一为相同的格式,以方便存储和使用。再比如,Java 中的 Arrays.asList() 也可以看作一种数据适配器,将数组类型的数据转化为集合容器类型。
|
||||
|
||||
```
|
||||
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 代理、桥接、装饰器、适配器 4 种设计模式的区别
|
||||
|
||||
代理、桥接、装饰器、适配器,这 4 种模式是比较常用的结构型设计模式。它们的代码结构非常相似。笼统来说,它们都可以称为 Wrapper 模式,也就是通过 Wrapper 类二次封装原始类
|
||||
|
||||
尽管代码结构相似,但这 4 种设计模式的用意完全不同,也就是说要解决的问题、应用场景不同,这也是它们的主要区别
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
# 职责链模式
|
||||
# 责任链模式
|
||||
|
||||
> 经常变化的产品需求该怎么设计?一个人关于需求变更问题的解决方案
|
||||
|
||||
|
||||
|
||||
## 定义
|
||||
职责链模式的英文翻译是 Chain Of Responsibility Design Pattern。在 GoF 的《设计模式》中,它是这么定义的:
|
||||
责任链模式的英文翻译是 Chain Of Responsibility Design Pattern。在 GoF 的《设计模式》中,它是这么定义的:
|
||||
> Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
|
||||
|
||||
翻译成中文就是:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止
|
||||
@@ -165,7 +169,12 @@ public class Application {
|
||||
```
|
||||
在 GoF 给出的定义中,如果处理器链上的某个处理器能够处理这个请求,那就不会继续往下传递请求。实际上,职责链模式还有一种变体,那就是请求会被所有的处理器都处理一遍,不存在中途终止的情况。
|
||||
|
||||
## 使用场景
|
||||
在之前的文章利用[责任链模式设计了一套校验器](./../Chapter1%20-%20iOS/1.110.md)
|
||||
再举个例子敏感词过滤的例子。
|
||||
|
||||
|
||||
## 使用场景
|
||||
|
||||
在之前的文章利用[责任链模式设计了一套校验器](./../Chapter1%20-%20iOS/1.110.md)
|
||||
|
||||
- Node 的洋葱模型
|
||||
- Redux 中间件思想都是责任链的使用场景
|
||||
- iOS 事件传递机制、响应链
|
||||
|
||||
@@ -8,12 +8,18 @@
|
||||
|
||||
命令模式将请求(命令)封装为一个对象,这样可以使用不同的请求参数化其他对象(将不同请求依赖注入到其他对象),并且能够支持请求(命令)的排队执行、记录日志、撤销等(附加控制)功能
|
||||
|
||||
|
||||
|
||||
## 使用场景
|
||||
|
||||
落实到编码实现,命令模式用的最核心的实现手段,是将函数封装成对象。我们知道,C 语言支持函数指针,我们可以把函数当作变量传递来传递去。但是,在大部分编程语言中,函数没法儿作为参数传递给其他函数,也没法儿赋值给变量。借助命令模式,我们可以将函数封装成对象。具体来说就是,设计一个包含这个函数的类,实例化一个对象传来传去,这样就可以实现把函数像对象一样使用。从实现的角度来说,它类似我们之前讲过的回调
|
||||
|
||||
当我们把函数封装成对象之后,对象就可以存储下来,方便控制执行。所以,命令模式的主要作用和应用场景,是用来控制命令的执行,比如,异步、延迟、排队执行命令、撤销重做命令、存储命令、给命令记录日志等等,这才是命令模式能发挥独一无二作用的地方。
|
||||
|
||||
|
||||
|
||||
## 实现
|
||||
|
||||
假设我们正在开发一个类似《天天酷跑》或者《QQ 卡丁车》这样的手游。这种游戏本身的复杂度集中在客户端。后端基本上只负责数据(比如积分、生命值、装备)的更新和查询,所以,后端逻辑相对于客户端来说,要简单很多。
|
||||
|
||||
为了提高性能,我们会把游戏中玩家的信息保存在内存中。在游戏进行的过程中,只更新内存中的数据,游戏结束之后,再将内存中的数据存档,也就是持久化到数据库中。为了降低实现的难度,一般来说,同一个游戏场景里的玩家,会被分配到同一台服务上。这样,一个玩家拉取同一个游戏场景中的其他玩家的信息,就不需要跨服务器去查找了,实现起来就简单了很多。
|
||||
@@ -78,7 +84,17 @@ public class GameApplication {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
命令模式一般有2个角色:
|
||||
|
||||
- CommandManager
|
||||
- Command
|
||||
|
||||
|
||||
|
||||
## 命令模式 VS 策略模式
|
||||
|
||||
命令模式跟策略模式、工厂模式非常相似啊,那它们的区别在哪里呢?
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
单例与静态类的区别?
|
||||
有何替代的解决方案?
|
||||
|
||||
|
||||
|
||||
## 为什么要使用单例?
|
||||
|
||||
创建型模式主要解决对象的创建问题,封装复杂的创建过程,解耦对象的创建代码和使用代码。其中单例模式、工厂模式、建造者模式、原型模式都是创建型模式。
|
||||
@@ -207,3 +209,34 @@ SingletonHolder 是一个静态内部类,当外部类 IdGenerator 被加载的
|
||||
## 如何理解单例模式的唯一性
|
||||
“一个类只允许创建唯一一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。定义中提到,“一个类只允许创建唯一一个对象”。那对象的唯一性的作用范围是什么呢?是指线程内只允许创建一个对象,还是指进程内只允许创建一个对象?答案是后者,也就是说,单例模式创建的对象是进程唯一的
|
||||
|
||||
|
||||
|
||||
## iOS 侧单例的实现
|
||||
|
||||
- GCD `dispatch_once` 保证执行1次
|
||||
|
||||
- 实现 `+ (id)allocWithZone:(struct _NSZOne *)zone { } ` 方法。避免单例对象,调用 copy 方法,产生一个新的对象,打破单例效果
|
||||
|
||||
实现 `- (id)copyWithZone:(nullable NSZone *)zone { }` 方法,避免单例对象,调用 copy 方法,产生一个新的对象,打破单例效果
|
||||
|
||||
|
||||
|
||||
```objective-c
|
||||
+ (id)sharedInstance {
|
||||
static Mooc *instance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[super allocWithZone:NULL] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
+ (id)allocWithZone:(struct _NSZone *)zone {
|
||||
return [self sharedInstance];
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(nullable NSZone *)zone {
|
||||
return self;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
* [20、观察者模式](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter6%20-%20Design%20Pattern/6.20.md)
|
||||
* [21、模板模式](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter6%20-%20Design%20Pattern/6.21.md)
|
||||
* [22、模板模式](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter6%20-%20Design%20Pattern/6.22.md)
|
||||
* [23、职责链模式](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter6%20-%20Design%20Pattern/6.23.md)
|
||||
* [23、责任链模式](https://github.com/FantasticLBP/knowledge-kit/blob/master/Chapter6%20-%20Design%20Pattern/6.23.md)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user