如何理解这6种常见设计模式?

简介: 设计模式能够帮助我们优化代码结构,让代码更优雅灵活。有哪些常见的设计模式?如何合理运用?本文分享作者对工厂模式、单例模式、装饰模式、策略模式、代理模式和观察者模式的理解,介绍每种模式的模式结构、优缺点、适用场景、注意实现及代码实现。

image.png

一 前言

最近在改造一些历史的代码,发现一个很明显的特点,大部分代码是记叙文,按照事件的发展过程将故事平铺直叙的讲解出来。

这种方式的好处是比较符合人类的思维习惯,一条主线讲到底,代码阅读起来没有太大难度,只要顺着藤就能摸到瓜,但是缺点也很明显,一旦故事线中需要插入一些新的元素,比如:加入一个新的人物角色、新的时间线,都会需要大量更改故事线以配合这个新元素的融入,甚至对原有文章造成破坏性的影响。

为了解决这个问题,人们总结出了很多种文章结构,例如:总-分结构,并列结构,总-分-总结构等等,有了这些结构,在加入新元素的时候,甚至不必考虑新元素与原故事情节的关联性,直接单拉一个分支故事线独立去讲就好了,只要能够在整体故事结束前,与汇聚到主线故事就可以了(是不是很像git?)。

在软件开发领域,也有很多这样的非常有用的实践总结,我们称之为设计模式。对于设计模式,大家都不陌生,随便找个人,估计都能讲出N个设计模式来,但是除了这些设计模式的概念,很多人不知道如何灵活运用这些设计模式。所以借这篇文章和大家共同学习设计模式的思想。

二 理解设计模式

我尽量用最通俗易懂的示例和语言来讲述我理解的设计模式,希望能对大家有所帮助。

另外也无需精通所有的设计模式,只要能够融汇贯通常见的设计模式,就能让你的代码变得优雅。就像程咬金只会三板斧,但是熟练度无人能及,照样能横行天下。

1 工厂模式(Factory)

简单工厂(Simple Factory)

小明追妹子的时候,请她喝了不少咖啡,她爱喝卡布奇诺,每次去咖啡店,只要跟服务员说“来杯卡布奇诺”就行了,虽然各家的口味有些不同,但是不管是星爸爸还是Costa,都能够提供卡布奇诺这种咖啡。这里的星爸爸和Costa就是生产咖啡的工厂。

(1)简单工厂模式结构

简单工厂模式包含如下角色:

  • Factory:工厂角色-负责实现创建所有实例的内部逻辑.
  • Product:抽象产品角色-是所创建的所有对象的父类,负责描述所有实例所共有的公共接口。
  • ConcreteProduct:具体产品角色-是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。

结构图:

image.png

时序图:
image.png

(2)优缺点

    • 优点:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。
  • 缺点:是当产品修改时,工厂类也要做相应的修改。

工厂方法(Factory Method)

以前经常带老婆去优衣库(简单工厂)买衣服,就那么多款式,逛的次数多了,她就烦了。后来我改变策略,带老婆去逛商场(抽象工厂),商场里有各式品牌的店铺,不用我管,她自己就能逛上一整天。
区别于简单工厂,核心工厂类(商场)不再负责所有产品的创建,而是将具体创建的工作交给子类(服装店)去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口(门店),而不接触哪一个产品类应当被实例化这种细节。

(1)工厂方法模式结构

工厂方法模式包含如下角色:

  • Product:抽象产品
  • ConcreteProduct:具体产品
  • Factory:抽象工厂
  • ConcreteFactory:具体工厂

结构图:
image.png

时序图:
image.png

工厂模式总结

(1)适用场景

输出的产品是标准品,谁来做都可以。

(2)举例

常见的数据库连接工厂,SqlSessionFactory,产品是一个数据库连接,至于是oracle提供的,还是mysql提供的,我并不需要关心,因为都能让我通过sql来操作数据。

(3)注意事项

项目初期,软件结构和需求都没有稳定下来时,不建议使用此模式,因为其劣势也很明显,增加了代码的复杂度,增加了调用层次,增加了内存负担。所以要注意防止模式的滥用。

(4)简单实现

package FactoryMethod;
public class FactoryPattern
{public static void main(String[] args)
{Factory factory = new ConcreteFactoryA();Product product = factory.createProduct();product.use();}
}
//抽象产品:提供了产品的接口
interface Product
{public void use;
}
//具体产品A:实现抽象产品中的抽象方法
class ConcreteProductA implements Product
{public void use()
{System.out.println("具体产品A显示...");}
}
//具体产品B:实现抽象产品中的抽象方法
class ConcreteProductB implements Product
{public void use()
{System.out.println("具体产品B显示...");}
}
//抽象工厂:提供了厂品的生成方法
interface Factory
{public Product createProduct();
}
//具体工厂A:实现了厂品的生成方法
class ConcreteFactoryA implements AbstractFactory
{public Product createProduct()
{System.out.println("具体工厂A生成-->具体产品A.");return new ConcreteProductA();}
}
//具体工厂B:实现了厂品的生成方法
class ConcreteFactoryB implements AbstractFactory
{public Product createProduct()
{System.out.println("具体工厂B生成-->具体产品B.");return new ConcreteProductB();}
}

2 单例模式(Singleton)

韦小宝有7个老婆,但是每个都只有他这一个老公,他的所有老婆叫老公时,指的都是他,他就是一个单例。

单例模式结构

单例模式包含如下角色:

  • Singleton:单例

结构图:
image.png

时序图:

image.png

优缺点

  • 优点:全局只有一个实例,便于统一控制,同时减少了系统资源开销。
  • 缺点:没有抽象层,扩展困难。

应用场景

适合需要做全局统一控制的场景,例如:全局唯一的编码生成器。

注意事项

只对外提供公共的getInstance方法,不提供任何公共构造函数。

简单实现

public class Singleton
{private static volatile Singleton instance=null;    //保证 instance 在所有线程中同步private Singleton(){}    //private 避免类在外部被实例化public static synchronized Singleton getInstance()
{//getInstance 方法前加同步if(instance == null){instance = new Singleton();}return instance;}
}

3 装饰模式(Decorator)

大学毕业,想要送给室友一个有纪念意义的礼物,就找到一张大家的合照,在上面写上“永远的兄弟!”,然后拿去礼品店装了个相框,再包上礼盒。这里的我和礼品店都是装饰器,都没有改变照片本身,却都让照片变得更适合作为礼物送人。

装饰模式结构

装饰模式包含如下角色:

  • Component:抽象构件
  • ConcreteComponent:具体构件
  • Decorator:抽象装饰类
  • ConcreteDecorator:具体装饰类

结构图:

image.png

时序图:

image.png

优缺点

  • 优点:比继承更加灵活(继承是耦合度很大的静态关系),可以动态的为对象增加职责,可以通过使用不同的装饰器组合为对象扩展N个新功能,而不会影响到对象本身。
  • 缺点:当一个对象的装饰器过多时,会产生很多的装饰类小对象和装饰组合策略,增加系统复杂度,增加代码的阅读理解成本。

适用场景

  • 适合需要(通过配置,如:diamond)来动态增减对象功能的场景。
  • 适合一个对象需要N种功能排列组合的场景(如果用继承,会使子类数量爆炸式增长)

注意事项

  • 一个装饰类的接口必须与被装饰类的接口保持相同,对于客户端来说无论是装饰之前的对象还是装饰之后的对象都可以一致对待。
  • 尽量保持具体构件类Component作为一个“轻”类,也就是说不要把太多的逻辑和状态放在具体构件类中,可以通过装饰类。

简单实现

package decorator;
public class DecoratorPattern
{public static void main(String[] args)
{Component component = new ConcreteComponent();component.operation();System.out.println("---------------------------------");Component decorator = new ConcreteDecorator(component);decorator.operation();}
}
//抽象构件角色
interface  Component
{public void operation();
}
//具体构件角色
class ConcreteComponent implements Component
{public ConcreteComponent()
{System.out.println("创建具体构件角色");       }   public void operation()
{System.out.println("调用具体构件角色的方法operation()");           }
}
//抽象装饰角色
class Decorator implements Component
{private Component component;   public Decorator(Component component)
{this.component=component;}   public void operation()
{component.operation();}
}
//具体装饰角色
class ConcreteDecorator extends Decorator
{public ConcreteDecorator(Component component)
{super(component);}   public void operation()
{super.operation();addBehavior();}public void addBehavior()
{System.out.println("为具体构件角色增加额外的功能addBehavior()");           }
}

4 策略模式(Strategy)

男生追妹子时,一般都会用到这种模式,常见的策略有这些:约会吃饭;看电影;看演唱会;逛街;去旅行……,虽然做的事情不同,但可以相互替换,唯一的目标都是捕获妹子的芳心。

策略模式结构

  • Context: 环境类
  • Strategy: 抽象策略类
  • ConcreteStrategy: 具体策略类

结构图:
image.png

时序图:

image.png

优缺点

  • 优点:策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为。干掉复杂难看的if-else。
  • 缺点:调用时,必须提前知道都有哪些策略模式类,才能自行决定当前场景该使用何种策略。

试用场景

一个系统需要动态地在几种可替换算法中选择一种。不希望使用者关心算法细节,将具体算法封装进策略类中。

注意事项

一定要在策略类的注释中说明该策略的用途和适用场景。

简单实现

package strategy;
public class StrategyPattern
{public static void main(String[] args)
{Context context = new Context();Strategy strategyA = new ConcreteStrategyA();context.setStrategy(strategyA);context.algorithm();System.out.println("-----------------");Strategy strategyB = new ConcreteStrategyB();context.setStrategy(strategyB);context.algorithm();}
}
//抽象策略类
interface Strategy
{   public void algorithm();    //策略方法
}
//具体策略类A
class ConcreteStrategyA implements Strategy
{public void algorithm()
{System.out.println("具体策略A的策略方法被访问!");}
}
//具体策略类B
class ConcreteStrategyB implements Strategy
{public void algorithm()
{System.out.println("具体策略B的策略方法被访问!");}
}
//环境类
class Context
{private Strategy strategy;public Strategy getStrategy()
{return strategy;}public void setStrategy(Strategy strategy)
{this.strategy=strategy;}public void algorithm()
{strategy.algorithm();}
}

5 代理模式(Proxy)

淘宝店客服总是会收到非常多的重复问题,例如:有没有现货?什么时候发货?发什么快递?大量回答重复性的问题太烦了,于是就出现了小蜜机器人,他来帮客服回答那些已知的问题,当碰到小蜜无法解答的问题时,才会转到人工客服。这里的小蜜机器人就是客服的代理。

代理模式结构

代理模式包含如下角色:

  • Subject: 抽象主题角色
  • Proxy: 代理主题角色
  • RealSubject: 真实主题角色

结构图:

image.png

时序图:

image.png

优缺点

  • 优点:代理可以协调调用方与被调用方,降低了系统的耦合度。根据代理类型和场景的不同,可以起到控制安全性、减小系统开销等作用。
  • 缺点:增加了一层代理处理,增加了系统的复杂度,同时可能会降低系统的相应速度。

试用场景

理论上可以代理任何对象,常见的代理模式有:

  • 远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又叫做大使(Ambassador)。
  • 虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
  • Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
  • 保护(Protect or Access)代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
  • 缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
  • 防火墙(Firewall)代理:保护目标不让恶意用户接近。
  • 同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
  • 智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,如将此对象被调用的次数记录下来等。

简单实现

package proxy;
public class ProxyPattern
{public static void main(String[] args)
{Proxy proxy = new Proxy();proxy.request();}
}
//抽象主题
interface Subject
{void request();
}
//真实主题
class RealSubject implements Subject
{public void request()
{System.out.println("访问真实主题方法...");}
}
//代理
class Proxy implements Subject
{private RealSubject realSubject;public void request()
{if (realSubject==null){realSubject=new RealSubject();}preRequest();realSubject.request();afterRequest();}public void preRequest()
{System.out.println("访问真实主题之前的预处理。");}public void afterRequest()
{System.out.println("访问真实主题之后的后续处理。");}
}

6 观察者模式(Observer)

出差在外,想了解孩子在家的情况,这时候只要加入“相亲相爱一家人”群,老爸老妈会经常把孩子的照片和视频发到群里,你要做的就是作为一个观察者,刷一刷群里的信息就能够了解一切了。

观察者模式结构

观察者模式包含如下角色:

  • Subject:目标
  • ConcreteSubject:具体目标
  • Observer:观察者
  • ConcreteObserver:具体观察者

结构图:

image.png

时序图:

image.png

优缺点

  • 优点:将复杂的串行处理逻辑变为单元化的独立处理逻辑,被观察者只是按照自己的逻辑发出消息,不用关心谁来消费消息,每个观察者只处理自己关心的内容。逻辑相互隔离带来简单清爽的代码结构。
  • 缺点:观察者较多时,可能会花费一定的开销来发消息,但这个消息可能仅一个观察者消费。

适用场景

适用于一对多的的业务场景,一个对象发生变更,会触发N个对象做相应处理的场景。例如:订单调度通知,任务状态变化等。

注意事项

避免观察者与被观察者之间形成循环依赖,可能会因此导致系统崩溃。

简单实现

package observer;
import java.util.*;
public class ObserverPattern
{public static void main(String[] args){Subject subject = new ConcreteSubject();Observer obsA = new ConcreteObserverA();Observer obsb = new ConcreteObserverB();subject.add(obsA);subject.add(obsB);subject.setState(0);}
}
//抽象目标
abstract class Subject
{protected List<Observer> observerList = new ArrayList<Observer>();   //增加观察者方法public void add(Observer observer){observers.add(observer);}    //删除观察者方法public void remove(Observer observer){observers.remove(observer);}   public abstract void notify(); //通知观察者方法
}
//具体目标
class ConcreteSubject extends Subject
{private Integer state;public void setState(Integer state){this.state = state;// 状态改变通知观察者notify();}public void notify(){System.out.println("具体目标状态发生改变...");System.out.println("--------------");       for(Observer obs:observers){obs.process();}}          
}
//抽象观察者
interface Observer
{void process(); //具体的处理
}
//具体观察者A
class ConcreteObserverA implements Observer
{public void process(){System.out.println("具体观察者A处理!");}
}
//具体观察者B
class ConcreteObserverB implements Observer
{public void process(){System.out.println("具体观察者B处理!");}
}

 

原文链接
本文为阿里云原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/515070.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

构建在线教育弹性高可用视频处理架构实战

简介&#xff1a; 对于负责建设视频处理系统的技术团队而言&#xff0c;这样的业务场景就留给了他们一系列的挑战。 前言 近些年&#xff0c;在线教育行业飞速发展&#xff0c;为整个社会的知识传播提供了前所未有的便利性。通过多种形式的在线教育平台&#xff0c;学员与教师…

一文解开java中字符串编码的小秘密

简介&#xff1a; 在本文中你将了解到Unicode和UTF-8,UTF-16,UTF-32的关系&#xff0c;同时你还会了解变种UTF-8&#xff0c;并且探讨一下UTF-8和变种UTF-8在java中的应用。 简介 在本文中你将了解到Unicode和UTF-8,UTF-16,UTF-32的关系&#xff0c;同时你还会了解变种UTF-8&…

Gartner数据劲爆:阿里全球第三,华为中国第二!

看了一份数据&#xff0c;非常振奋人心&#xff0c;给大家分享一下。国外著名信息分析公司 Gartner&#xff0c;4月21号发布了一份数据&#xff0c;瞬间引发了朋友圈是刷屏。这份数据是讲什么的呢&#xff1f;云计算&#xff01;可能由于疫情&#xff0c;很多公司上云的热情变得…

程序员:写作能收获什么?

简介&#xff1a; 很多程序员已经通过自己的个人博客或者公众号来进行技术沉淀&#xff0c;记录自己的成长。越来越多的程序员们也开始意识到了写作的重要性。程序员为什么需要写作&#xff1f;写作能带来什么收获&#xff1f;又有哪些额外的惊喜&#xff1f;本文介绍三位长期坚…

腾讯云~Redis6.2.6 伪集群 哨兵模式_搭建

文章目录一、redis准备3节点1. 创建目录2. 节点1~配置3. 节点2~配置4. 节点3~配置5. 启动redis二、新增sentinel配置1. sentinel_01.conf2. sentinel_02.conf3. sentinel_03.conf4. sentinel 启动5. sentinel 监控6. 哨兵验证一、redis准备3节点 1. 创建目录 mkdir /usr/loca…

教你 4 步搭建弹性可扩展的 WebAPI

简介&#xff1a; 本文整理自《Serverless 技术公开课》&#xff0c;关注“Serverless”公众号&#xff0c;回复“入门”&#xff0c;即可获取 Serverless 系列文章 PPT。 作者 | 萧起 阿里云云原生团队 本文整理自《Serverless 技术公开课》&#xff0c;关注“Serverless”公…

从 0 到 1,高德 Serverless 平台建设及实践

来源 | Serverless作者 | 邓学祥头图 | 下载于东方IC导读&#xff1a;高德从 FY21 财年开始启动 Serverless 建设&#xff0c;至今一年了&#xff0c;高德 Serverless 业务的峰值超过十万 qps 量级&#xff0c;平台从 0 到 1&#xff0c;qps 从零到十万&#xff0c;成为阿里集团…

看动画学算法之:排序-快速排序

简介&#xff1a; 快速排序也采用的是分而制之的思想。那么快速排序和归并排序的区别在什么地方呢&#xff1f; 归并排序是将所有的元素拆分成一个个排好序的数组&#xff0c;然后将这些数组再进行合并。 而快速排序虽然也是拆分&#xff0c;但是拆分之后的操作是从数组中选出一…

思考、创新、坚持——阿里做了七年前端,我的成长经验分享

在成长的未知道路上&#xff0c;我们总会遇到各种各样的问题&#xff0c;但是&#xff0c;所有的迷茫与逆境都能够帮助我们成长&#xff0c;我们要抓住每一个机会让自己进步&#xff0c;而不是徘徊不前。 淘系前端开发同学——林晚&#xff0c;今天就来和大家分享他这七年的成长…

存储进阶:怎么才能保证 IO 数据的安全?

来源 | 奇伢云存储头图 | 下载于视觉中国写成功了数据就安全了吗&#xff1f;思考一个问题&#xff1a;写数据做到什么程度才叫安全了&#xff1f;就是&#xff1a;用户发过来一个写 IO 请求&#xff0c;只要你给他回复了 “写成功了”&#xff0c;那么无论机器发生掉电&#x…

设计稿生成代码与 Serverless 的前世今生与未来!

简介&#xff1a; 云栖大会云上 Hello World 活动火热进行中&#xff01;每位参与者都可收获一份阿里云出品的全球唯一序列号纪念证书&#xff01; 一场脑洞实验 云栖大会云上 Hello World 活动火热进行中&#xff01;每位参与者都可收获一份阿里云出品的全球唯一序列号纪念证…

ARMS在APM工具选型中的实践

简介&#xff1a; 当前的系统在数字化转型需求以及互联网架构实施的影响下&#xff0c;越来越普遍地使用了微服务架构&#xff0c;我们在享受微服务带来的好处&#xff08;开发效率高&#xff0c; 独立部署&#xff0c; 水平扩展&#xff0c; 故障与资源隔离等等&#xff09;外…

无人机、IoT 设备都有漏洞?专访以色列老牌安全公司 Check Point|拟合

从无序中寻找踪迹&#xff0c;从眼前事探索未来。 2021 年正值黄金十年新开端&#xff0c;CSDN 以中立技术社区专业、客观的角度&#xff0c;深度探讨中国前沿 IT 技术演进&#xff0c;推出年度重磅企划栏目——「拟合」&#xff0c;通过对话企业技术高管大咖&#xff0c;跟踪报…

从零入门 Serverless | 函数计算的可观测性

简介&#xff1a; 本文主要分为三个部分&#xff1a;概述中介绍可观测性的基本概念&#xff0c;主要包括 Logging、Metrics、Tracing 三个方面&#xff1b;然后详细介绍函数计算上的 Logging、Metrics、Tracing&#xff1b;最后以几个常见场景为例&#xff0c;介绍在函数计算中…

宜家:打造新零售时代的智能客户身份管理系统

简介&#xff1a; 宜家选择了阿里云应用身份服务&#xff08;IDaaS&#xff09;来为其提供一个包括统一认证、统一账户管理的CIAM解决方案&#xff0c;为所有前端提供统一的安全、可扩展和可靠的身份认证服务&#xff0c;包括灵活的认证配置、单点登录、多因素认证、社交平台登…

生意参谋牵手Quick BI 让数据再次驱动店铺经营

刚刚过去的一周&#xff0c;超两百家店铺体验了阿里巴巴官方全渠道、全链路、一站式数据平台生意参谋推出的全新功能&#xff0c;自助分析。 作为生意参谋联合Quick BI的初次尝试&#xff0c; “自助分析”面向店铺提供自助分析解决方案&#xff0c;支持店铺个性化数据报表制作…

到底是谁发明了物联网?

来源 | 鲜枣课堂作者 | 小枣君头图 | 下载于视觉中国1965年的越南战场&#xff0c;美军正深陷战争泥潭。突然有一天&#xff0c;北越士兵在胡志明小道发现了一些奇怪的东西。这些东西看上去像树枝&#xff0c;但实际上由金属构成&#xff0c;里面包含一些神秘的电子元件。这些士…

八种经典排序算法总结

前言 算法和数据结构是一个程序员的内功&#xff0c;所以经常在一些笔试中都会要求手写一些简单的排序算法&#xff0c;以此考验面试者的编程水平。下面我就简单介绍八种常见的排序算法&#xff0c;一起学习一下。 一、冒泡排序 思路&#xff1a; 比较相邻的元素。如果第一…

docker onlyoffice7.1.1 word excel ppt在线编辑、在线预览_部署01

文章目录1. 创建onlyoffice容器2. 启动在线案例3. 开放防火墙4. 浏览器验证5. 上传文件测试6. 在线编辑7. 测试主页面1. 创建onlyoffice容器 下面命令作用&#xff1a;拉取镜像、映射宿主机端口和docker内部端口、创建宿主机和docker容器挂载目录、拉取指定版本的onlyoffice/d…

漫画 | 程 序 员 脱 单 指 南

本文纯属娱乐&#xff0c;切勿模仿&#xff0c;模仿后果难以评估&#xff0c;务必小心再小心&#xff0c;谢谢&#xff01;