对于部分设计模式的个人理解

定义

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。 使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。

设计模式的六大原则

  • 单一职责原则(Single responsibility principle)
    应该有且仅有一个原因引起类的变更(There should never be more than one reason for a class to change)
    单一职责原则为我们提供了一个编写程序的准则,要求我们在编写类,抽象类,接口时,要使其功能职责单一纯碎,将导致其变更的因素缩减到最少。

  • 开闭原则(Open Close Principle)
    开闭原则要求我们要尽可能的通过保持原有代码不变添加新代码而不是通过修改已有的代码来实现软件产品的变化
    说的通俗一点就是,已经开发好的软件实体(如类、模块、函数),在升级迭代引入新功能时,不应该修改已有的代码,而是在已有代码的基础上,添加新代码来实现。

  • 里氏代换原则(Liskov Substitution Principle)
    任何使用基类的地方都可以在不了解其子类具体实现的情况下,无条件地使用其子类替换,而不会产生任何错误或异常。
    这要求子类可以拓展父类的功能,但不能改变父类原有的功能。包含以下4个方面:
    1、子类必须实现父类的抽象方法,但是不能重写父类的非抽象方法。
    2、子类可以增加自己特有的方法
    3、当子类重载父类的方法时,方法的形参要比父类更宽松
    4、当子类重载父类的方法时,方法的返回值要比父类更严格

  • 依赖倒转原则(Dependence Inversion Principle)
    高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。
    这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

  • 接口隔离原则(Interface Segregation Principle)
    客户端不应该依赖它不需要的接口(Clients should not be forced to depend upon interfaces that they don't use)
    类间的依赖关系应该建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)
    接口隔离原则要求我们在设计接口时,要使用多个专门的接口不要使用单一的庞大臃肿的总接口,一个类对另一个类的依赖性应该建立在最小的接口上。要做到接口与角色一一对应,不应该让一个接口承担多个角色,也不应该让一个角色由多个接口承担。这样设计的接口在应对未来变更时,会更具有灵活性和可拓展性。

  • 迪米特法则,又称最少知道原则(Demeter Principle)
    最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
    通俗的讲一 个类对自己需要耦合或者调用的类应该知道的最少,你类内部是怎么复杂、怎么的纠缠不清都和我没关系, 那是你的类内部的事情,我就知道你提供的这么多 public 方法,我就调用这个

这些东西都是一下概念上的描述,理解起来比较困难,小伙伴们可以看看下面的文章,有助于大家更好的理解这些原则。
设计模式原则之接口隔离原则
设计模式心法
偶my耶-设计模式

设计模式的类型

根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)结构型模式(Structural Patterns)行为型模式(Behavioral Patterns)

创建型模式:
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
主要用于创建对象。

结构型模式:
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
主要用于处理类或对象的组合。

行为型模式
这些设计模式特别关注对象之间的通信。
主要用于描述对类或对象怎样交互和怎样分配职责。

常用的设计模式

本文将简单介绍在iOS开发中几种常用的设计模式:

创建型模式

工厂模式

在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

比如存在如下需求:
系统需要提供两个画笔(铅笔、钢笔),根据业务需要使用其中一种来绘制页面。
根据上述需求,我们可以创建两个类 Pencil 和 FountainPen,然后他们俩各自有自己的方法 draw。
到这里,我们的工作其实已经结束,开发者只需在使用的地方进行判断,初始化相应的对象来调用 draw 即可。

这样的话会存在什么样的问题呢,比如项目中多个对方存在对这个两个对象的初始化,假如有一天,对于这两个对象的创建存在一些逻辑上的限制,就需要修改多个地方。利用工厂模式对将创建对象都放在一个地方的话,将来进行修改就比较方便。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

那么对于上述需求该怎么设计呢:
首先 Pencil 和 FountainPen 肯定是实现了同一个协议(PenProtocol 协议提供方法 func draw())的,然后我们为 Pencil 和 FountainPen 分别创建一个工厂类 PencilFactory 和 FountainPenFactory,这两个工厂类也是实现了同一个协议 (PenFactoryProtocol 协议提供方法 static func createPen() -> PenProtocol),这样我们就可以在需要的时候,来使用相应的工厂,创建出我们需要的对象来进行绘制了。

上面一堆文字不太好看懂,来张图看看:


简单工厂类图

图也看不懂,没关系,上代码:

两个协议

protocol PenProtocol {
    
    //绘制的方法
    func draw()
}
protocol PenFactoryProtocol {
    
    //生产笔的方法
    static func createPen() -> PenProtocol
}

两种产品

import UIKit

class Pencil: NSObject {

}

extension Pencil:PenProtocol{
    func draw() {
        print("Pencil" + "绘制")
    }
}
import UIKit

class FountainPen: NSObject {

}

extension FountainPen:PenProtocol{
    func draw() {
        print("FountainPen" + "绘制")
    }
}

两个产品对应两个工厂

import UIKit

class PencilFactory: NSObject {

}

extension PencilFactory:PenFactoryProtocol{
    class func createPen() -> PenProtocol {
        return Pencil()
    }
}
import UIKit

class FountainPenFactory: NSObject {

}

extension FountainPenFactory:PenFactoryProtocol{
    class func createPen() -> PenProtocol {
        return FountainPen()
    }
}

调用

print("工厂模式")
//工厂模式使用(需要什么对象,就用对应的工厂类来创造)
PencilFactory.createPen().draw()

//工厂模式使用
FountainPenFactory.createPen().draw()

工厂模式的结构就是上面这种的,总体思想就是一个工厂生产一个类的对象。

下面是一个在iOS中使用的实例:
iOS - TableViewCell(CollectionViewCell)的工厂设计模式

在创建型模式模式中,还存在两个和工厂模式类似的模式,就是 简单工厂模式抽象工厂模式。本文就不做介绍了,以后有时间可以另外补充文章。

单例模式

单例模式(Singleton Pattern):单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。

单例模式的要点有三个:
一是某个类只能有一个实例;
二是它必须自行创建这个实例;
三是它必须自行向整个系统提供这个实例。

使用场景:

  • 需要频繁的进行创建和销毁的对象;
  • 创建对象时耗时过多或耗费资源过多,但又经常用到的对象;
  • 工具类对象;
  • 频繁访问数据库或文件的对象。

类图 :


单例模式类图.png

代码:

import UIKit

class SingleObject: NSObject {
    @objc static let shared = SingleObject.init()
    
    private override init() {
        super.init()
        
    }
}

调用

print("单例模式")
print(SingleObject.shared)
print(SingleObject.shared)

这里给到的是 swift 的写法,比较简单粗暴,关于单例在 oc 中的实现,可以看下面的文章。

相关文章
iOS 单例模式
iOS-单例模式写一次就够了

结构型模式

代理模式

这个模式不说啦,我是越研究越懵逼。
主要在纠结 iOS 中的代理、委托 到底是不是基于这种设计模式的。
真的整不明白啦,就不多说啦,免得误导大家,等哪天能力提升,搞清楚啦,再来补充这里一块儿。

贴几个文章大家有兴趣的可以看看。
设计模式---代理模式

设计模式:代理(Proxy)模式

iOS设计模式之代理模式

你真的了解iOS代理设计模式吗?

【java设计模式】(3)---代理模式(案例解析)

行为型模式

观察者模式

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。

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

主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

举例说明,在一场短跑比赛上,裁判员的枪声一响,所有运动员都要极速冲出去。在这里运动员就是观察者,裁判员就是目标对象。

使用场景:

  • 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
  • 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。

现在就使用上面提到的运动员和裁判员作为业务需求,看看代码应该怎么实现。

类图 :


观察者模式.png

代码:

首先需要一个抽象的观察者,这类观察者就观察发号枪的枪声

protocol Observer {
    
    //观察 发号枪 枪响
    func acceptGunshot()
}

具体的实例(运动员),来扩展 Observer 类,从而在观察到 acceptGunshot 触发时,就赶紧冲出去。

import UIKit

class Sportsman: NSObject {
    
    //跑步
    fileprivate func run(){
        print("\(self):快跑")
    }
}

extension Sportsman: Observer{
    func acceptGunshot() {
        run()
    }
}

被观察者(裁判员),需要给外部提供一个成为自己观察者的方法,然后在特定的时间点,来通知观察者。

import UIKit

class Judgment: NSObject {
    //观察者
    fileprivate var observers:[Observer] = []
    
    //添加观察者
    public func addObserver(observer:Observer){
        observers.append(observer)
    }
    
    //开枪
    public func gunshot(){
        print("biubiubiu")
        
        //通知所有观察者
        notifyAllObservers()
    }
    
    fileprivate func notifyAllObservers(){
        for item in observers {
            item.acceptGunshot()
        }
    }
}

调用

print("观察者模式")
//裁判就位
let judgment:Judgment = Judgment.init()
        
//三个运动员就位
let sportsman1:Sportsman = Sportsman()
let sportsman2:Sportsman = Sportsman()
let sportsman3:Sportsman = Sportsman()
        
//都要听裁判的发令枪
judgment.addObserver(observer: sportsman1)
judgment.addObserver(observer: sportsman2)
judgment.addObserver(observer: sportsman3)
        
//开炮
judgment.gunshot()

观察者模式在iOS中经典的应用场景就是 Notification、KVO。

有兴趣的同学可以看看下面这篇文章:
iOS Notification实现原理

策略模式

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。

使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

现在产品又来了一个这样的需求,要设计计算类,这个类需要支持加法运算和减法运算。多数人的处理方案应该就是以下两种:
1、写一个运算的方法,通过传递参数进行不同的运算。
2、写两个方法,来处理不同的运算。
这样的话如果哪天需求变更,比如要添加一个乘法运算和除法运算,这时候用上面的方法就要修改原有的类,这样就不太优雅。使用策略模式改怎么处理呢?

类图:


策略模式类图.png

代码:
为了扩展,肯定有个策略的接口,方便将来策略的具体实现。

protocol OperationStrategy {
    
    /// 运算处理
    ///
    /// - Parameters:
    ///   - num1: 数字1
    ///   - num2: 数字2
    /// - Returns: 返回值
    func doOperation(num1:Int, num2:Int) -> Int
}

然后就是各个策略的具体实现喽

class AddOperation: NSObject {

}

extension AddOperation:OperationStrategy{
    func doOperation(num1: Int, num2: Int) -> Int {
        return num1 + num2
    }
}
class SubstractOperation: NSObject {

}

extension SubstractOperation:OperationStrategy{
    func doOperation(num1: Int, num2: Int) -> Int {
        return num1 - num2
    }
}

所有准备做好就需要执行计算的管理类了

class OperationManager: NSObject {
    
    /// 运算
    ///
    /// - Parameters:
    ///   - num1: 数字1
    ///   - num2: 数字2
    ///   - strategy: 运算策略
    /// - Returns: 运算结果
    public class func operation(num1:Int, num2:Int, strategy:OperationStrategy) -> Int{
        return strategy.doOperation(num1: num1, num2: num2)
    }
}

使用

print("策略模式")
//根据需要,选择相应的运算方式就好喽
let result = OperationManager.operation(num1: 1, num2: 2, strategy: AddOperation())
print("\(result)")

let result = OperationManager.operation(num1: 2, num2: 1, strategy: SubstractOperation())
print("\(result)")

都这里我们就完成了最初的需求,将来需要添加不同的计算方式的时候,添加具体策略的实现就好啦。就不存在改动已有代码的问题喽。

上面的几种设计模式看完,可能会脑子里有点迷糊,肯能会搞不清楚两个设计模式之间的区别。

下面是我总结的我比较迷惑的几个设计模式之间的区别:

工厂模式vs策略模式
工厂:注重点是生产不同的对象。
策略:注重点是得到不同的方法。

比如上面的计算器的功能,网上有个解决方案就是使用工厂模式。具体实现就是为每一种运算方法提供一个类。

策略模式vs观察者模式
策略:注重其他类为自己提供某一功能的具体实现。
观察者:自身变化对其他类的通知。

这篇文章到这来就结束啦,小伙伴也许都是一脸懵逼,其实在项目开发中大家如果谨记面向对象的特点:继承、封装、多态。谨记自己写出的代码要高内聚、低耦合、易扩展。能够想方设法、多费点精力使敲出的代码符合这几点要求,就在自己无意识的情况下使用了某些设计模式。
另外就是需要多去阅读一些知名轮子的源码,多些代码,多些经过脑子的代码。

设计模式还有很多,网上也还有挺多文章的。这里再给大家贴个链接,需要的同学可以看看。
设计模式

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • 创建型模式 工厂模式 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设...
    liuyang7519阅读 317评论 0 2
  • 创建型模式 工厂模式 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设...
    隔墙送来秋千影阅读 2,637评论 0 11
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,067评论 1 32
  • 文章部分内容转载自:http://blog.csdn.net/zhangerqing 一、设计模式的分类 总体来说...
    j_cong阅读 2,051评论 0 20
  • 麦田·黄昏之思 文/邹航 走在乡间的土地上,一股股收获的麦浪 正以悠扬的姿态吐纳自己的清香 那金黄的麦穗儿,静悄悄...
    邹航阅读 421评论 0 2