通俗易懂--常见23种设计模式

2,166 阅读24分钟

前言

一个单身的兄弟,问我:什么是设计模式?

设计模式?这么说吧,在生活中,常常看到男孩子追女孩子,对于女孩子来说,“被人追”可能是一个会经常发生的问题;对于男孩子来说,经常要去追别人,这就是一个经常发生的问题。

发现了吗,对于这些重复发生的问题,前人们已经留下了各种各样“宝贵”的经验,教男孩子们如何一步一步去追女孩子,教女孩子怎么防范和识别男孩。

大量的这种所谓的实践经验沉淀下来,就会形成一些套路,而你发现吧,有时候这种“套路”还非常好用,能帮我们快速解决问题,这些套路大概就是所谓的设计模式吧。

哦哦,原来是这样!

实际上,在任何活动中都存在这某些重复遇到的典型问题,不同开发人员对这些问题设计出不同的解决方案,随着设计经验在实践者之间日益广泛的被利用,描述这些共同问题和解决这些问题的方案就形成了所谓的模式。

设计模式的主要作用

兄弟问我,那我们为什么要使用设计模式呢?

这就要说到设计模式的作用了。

刚才我们已经说到,各路情感大师留下了非常多的宝贵的情感经验,这些所谓的“套路”,有什么用呢,它可以帮我们快速追到女孩子,帮我们快速识别渣男等等。

比如男孩子去追女孩子,应该怎么去追呢?

(1)你可以先和她交流,看看对方和你能否聊的来。

(2)如果对方和你聊得来,你就找个周末约她出来。

(3)如果约出来了,你就带她去吃饭,去看电影,去过马路,那过马路多危险呀,她一个柔弱的女孩子,没有人牵着,该多危险,于是你应该去牵她的手。

(4)如果她不拒绝你,你就看看夜晚的星空,感叹深秋夜凉,她一个弱小的女孩子,衣着单薄,你应该果断给她一个温暖的拥抱。

(5)这时你发现,一个柔弱的女孩子,正深情的望着你,于是你新生怜悯,深情抱着她,给了她一个热烈的吻。

后续剧情请自行脑补 ... ...

所以你发现了吗,有些套路简直老套到掉牙,但是它有时候就是很香。

首先,它可以让我们快速去推进男女之间的感情发展。

其次,你朋友向你打听你们之间有何进展的时候,你不需要说,我们周四晚上约了周五晚上吃饭,然后到哪里见面,去了哪个餐厅,去了哪个电影院,看了什么电影... ...

你仅仅需要说:我们今天牵手了,就可以表达出你们之间的情感关系了。“牵手”就是你们之间关系的定义,更加快速的向任何人传达你们之间的关系进展。

再次,如果你不懂这些所谓的套路的时候,第一次约会的你,直接上来就问,你有房吗,你月收入多少,以后孩子跟谁姓? 不好意思,这段发展到此接受。但是当你按照套路去发展男女关系的时候,你会发现,你们谈崩的风险大大降低了。

发现了吗,设计模式,就是有这么多相当重要的效果,这就是为什么自古深情留不住,总是套路得人心

在实际开发工作中,并不是说我兢兢业业,老老实实工作就好,你需要懂一定的设计模式,并且用好设计模式,才能写出优雅的代码。

设计模式具体的好处有:

1,简化并加快设计

2,方便开发人员之间的通信

3,降低风险

4,有助于转到面向对象技术

成熟的软件设计模式具有以下特性

兄弟继续问我,那么设计模式有什么特点呢?

我说,你有没有发现,刚才那些套路,第一个特点,你会发现非常巧妙,为什么要在过马路的时候去牵她的手呢,因为这个时候路况危险,你欠她的手是为了保护她的安全,体现你的男子气概。这个时候就不容易被拒绝。

兄弟若有所思:真是妙呀,女孩子一心过马路,哪有心思去挣脱我的狼爪呢?

我说,是呀,第二个特点,就是通用。你越这个女孩子的时候,可以这样做,万一你们闹掰了,你又喜欢上别的女孩子,你还是可以用这些套路。女孩千千万,套路却唯一。

兄弟疑惑的说:这些对每个女孩子都有用吗?

我说,那当然,设计模式的第三个特点,就是得到了很好的证明,大量的“前辈”们,已经做了大量的情感实践,将这套情感攻略留给后人,他们不仅仅是停留在理论上的。

兄弟说,我懂了,听你这么一说,我觉得追女孩子倒也没那么复杂了。

我说,那是,设计模式的第四个特点,就是简单,你学完以后,用在实际的情感发展中,可以大大简化追女孩子这件事情,你不需要呕心沥血的想我第一步应该干嘛,接下来又该怎么办之类的问题了。你仅仅需要按照套路去走,简单实用。

那么还有最后一个特点,就是可重用,像请吃饭,请看电影这些方法,你每次约会都可以重复使用。一次维护和巩固你们之间的感情。

故事到这里就扯完了,我们下面直接进入正题吧,继续介绍设计模式:

所以回到实际开发中,设计模式有以下特点:

1,巧妙:设计模式是一些优雅的解决方案,是在大量实践经验的基础上提炼出来的。

2,通用:设计模式通常不依赖于某个特定的系统类型、程序设计语言或应用领域,他们是通用的

3,得到很好的证明:设计模式在实际系统和面向对象系统中得到广泛应用,它们并不仅仅停留在理论上。

4,简单:设计模式通常都非常简单,只涉及很少的一些类。为了构件更多更复杂的解决方案,可以把不同的设计模式与应用代码结合或混合使用

5,可重用:设计模式的建档方式使它们非常易用,因而可方便用于任何适宜的系统。

6,面向对象: 设计模式是最基本面向对象机制,诸如类、对象、多态等构成的。

设计模式的六大原则

这里再简单介绍一下设计模式的基本原则:

1、开闭原则(Open Close Principle)

开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

2、里氏代换原则(Liskov Substitution Principle)

里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

3、依赖倒转原则(Dependence Inversion Principle)

这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

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

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

5、迪米特法则,又称最少知道原则(Demeter Principle)

最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)

合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。

设计模式的分类

我们一共介绍常见的23种设计模式。

一共分为三类 5 + 7 + 11 = 23种

  • 创建型模式

    • 工厂模式(Factory Pattern)
    • 抽象工厂模式(Abstract Factory Pattern)
    • 建造者模式(Builder Pattern)
    • 原型模式(Prototype Pattern)
    • 单例模式(Singleton Pattern)
  • 结构型模式

    • 适配器模式(Adapter Pattern)
    • 桥接模式(Bridge Pattern)
    • 组合模式(Composite Pattern)
    • 装饰器模式(Decorator Pattern)
    • 外观模式(Facade Pattern)
    • 享元模式(Flyweight Pattern)
    • 代理模式(Proxy Pattern)
  • 行为性模式

    • 责任链模式(Chain of Responsibility Pattern)
    • 命令模式(Command Pattern)
    • 解释器模式(Interpreter Pattern)
    • 迭代器模式(Iterator Pattern)
    • 中介者模式(Mediator Pattern)
    • 备忘录模式(Memento Pattern)
    • 观察者模式(Observer Pattern)
    • 状态模式(State Pattern)
    • 策略模式(Strategy Pattern)
    • 模板模式(Template Pattern)
    • 访问者模式(Visitor Pattern)

创建型模式

所谓创建型模式,就是我们用来创建对象时用到的模式。

工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

模式举例:

你约女朋友去管商场,商场需要为顾客提供各种食品。
你想吃中餐,商场只需要开一个中餐厅就行了,这个中餐厅就是“工厂”
你想吃鱼香肉丝,你只需要告诉“工厂”,你要“鱼香肉丝”;你也可以要“宫保鸡丁”。

通过这个工厂,就可以提供中餐给你。

建造者模式

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。强制实行一种分步骤进行的建造过程。但是每个过程的细节我们不必知道。

模式举例:

你和女朋友去中餐厅吃饭,比如女朋友想吃一个炒鸡蛋,厨师先放油,再放鸡蛋,再翻面炒,再出锅。这里的几个步骤和顺序,都规定好了.

具体每个步骤是怎么实施的,比如鸡蛋是先打碎再入锅,还是敲碎就立刻入锅,不同厨师可能不一样,这个细节我们不用管。

抽象工厂模式

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

模式举例:

你和女朋友去商场里游玩,你女朋友今天想吃中餐,明天想去西餐,这样,一个“工厂”就不够用了。需要中餐和西餐等不同的“工厂”。

在客户的角度,我需要先选一个“工厂”,选一个“菜品”。
在工厂模式中:在客户的角度,我只需要选一个“菜品”。

原型模式

原型模式,允许对象在不了解要创建的对象的确切类,以及如何创建等细节的情况下创建自定义对象。

模式举例:

你带女朋友去吃黄焖鸡米饭。服务员问你,你需要中辣还是微辣?你说中辣。

假设服务员给你在一个不辣的黄焖鸡米饭上加两勺辣椒,就给你端出来了,说这就是中辣。

这个过程,其实就是原型模式,原型就是这个“不辣的黄焖鸡米饭”.
“中辣的黄焖鸡米饭”就是在不辣这个基础上加了两勺辣椒。

给你出餐的这个服务员,也许不需要知道“不辣的黄焖鸡米饭”是怎么做的。仅仅是在辣度上有一点区别。

原型模式通常适用于以下场景。

  • 对象之间相同或相似,即只是个别的几个属性不同的时候。
  • 创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
  • 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
  • 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。

单例模式

单利模式确保全一个类只有一个实例,并且提供了对该类的全局访问入口,可以确保使用这个类实例的所有的对象,使用相同的实例。

模式举例:

你对女朋友讲起了你小时候的峥嵘岁月
如果是在学校,因为你每个科目都不好好学。你不听话,老师就千辛万苦的教导你。这里的老师,可能是多个老师。老师是存在多个的。这是多例。

但是回到家你也不听话,调皮捣蛋,只要不听话,你爸就用皮带抽你。 这里的“你爸”就是单例模式。亲爸爸只有一个。

结构性模式

适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

模式举例:

你和女朋友去餐厅吃饭,你女朋友要吃火锅,而且点了重辣,但是你这几天胃不好,医生告诉你不能吃重辣。于是你向服务员要了一大杯清水,每次吃之前,你就在清水里涮一下,就不辣了。

原本重辣火锅,只有你女朋友可以吃,但是现在同过一个额外的“适配”,也就是这杯水,你也可以吃上这个火锅了。

这就是适配器模式。让重辣火锅同时服务于不吃辣和吃辣的人。

桥接模式

桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。

这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。

这里强调了实体类和接口相互独立。

模式举例:

你带女朋友去喝咖啡,咖啡有美式、卡布奇诺、拿铁。口味有不加糖和加糖。

咖啡馆如果要满足所有客户,就要准备美式加糖、卡布奇诺加糖、拿铁加糖、美式不加糖、卡布奇诺不加糖、拿铁不加糖。一共6种咖啡。
但是实际上,我们只需要三种口味的咖啡,和两种不同的甜度就行了。一共5种方案。

让口味和甜度独立配置,相互解耦。这就是桥接模式。

组合模式

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。

模式举例:

你送女朋友礼物,你买了iphone,买了手机壳,装在一起就是一个带壳的手机。

这就是组合模式。你送的礼物是“带壳的iphone”

装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

模式举例:

你带女朋友玩吃鸡。你女朋友捡到一个98k,叫你给她一个8倍镜。那么这就是装饰器模式。

在原有98k的基础上,增加了瞄准远视的功能, 扩展了原来的98k的功能。但是原来的98k还是完整的。

外观模式

外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。

模式举例:

你带女朋友去吃黄焖鸡米饭,点完餐以后,老板娘还给你送到座位上了。整个过程,在你这个顾客看来,只会接触到一个老板娘,仿佛这个饭店就只有一个点餐员。里面食物加工过程,你是不需要了解的。对于客户来说,

餐厅简化了整个吃饭的过程。只安排一个老板娘和你对接。

享元模式

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。

享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。

模式举例:

你女朋友有习惯,每天早起要在卧室喝一杯水,午饭前要在餐桌上喝一杯水,晚上需要在书房喝一杯水。
你是不是需要准备三个杯子来喝水呢,并不是。你只需要一个杯子就行了。

享元模式,就是为了节省对象的创建的。但是你需要早中晚把杯子放在不同的地方去。这就需要额外安排了。
享元模式就是这样增加了程序复杂度,但是节省了杯子这个资源的创建。

代理模式

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

模式举例:

你和女朋友周末不想出门买菜,于是麻烦邻居帮你们去买。整个买菜的过程,全部由邻居决定。邻居买菜的时候,会考虑你的口味等。这就是代理模式。

行为性模式

责任链模式

顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

模式举例:

你向女朋友求婚,女朋友不能做决定,于是把这个问题交给自己的父亲;
父亲不能决定,于是把问题交给母亲;
最后母亲看到你衣冠禽兽,不,衣冠楚楚,一表人才,决定把女儿嫁给你。

这个模式就是责任链模式。

命令模式

命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

模式举例:

你和女朋友在家躺着,你女朋友喊你去做饭,你就去做饭,喊你去扫地,你就去扫地。
发出命令和执行命令的不是同一个人。这就是命令模式。命令封装在“你”身上,你女朋友只需要调用你身上的“命令”。

解释器模式

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

中介者模式

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。

模式举例:
你和女朋友在家里躺平,不想出门啊,点外卖。外卖平台对于干饭人和店家就是中介。干饭人不需要知道饭具体是怎么做的,也不需要知道店的具体地址在哪里,卖家也不需要知道具体是谁吃了干了自家的饭,是男是女等。

对于干饭人和店家来说,互相隐去了很多通信上的信息,干饭人只需要付钱,店家只需要做饭就好了。

备忘录模式

备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。

模式举例:

某年的生日,你女朋友享受到了你的惊喜,你准备了丰盛的晚餐,精致的礼物,还有浪漫的烛光。你把这个生日“套装”深深的记录在脑海里。第二年的时候,你又给女朋友来了一套相同的“惊喜”,气的你女朋友离家出走。

这里的“生日惊喜套装”,其实就是一个备忘录。你采用的就是备忘录模式。在需要的时候,又拿出一份同样的东西出来。

但是也要灵活运用。要不然就是汪汪妙计安天下,赔了夫人又折兵。

观察者模式

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

这个是各位同学最熟悉的设计模式了。

模式举例:

你把女朋友的心情记作一个状态。
每当女朋友心情是“好”的时候,你女朋友就告诉你,我好开心。这时候你可以为所欲为。
但是女朋友发火的时候,她就告诉你,“我很生气”。这时候,你一定要毫不犹豫的下跪,眼泪汪汪的说:“对不起,亲爱的,我错了。总而言之都是我的错。”

这里不就是一个观察者模式么。你的行为完全依赖女朋友的心情变化。

状态模式

在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。

在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

模式举例:

假设你女朋友的心情可以分为“开心”、“不开心”、“一般”三种。

而且她告诉你一些方法:
“买礼物”可以使她从“一般”变成“开心”
“买包包”可以使她的心情从“不开心”变成“开心”
“勾搭妹子”可以让她的心情从“开心”变成“不开心”。等等。

这里的三种心情就是状态,而且女朋友还给你提供了一些方法,来改变状态。
这不就是状态模式么。

策略模式

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

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

模式举例:

你女朋友过生日,你想让她开心,你可以带她“看电影”,带她“旅游”,请她“吃饭”。

为了达到一个结果,你可以使用不同的方法,这些方法之间可以相互替换的,这都是策略模式。

模板模式

在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

模式举例:我告诉你,追女朋友要先“牵手”,再“拥抱”,再“接吻”, 再“拍拍..额..手”... ...

具体你用左手还是右手牵,无所谓,但是整个过程,定了一个模板。按照模板来就行。

访问者模式

在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

访问者(Visitor)模式实现的关键是如何将作用于元素的操作分离出来封装成独立的类

模式举例:你有钱,你有兴趣爱好:喜欢女孩子。

但是有一天。你有女朋友了。

你女朋友告诉你,面对其他女孩子的时候,你要说:“我是穷光蛋,我只喜欢男人”;面对女朋友的时候,你要说:“我的钱就是你的钱,而且我今生今世只爱你一个。”

这大概就是访问者模式。

女朋友和其他女孩子是两种访问者,面对不同访问者,你的富裕程度不一样,你的性取向也不一样。

后记

忘记说了,程序员怎么会有女朋友呢?

如果举例不切当,请多包含。

如果觉得某个模式举例不恰当,欢迎留言指出。

喜欢的朋友可以帮我点赞和关注,您的关注是我最大的动力。