设计模式三大分类以及六大原则
设计模式三大分类
设计模式分为三种类型,共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); Future<bool > delete(String key, {String? subKey}); Future<bool > clearExpired(); Future<bool > clearAll(); } class CacheManager { CacheConfig _config; ICacheStore? _diskCacheStore; ICacheStore? _memoryCacheStore; 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; } } class CacheNetworkClient extends NetworkClient { 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 ``` [设计模式六大原则(五)----迪米特法则](https://cloud.tencent.com/developer/article/1836752) 实施迪米特法则:做到两点就足够了: 1.只和直接的朋友交流; 2.减少对朋友的了解。 什么是直接的朋友呢?出现在**成员变量**、**方法的输入输出参数**中的类就是直接的朋友。 减少对朋友的了解:**换作在一个类中,就是尽量减少一个类对外暴露的方法。** CacheManager是NetworkClient的好友,CacheManager只需提供pushToCache公共方法,不用提供pushToDiskCache、pushToMemoryCache这两个私有方法。 ### 1、开闭原则(O:Open Close Principle)-- 总则 开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。 eg:在你有新的需求的时候,最好不要轻易修改老功能,你应当增加新的对象来实现,而不是修改原来的对象 开闭原则:对扩展开发,对修改关闭。 在代码层面而言就是**在你有新的需求的时候,你应当增加新的对象来实现,而不是修改原来的对象。**
设计模式六大原则(6):开闭原则 定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
问题由来:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。 解决方案:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。 举例: 当有新需求时,如果增加功能涉及老功能点某些地方,最好不要轻易修改老功能,最好重新新增相应的功能,不然一些忽略的细节可能就会造成隐患。
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 [设计模式六大原则(六)----开闭原则](https://cloud.tencent.com/developer/article/1836753) 实施开放封闭原则的基本思路:让类xxxStore依赖于固定的抽象ICacheStore,所以对修改是封闭的;而通过面向对象的继承和多态机制,可以实现对抽象体的继承,通过覆写其方法来改变固有行为,实现新的扩展方法,所以对于扩展就是开放的。 ### 2、依赖倒置原则(D:Dependence Inversion Principle)-- 手段 **原则定义:** 要依赖抽象(接口),不要依赖具体实现(类)。 依赖倒置原则告诉我们要面向接口编程;通过抽象(接口或抽象类)使各个类或模块实现彼此独立,互不影响,实现模块间的松耦合。 定义: 高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。//“抽象”指“接口或抽象类”,“细节”指“实现类” eg:Driver开车,依赖的是Driver.drive(BaseCar)。而不能是没有基类的BenchiCar、BMWCar。 [依赖倒置原则(设计模式原则)](https://blog.csdn.net/u013862108/article/details/79054620) ### 3.1、单一职责原则(S:Single Responsibility Principle)-- 类质量 单一职责原则告诉我们实现类要职责单一; eg:UserService专做用户相关,OrderService专做订单相关 ### 3.2、接口隔离原则(I:Interface Segregation Principle)-- 类质量 “隔离”指的是**“客户端的角色”** 接口隔离原则告诉我们在设计接口的时候要精简单一; 通俗讲:不要把一大堆方法塞进一个接口里,导致这个接口变得臃肿无比。应该要根据实际需要,让接口中只有用得上的方法,**也就是说要细化我们的接口**。 定义为: > 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. (类间的依赖关系应该建立在最小的接口上。)它要求“最小的接口”,也就是该接口中没有多余的方法,所以**这里的隔离是指和多余的方法隔离。** 管理员只关心‘用户管理’,普通用户只关心‘登录’,游客只关心‘注册’。如果你把这三个方法塞到一个接口里,所有客户端都得依赖另外两个不需要的方法。” ### 4.1、迪米特法则(最少知道原则)-- 低藕 ⑥迪米特法则(最少知道原则),告诉我们要尽量降低类与类之间的耦合; eg:UserService与OrderService之间要降低耦合 ### 4.2、组合复用 -- 低藕 [设计模式之七大原则-合成复用原则(Composite Reuse Principle)](https://juejin.cn/post/6978277541861654558) - 原则是尽量使用合成/聚合的方式,而不是使用继承 该原则是在一个新的对象里面使用一些已有的对象, 使之成为新对象的一部分,新的对象通过向这些对象的委派达到复用已有功能的目的 ### 5、里氏替换原则(L:Liskov Substitution Principle)--继承 **里氏替换原则:子类可以扩展父类的方法,但不应该复写父类的方法。不要破坏继承体系;** > ③里氏替换原则告诉我们不要破坏继承体系; > eg:子类可以扩展父类的方法(扩展出一个showSpeed方法),但不应该复写父类的方法。不要破坏继承体系; 假设有父类如下: ```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" ) } } class BaoMaCar : Car { func showSpeed () { print ("当前行驶速度是80Km/h" ) } }
设计模式六大原则(二)—-里式替换原则
里氏替换原则主要阐述了有关继承的一些原则,即什么时候应该使用继承,什么时候不应该使用继承 ,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范 。
实现里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
里氏替换原则是针对继承而言的。其至少有两种含义: 1、如果继承是为了实现代码重用,也就是为了共享方法,那么共享的父类方法就应该保持不变,不能被子类重新定义。子类只能通过新添加方法来扩展功能,父类和子类都可以实例化,而子类继承的方法和父类是一样的,父类调用方法的地方,子类也可以调用同一个继承得来的,逻辑和父类一致的方法,这时用子类对象将父类对象替换掉时,当然逻辑一致,相安无事。 2、如果继承的目的是为了多态,。。。。
二、常见的设计模式及其应用场景
< 返回目录
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; } }
22种设计模式
1、创建型模式 1.1、单例模式 应用场景:确保程序运行期间,某个类只有一份实例,常用于进行资源共享控制 。
实例:[UIApplication sharedApplication]。
注意事项:确保使用者只能通过 getInstance方法才能获得,单例类的唯一实例。 返回的也只是此单例类的唯一静态变量。
1.2、工厂模式 应用场景:工厂方式创建类的实例,多与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];
显然上面这种方法,在我们需要不同对象时,修改起来更方便点。
工厂方法模式是为每一个要创建的对象所在的类都相应地创建一个工厂
2、结构型模式 2.1、代理模式 应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现。
实例:tableview的datasource和delegate
2.2、适配器模式 设计模式-④适配器、策略、责任链模式.md
3、行为模式 3.1、观察者模式 应用场景:一般为model层对,controller和view进行的通知方式,不关心谁去接收,只负责发布信息。
实例: ①Notification通知中心,注册通知中心,任何位置可以发送消息,注册观察者的对象可以接收。 ②KVO,键值对改变通知的观察者。
模式
iOS 中的应用
订阅方式
观察者模式
KVO(Key-Value Observing)、 Delegate(代理模式也是观察者的一种特殊情况)
观察者直接注册到被观察者/ 被观察者直接注册给观察者
发布-订阅模式
NotificationCenter、Combine、RxSwift
订阅者向事件中心注册
3.2、策略模式 应用场景:定义算法族,封装起来,使他们之间可以相互替换。 优势:使算法的变化独立于使用算法的用户 敏捷原则:接口隔离原则;多用组合,少用继承;针对接口编程,而非实现。 实例:排序算法,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); }
策略模式的其他例子:
策略模式实例
自己TableHomeViewController中的selector
3.3、责任链模式 设计模式-④适配器、策略、责任链模式.md
END