设计模式-①概览

[Toc]

设计模式三大分类以及六大原则

  1. 设计模式三大分类

设计模式分为三种类型,共23类。

(1)创建型模式:单例模式抽象工厂模式、建造者模式、工厂模式、原型模式。

(2)结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式

(3)行为型模式:模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式职责链模式、访问者模式。

一、设计模式的六大原则/SOLID原则

使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。

六大原则在代码中的设计体现

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
// 里氏替换原则:应用程序中任何父类对象出现的地方,我们都可以用其子类的对象来替换,并且可以保证原有程序的逻辑行为和正确性。因为这里父类是抽象类,所以肯定遵守里氏替换原则。
abstract class ICacheStore {
ICacheStore();

Future<CacheObj?> getCacheObj(String key, {String? subKey});
Future<bool> setCacheObj(CacheObj obj); // 接口隔离原则:告诉我们在设计接口的时候要精简单一; get set分为两个接口,不要一个接口用type
Future<bool> delete(String key, {String? subKey});
Future<bool> clearExpired();
Future<bool> clearAll();
}


class CacheManager {
CacheConfig _config;
ICacheStore? _diskCacheStore; // 依赖倒置原则:CacheManager高层模块依赖于cache抽象(接口或抽象类),而不是具体实现
ICacheStore? _memoryCacheStore; // 单一职责原则

// 迪米特法则(又叫做最少知识原则)中的第2点减少对朋友的了解,即尽量减少一个类对外暴露的方法。
Future<bool> pushToCache(CacheObj obj) {
return _getCacheFutureResult(_memoryCacheStore, _diskCacheStore,
_memoryCacheStore?.setCacheObj(obj), _diskCacheStore?.setCacheObj(obj));
}

Future<bool> _getCacheFutureResult(
ICacheStore? memoryCacheStore, // 开闭原则:对扩展开放,对修改封闭. 不同的保存用不同的store,不影响原本的store。实施开放封闭原则的基本思路:让类xxxStore依赖于固定的抽象ICacheStore,所以对修改是封闭的;而通过面向对象的继承和多态机制,可以实现对抽象体的继承,通过覆写其方法来改变固有行为,实现新的扩展方法,所以对于扩展就是开放的。
ICacheStore? diskCacheStore,
Future<bool>? memoryCacheFuture,
Future<bool>? diskCacheFuture) async {
var result1 = (null == memoryCacheStore) ? true : (await memoryCacheFuture!);
var result2 = (null == diskCacheStore) ? true : (await diskCacheFuture!);
return result1 && result2;
}
}

class CacheNetworkClient extends NetworkClient {
// 迪米特法则(又叫做最少知识原则)中的第1点只和直接的朋友交流
DioCacheManager? _dioCacheManager;
}
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
```





[设计模式六大原则(六)----开闭原则](https://cloud.tencent.com/developer/article/1836753)

实施开放封闭原则的基本思路:让类xxxStore依赖于固定的抽象ICacheStore,所以对修改是封闭的;而通过面向对象的继承和多态机制,可以实现对抽象体的继承,通过覆写其方法来改变固有行为,实现新的扩展方法,所以对于扩展就是开放的。



[设计模式六大原则(二)----里式替换原则](https://cloud.tencent.com/developer/article/1831005)

里氏替换原则主要**阐述了有关继承的一些原则,即什么时候应该使用继承,什么时候不应该使用继承**,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,**是对开闭原则的补充,是对实现抽象化的具体步骤的规范**。

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

里氏替换原则是针对继承而言的。其至少有两种含义:
1、如果继承是为了实现代码重用,也就是为了共享方法,那么共享的父类方法就应该保持不变,不能被子类重新定义。子类只能通过新添加方法来扩展功能,父类和子类都可以实例化,而子类继承的方法和父类是一样的,父类调用方法的地方,子类也可以调用同一个继承得来的,逻辑和父类一致的方法,这时用子类对象将父类对象替换掉时,当然逻辑一致,相安无事。
2、如果继承的目的是为了多态,。。。。



[设计模式六大原则(五)----迪米特法则](https://cloud.tencent.com/developer/article/1836752)

实施迪米特法则:做到两点就足够了: 1.只和直接的朋友交流; 2.减少对朋友的了解。

什么是直接的朋友呢?出现在**成员变量**、**方法的输入输出参数**中的类就是直接的朋友。

减少对朋友的了解:**换作在一个类中,就是尽量减少一个类对外暴露的方法。**

CacheManager是NetworkClient的好友,CacheManager只需提供pushToCache公共方法,不用提供pushToDiskCache、pushToMemoryCache这两个私有方法。





### 1、单一职责原则(S:Single Responsibility Principle)

>①单一职责原则告诉我们实现类要职责单一;
>eg:UserService专做用户相关,OrderService专做订单相关
>
>
>
>②依赖倒置原则告诉我们要面向接口编程;通过抽象(接口或抽象类)使各个类或模块实现彼此独立,互不影响,实现模块间的松耦合。
>
>定义: 高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。//“抽象”指“接口或抽象类”,“细节”指“实现类”
>
>eg:Driver开车,依赖的是Driver.drive(BaseCar)。而不能是没有基类的BenchiCar、BMWCar。
>
>[依赖倒置原则(设计模式原则)](https://blog.csdn.net/u013862108/article/details/79054620)
>
>
>
>③里氏替换原则告诉我们不要破坏继承体系;
>eg:子类可以扩展父类的方法(扩展出一个showSpeed方法),但不应该复写父类的方法。不要破坏继承体系;
>
>
>
>④开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。
>eg:在你有新的需求的时候,最好不要轻易修改老功能,你应当增加新的对象来实现,而不是修改原来的对象
>
>
>
>⑤接口隔离原则告诉我们在设计接口的时候要精简单一;
>
>通俗讲:不要把一大堆方法塞进一个接口里,导致这个接口变得臃肿无比。应该要根据实际需要,让接口中只有用得上的方法,**也就是说要细化我们的接口**。
>
>定义为:
>
>> 1. Clients should not be forced to depend upon interfaces that they don’t use. (客户端不应该依赖它不需要的接口。)
>> 2. The dependency of one class to another one should depend on the smallest possible interface. (类间的依赖关系应该建立在最小的接口上。)它要求“最小的接口”,也就是该接口中没有多余的方法,所以**这里的隔离是指和多余的方法隔离。**
>
>
>
>⑥迪米特法则(最少知道原则),告诉我们要尽量降低类与类之间的耦合;
>eg:UserService与OrderService之间要降低耦合

### 2、开闭原则(O:Open Close Principle)

开闭原则:对扩展开发,对修改关闭。

在代码层面而言就是**在你有新的需求的时候,你应当增加新的对象来实现,而不是修改原来的对象。**

设计模式六大原则(6):开闭原则
定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

问题由来:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。
解决方案:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
举例:
当有新需求时,如果增加功能涉及老功能点某些地方,最好不要轻易修改老功能,最好重新新增相应的功能,不然一些忽略的细节可能就会造成隐患。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

### 3、里氏替换原则(L:Liskov Substitution Principle)

**里氏替换原则:子类可以扩展父类的方法,但不应该复写父类的方法。不要破坏继承体系;**

假设有父类如下:

```swift
import Foundation

class Car {
func run() {
print("汽车跑起来了")
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 错误
class BaoMaCar: Car {
override func run() {
super.run()

print("当前行驶速度是80Km/h")
}
}

// 正确:子类可以扩展父类的方法(扩展出一个showSpeed方法),但不应该复写父类的方法。不要破坏继承体系;
class BaoMaCar: Car {
func showSpeed() {
print("当前行驶速度是80Km/h")
}
}

4、接口隔离原则(I:Interface Segregation Principle)

5、依赖倒置原则(D:Dependence Inversion Principle)

依赖倒置原则告诉我们要面向接口编程;通过抽象(接口或抽象类)使各个类或模块实现彼此独立,互不影响,实现模块间的松耦合。

二、常见的设计模式

< 返回目录

MVC是模型、视图、控制器开发模式,对于iOS SDK,所有的View都是视图层的,它应该独立于模型层,由视图器来控制。所有的用户数据都是模型层,它应该独立于视图。所有的ViewController都是视图器,由它负责控制视图,访问模型数据。

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
class CacheManager {	// 单例模式
CacheConfig _config;
ICacheStore? _diskCacheStore;
ICacheStore? _memoryCacheStore;

late StreamSubscription? _xxxSubscription;
CacheManager(this._config) {
......
_xxxSubscription = eventBus.on<MemoryReleaseEvent>().listen((event) { // 观察者模式

});
}

Future<bool> pushToCache(CacheObj obj) {
return _getCacheFutureResult(_memoryCacheStore, _diskCacheStore,
_memoryCacheStore?.setCacheObj(obj), _diskCacheStore?.setCacheObj(obj));
}

Future<bool> _getCacheFutureResult(
ICacheStore? memoryCacheStore, // 策略模式
ICacheStore? diskCacheStore,
Future<bool>? memoryCacheFuture,
Future<bool>? diskCacheFuture) async {
var result1 = (null == memoryCacheStore) ? true : (await memoryCacheFuture!);
var result2 = (null == diskCacheStore) ? true : (await diskCacheFuture!);
return result1 && result2;
}
}

1、iOS开发中的几种设计模式介绍(应用场景、实例)

iOS开发中的几种设计模式介绍

(一)代理模式

应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现。

实例:tableview的datasource和delegate

(二)观察者模式

应用场景:一般为model层对,controller和view进行的通知方式,不关心谁去接收,只负责发布信息。

实例:
①Notification通知中心,注册通知中心,任何位置可以发送消息,注册观察者的对象可以接收。
②KVO,键值对改变通知的观察者。

(三)MVC模式

应用场景:MVC模式是一种非常古老的设计模式,它通过数据模型,控制器逻辑,视图展示将应用程序进行逻辑划分。

实例:model-即数据模型,view-视图展示,controller进行UI展现和数据交互的逻辑控制。

(四)单例模式

应用场景:确保程序运行期间,某个类只有一份实例,常用于进行资源共享控制

实例:[UIApplication sharedApplication]。

注意事项:确保使用者只能通过 getInstance方法才能获得,单例类的唯一实例。
java,C++中使其没有公有构造函数,私有化并覆盖其构造函数。
object c中,重写allocWithZone方法,保证即使用户用 alloc方法直接创建单例类的实例,
返回的也只是此单例类的唯一静态变量。

(五)策略模式

应用场景:定义算法族,封装起来,使他们之间可以相互替换。
优势:使算法的变化独立于使用算法的用户
敏捷原则:接口隔离原则;多用组合,少用继承;针对接口编程,而非实现。
实例:排序算法,NSArray的sortedArrayUsingSelector;经典的鸭子会叫,会飞案例。
注意事项:1,剥离类中易于变化的行为,通过组合的方式嵌入抽象基类
2,变化的行为抽象基类为,所有可变变化的父类
3,用户类的最终实例,通过注入行为实例的方式,设定易变行为
防止了继承行为方式,导致无关行为污染子类。完成了策略封装和可替换性。

附:Objective C中数组排序几种情况的总结

OC中常用的数组排序有以下几种方法:

①、sortedArrayUsingSelector:;

②、sortedArrayUsingComparator:;

③、sortedArrayUsingDescriptors:

1
2
3
4
5
6
//简单排序
- (void)sortArray() {
NSArray *array = [NSArray arrayWithObjects:@"abc",@"456",@"123",@"789",@"ef", nil];
NSArray *sortedArray = [array sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"排序后:%@",sortedArray);
}

策略模式的其他例子:

(六)工厂模式

应用场景:工厂方式创建类的实例,多与proxy模式配合,创建可替换代理类。
优势:易于替换,面向抽象编程,application只与抽象工厂和易变类的共性抽象类发生调用关系。
敏捷原则:DIP依赖倒置原则
实例:项目部署环境中依赖多个不同类型的数据库时,需要使用工厂配合proxy完成易用性替换
注意事项:项目初期,软件结构和需求都没有稳定下来时,不建议使用此模式,因为其劣势也很明显,
增 加了代码的复杂度,增加了调用层次,增加了内存负担。所以要注意防止模式的滥用。

iOS 设计模式之工厂模式
工厂模式我的理解是:他就是为了创建对象的

创建对象的时候,我们一般是alloc一个对象,如果需要创建100个这样的对象,如果是在一个for循环中还好说,直接一句alloc就行了,但是事实并不那么如意,我们可能会在不同的地方去创建这个对象,那么我们可能需要写100句alloc 了,但是如果我们在创建对象的时候,需要在这些对象创建完之后,为它的一个属性添加一个固定的值,比方说都是某某学校的学生,那么可能有需要多些100行重复的代码了,那么,如果写一个-(void)createObj方法,把创建对象和为对象设置初始的属性值(比如学校属性)写在这个方法里边,那么就是会省事很多,也就是说我们可以alloc 创建对象封装到一个方法里边,直接调用这个方法就可以了,这就是简单工厂方法

如果一个工厂里有两个create创建方法,比如PeopleFactory类工厂同时有createTeacher和createStudent两个方法,现在假设原本创建100个老师的写法是如下所示:

1
2
3
4
5
PeopleFactory *factory = [PeopleFactory alloc] init];
People *people1 = [factory createTeacher];
People *people2 = [factory createTeacher];
People *people3 = [factory createTeacher];
People *people4 = [factory createTeacher];

现在突然想让创建出来的对象是student,而不是老师teacher,那么这时候我们就不得不一个个的把createTeacher替换成createStudent方法了。

1
2
3
4
5
PeopleFactory *factory = [PeopleFactory alloc] init];
People *people1 = [factory createStudent];
People *people2 = [factory createStudent];
People *people3 = [factory createStudent];
People *people4 = [factory createStudent];

但是如果利用工厂模式,我们为每一个要创建的对象所在的类都相应地创建一个工厂,则我们的写法将会变成

1
2
3
4
5
6
7
8
9
10
11
12
13
//创建老师
TeacherFactory *factory = [TeacherFactory alloc] init];
People *people1 = [factory createPeople];
People *people2 = [factory createPeople];
People *people3 = [factory createPeople];
People *people4 = [factory createPeople];

//创建学生
StudentFactory *factory = [StudentFactory alloc] init];
People *people1 = [factory createPeople];
People *people2 = [factory createPeople];
People *people3 = [factory createPeople];
People *people4 = [factory createPeople];

显然上面这种方法,在我们需要不同对象时,修改起来更方便点。

工厂方法模式是为每一个要创建的对象所在的类都相应地创建一个工厂

END