23种设计模式的优点与缺点概况

设计模式

标签(空格分隔): 设计模式优点 应用场景


整理自《设计模式之禅》

单例模式

优点:

  • 只有一个实例,减少了内存开支;
  • 可以避免对系统资源的多重占用;
  • 可以在系统中设置全局的访问点,优化和共享资源访问;

缺点:

  • 没有接口,扩展困难;
  • 对测试开发不利;

应用场景:

  • 要求生成唯一序列号的场景;
  • 需要一个共享访问点;
  • 创建一个对象需要消耗过多的资源时
  • 需要定义大量的静态常量和静态方法时(也可直接声明为static的方式);

工厂方法模式

优点:

  • 良好的封装性,代码结构清晰;
  • 扩展非常好;
  • 屏蔽产品类;

应用场景:

  • 是new一个对象的替代品;
  • 需要灵活的,可扩展的框架时;
  • 使用在测试驱动开发的框架下;

抽象工厂模式

优点:

  • 封装性;
  • 产品族内部的约束为非公开状态;

缺点:

  • 产品族扩展困难;

模板方法模式

优点:

  • 封装不变部分,扩展可变部分,把不变的算法封装到父类实现,可变的部分则通过继承来扩展;
  • 提取公共部分代码,便于维护;
  • 行为由父类控制,子类实现;

缺点:

  • 子类对父类产生影响,子类执行的结果影响了父类的结果;

应用场景:

  • 多个子类有公有的方法,且逻辑相同时;
  • 重要,复杂的算法,可以把核心算法设计为模板方法;
  • 重构时,把相同的代码抽取到父类,然后通过钩子函数结束其行为;

建造者模式

优点:

  • 封装性,使得客户端不必知道产品内部的组成细节,我们不用关心每一个具体的模型内部是如何实现的。
  • 建造者独立,容易扩展
  • 便于控制细节风险,由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响;

建造者模式的应用场景:

  • 相同的方法,不同的执行顺序,会产生不同的结果时;
  • 多个部件或零件,都可以装配到一个对象中,但产生的运行结果又不相同时,如Android中的AlertDialog的构造;
  • 产品类非常复杂,或产品类的的调用顺序不同产生不同的效果;

代理模式

优点:

  • 职责清晰,其实的角色就是实现实际的业务的逻辑,不用关心其他非本职责的事务;
  • 高扩展性,具体主题角色随时都会发生变化,但只要它实现了接口,我们的代理类就可以在完全不做任何修改的情况下使用;

原型模式(通过实现Cloneable接口)

优点:

  • 性能优良,原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好,特别是要在循环体内产生大量对象时,
  • 避免构造函数的约束,直接是在内存中拷贝的,构造函数是不会执行的。

应用场景:

  • 类初始化需要消化非常多的资源时
  • 性能和安全要求的场景,通过 new产生一个对象需要非常繁琐的数据准备和访问权限时;
  • 一个对象多个修改者的场景,一个对象需要提供给多个对象访问,而且各个调用者都可以修改其值时;

注意地方:浅拷贝与深拷贝

Java的Object类提供的clone方法只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,其他的原始类型如int,char等都会被拷贝,拷贝后的对象与原生对象共享内部元素的地址(浅拷贝),如果拷贝后的对象修改了原生对象的数组,则原生对象也会看到修改。如果需要进行深拷贝,则需要在复写的clone方法里对私有的类变量(内部数组,引用对象)进行独立的拷贝。并且使用final关键字修饰的变量不能被拷贝;

中介者模式

优点:

  • 减少了类间的依赖,把原有的一对多的依赖变成了一对一的依赖;

    缺点:

  • 中介者会膨胀得很大,而且逻辑复杂;原本N个对象的依赖关系转换为中介者与对象的依赖关系;

命令模式

优点:

  • 类间解耦,调用者与接收者之间没有任何依赖关系,调用者实现功能时不需要了解到底是哪个接收者执行,只需调用Command抽象类的execute方法就可以了;
  • 可扩展性,Command的子类可以非常容易扩展,并且调用者和高层模块不产生严重的代码耦合;

缺点:

  • Command类膨胀厉害,如果有N个命令,则Command类的子类就为N个;

    应用场景:如Android中各种事件的处理;

责任链模式

优点:

  • 请求与处理分开,请求者可以不用知道是谁处理的,处理者可以不用知道请求的全貌;

缺点:

  • 性能问题,每个请求都是从链头遍历到链尾的,当这个责任链比较长时,遍历开销会比较大;

应用场景:

  • 如Android事件的传递机制;

装饰器模式

优点:

  • 装饰类和被装饰类可以独立发展,而不会互相耦合;
  • 装饰模式是继承关系的一个替代方案,不管装饰多少层,最终返回的也还是那个对象;
  • 装饰模式可以动态地扩展一个实现类的功能;

缺点:

  • 多层的装饰比较复杂,当使用多层装饰出现问题时,排查问题的工作量比较大

应用场景:

  • 需要扩展一个类的功能,或给一个类增加附加功能;
  • 需要为一批兄弟类进行改装或加装功能;

策略模式

优点:

  • 算法可以自由切换,只要实现抽象策略,它就成为策略家庭的一个成员;
  • 避免使用多重条件判断,
  • 扩展性良好,在现有的系统中增加一个策略太容易,只要实现接口就可以了;

缺点:

  • 策略类数量多,每一个策略都是一个类,复用的可能性很小;
  • 所有的策略类都需要对外暴露,上层模块必须知道有哪些策略,然后决定使用哪一个策略;

应用场景:

  • 多个类只有在算法或行为上稍有不同的场景;
  • 算法需要自由切换的场景;
  • 需要屏蔽算法规则的场景;

适配器模式

优点:

  • 让两个没有任何联系的类在一起运行;
  • 增加了类的透明性;
  • 提高了类的复用度;
  • 灵活性好,当不需要适配器时,只要删掉这个适配器就可以了,

应用场景:

  • 修改一个已经投产的接口时,
  • Android中各种Adapter,

迭代器模式

  • 迭代器模式是为解决遍历容器中的元素而诞生的,没有人会单独写一个迭代器,使用Java提供的Itreator就可以满足要求了;

组合模式

优点:

  • 高层模块调用简单,高层模块不需要关心自己处理的是单个对象还是整个组合结构,
  • 节点可以自由增加;

缺点:

  • 调用时会直接使用实现类,不符合面向接口编程思想;

应用场景:

  • 维护和展示部分-整体关系的场景,如树型菜单,文件和文件夹的管理;
  • 只要是树型结构,就要考虑使用组合模式;

观察者模式

优点:

  • 观察者与被观察者之间是抽象耦合,不管是增加观察者还是被观察者都非常容易扩展;
  • 建立一套触发机制;

缺点:

  • 一个被观察者,多个观察者,开发与调度会比较复杂,在Java中消息的通知默认是顺序执行,其中一个观察者卡壳,会影响整体的执行效率,一般要考虑采用异步的方式;

应用场景:

  • 关联行为场景,如Android中数据变化会引起UI的变化;
  • 事件多级触发场景;
  • 跨系统的消息交换场景;

门面模式

优点:

  • 减少系统的相互依赖,所有的依赖都是与门面对象的依赖,与子系统无关。
  • 提高了灵活性;
  • 提高了安全性,想让你访问子系统的哪些业务就开通哪些逻辑;

缺点:

  • 不符合开闭原则,当出现bug后,只能通过修改门面角色的代码来修复;

应用场景:

  • 为一个复杂的模块或子系统提供一个供外界访问的接口,如Android的Context类只是一个抽象类,所有的功能都是在ContextImpl类实现的,我们不会察觉到ContextImpl的存在,只需要调用Context就可以了;
  • 子系统相对独立,外界对子系统的访问只要黑箱操作即可;
  • 预防低水平开发人员带来的风险,被限定在指定的子系统开发;

备忘录模式

应用场景:

  • 需要保存和恢复数据的相关状态场景;
  • 提供一个可回滚的操作场景;
  • 需要监控的副本场景中;
  • 数据库连接的事务管理就是用的备忘录模式;

注意事项:

  • 备忘录的生命期,要主动管理它的生命周期,建立就要使用,不使用就删除;
  • 备忘录的性能,不要在频繁建立备份的场景中使用备忘录模式;(对象的创建是需要消耗资源的)

访问者模式

优点:

  • 符合单一职责原则,具体元素角色负责数据的加载,而访问者类则负责数据的呈现;
  • 优秀的扩展性,
  • 灵活性非常高;

缺点:

  • 具体元素对访问者公布细节,访问者要访问一个类就必须要求这个类公布一些方法和数据;
  • 具体元素变更比较困难;具体元素角色的增加、删除、修改都是比较困难;
  • 违背了依赖倒置原则,访问者依赖的是具体的元素,而不是抽象的元素;

应用场景 :

  • 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作;
  • 需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作”污染“这些对象的类;
  • 业务规则要求遍历多个不同的对象;

状态模式

优点:

  • 结构清晰,避免了过多的switch...caseif...else语句的使用;
  • 遵循设计原则,每个状态就是一个子类;
  • 封装性非常好,将状态变换放置到类的内部来实现;

缺点:

  • 子类会太多,也就是类膨胀,有多少个状态,就会有多少个子类;

    应用场景:

  • 行为随状态改变而改变的场景,如权限设计;
  • 条件、分支判断语句的替代者,通过扩展子类实现条件的判断处理;
  • 状态的个数最好不要超过5个;

解释器模式(现在使用较少)

优点:

  • 扩展性好,

缺点:

  • 解释器模式会引起类膨胀;
  • 采用了递归调用方法;

享元模式

优点:

  • 大大减少应用程序创建的对象,降低程序内存的占用;

缺点:

  • 提高了系统复杂性,需要分离出内部和外部状态;

应用场景:

  • 系统中存在大量的相似对象;
  • 需要缓冲池的场景;
  • 细粒度的对象都具有较接近的外部状态;且内部状态与环境无关

桥梁模式

优点:

  • 抽象与实现分离;
  • 优秀的扩充能力;
  • 实现细节对客户透明;

应用场景:

  • 不希望或不适用继承的场景;
  • 接口或抽象类不稳定的情况;
  • 重要性要求较高的场景;

转载于:https://www.cnblogs.com/WoodJim/p/4715385.html

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

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

相关文章

How Many Shortest Path

zoj2760:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode2760 题意:给你一张有向带权图,然后问你最短路径有多少条。 题解:这一题用到了网络流,一开始,我想到用找到一条最短路,然后删除这条…

pat00-自测5. Shuffling Machine (20)

00-自测5. Shuffling Machine (20) 时间限制400 ms内存限制65536 kB代码长度限制8000 B判题程序Standard作者CHEN, YueShuffling is a procedure used to randomize a deck of playing cards. Because standard shuffling techniques are seen as weak, and in order to avoid …

E488: Trailing characters:

情景: 对vim进行配置,配置完成后进行保存,配置完成后打开其他文件时报错。原因: vim 配置文件中保存不合乎语法的语句,报错时如下: #显示行号 set number#字符导致的错误,改成"即可。 vi…

移动web开发总结

1、-webkit-tap-highlight-color:rgba(255,255,255,0)可以同时屏蔽ios和android下点击元素时出现的阴影。 备注:transparent的属性值在android下无效。2、-webkit-appearance:none可以同时屏蔽输入框怪异的内阴影。3,/*去除android浏览器下a/input等元素获得焦点时高…

人物角色群体攻击判定二(叉乘来判断敌人的位置)

建议阅读: 判断敌人在玩家的某一个区域: http://www.cnblogs.com/plateFace/p/4716799.html 我们可以根据玩家和敌人的坐标, 进行叉乘来获取一个向量可以用它来判断敌人的位置, 敌人是否在攻击范围内. 下面我简单实现下对单体敌人是否攻击做判定 这种方式有一种重大的BUG, 假设…

更改linux子系统软件源为国内镜像

cd /etc/apt/sudo cp sources.list sources.list.back20190831sudo vim sources.list执行vim替换命令 :%s/archive.ubuntu/mirrors.aliyun/g:%s/security.ubuntu/mirrors.aliyun/g执行sudo apt update即可。

[Z] Linux下进程的文件访问权限

原文链接:http://blog.csdn.net/chosen0ne/article/details/10581883对进程校验文件访问权限包括两个部分,一是确定进程的角色(属于哪个用户或者组),二是确定对应的角色是否具有该操作的权限。 首先看第一部分。默认情…

HDU 5371 Manacher Hotaru's problem

求出一个连续子序列,这个子序列由三部分ABC构成,其中AB是回文串,A和C相同,也就是BC也是回文串。 求这样一个最长的子序列。 Manacher算法是在所有两个相邻数字之间插入一个特殊的数字,比如-1, Manacher算法…

MySQL CURDATE() 函数

定义和用法 CURDATE() 函数返回当前的日期。 语法 CURDATE() 实例 例子 1 下面是 SELECT 语句: SELECT NOW(),CURDATE(),CURTIME() 结果类似: NOW()CURDATE()CURTIME()2008-12-29 16:25:462008-12-2916:25:46例子 2 下面的 SQL 创建带有日期时间列 (Orde…

平庸技术流,用 WebApi +AngularJS 实现网络爬虫

最近园子里网络爬虫很火爆,从 PHP 到 Python,从 windows服务 到 winform 程序,各路大神各显神通。小弟也献下丑,从平庸流出发,简述下 WebApi AngularJS 方式实现网络爬虫。 一、技术框架 1.1 前端: Angular…

linker `cc` not found

运行rustc hello_world.rs时出错。原因: 我的 gcc 是安装的指定版本 gcc-4.8,安装指定版本 gcc 可参考我的另一篇博文,这里找不到 cc 的原因是在移除原来软链的时候,cc 的软链也移除了。重新建立软链即可。 sudo ln -s gcc cc还有…

C# 通过服务启动窗体(把窗体添加到服务里)实现用户交互的windows服务[转发]...

由于个人需要,想找一个键盘记录的程序,从网上下载了很多,多数都是需要注册的,另外也多被杀软查杀。于是决定自己写一个,如果作为一个windows应用程序,可以实现抓取键盘的记录。想要实现随系统启动的话&…

error: default argument given for parameter 4

原因&#xff1a;定义函数的时候参数部分有默认值&#xff0c;如下&#xff1a; int classA::print(int a 0) {std::cout << a << std::endl; }分析&#xff1a;声明函数时参数可以有默认值&#xff0c;定义时不能。

python2.7虚拟环境virtualenv安装及使用

一 、虚拟环境virtualenv安装 1. 安装virtualenv 将Python的目录添加到系统环境变量后&#xff0c;在命令行输入&#xff1a; pip install virtualenv C:\Users\heroicai\Desktop>pip install virtualenv2. 建立虚拟环境 在桌面上建立建立一个虚拟环境myenv,输入:virtualenv…

Io 异常: The Network Adapter could not establish the connection

Io 异常: The Network Adapter could not establish the connection 这个异常的出现一般与数据库和你的PC的设置有关 这种异常的出现大致上有下面几种&#xff1a; 1。IP错误。 在设置URL时错误&#xff0c;例如&#xff1a;jdbc:oracle:thin:192.168.0.36:1521:sharp 数据库服…

git 删除tag

git tag -d v1.0如果 tag 已经在远程分支&#xff0c;还需执行一句git push origin :refs/tags/v1.0另&#xff1a;打 tag 的时候最好加上 description&#xff0c;防止出现未知的错误&#xff0c;如 Jenkins 集成的时候生成的包名不对等。

leetcode 的shell部分4道题整理

对shell的某些细节还不是十分熟悉&#xff0c;借鉴了好多别人的东西 1. Word Frequency此题很简单&#xff0c;只要能排序就可以cat words.txt |tr -s " " "\n" sort | unique -c | sort -r | awk {print $2" "$1}2. Valid Phone Numbers cat …

Mysql操作集锦

mysql安装成功后可以看到已经存在mysql、information_schema和test这个几个数据库&#xff0c;information_schema库中有一个名为COLUMNS的表&#xff0c;这个表中记录了数据库中所有表的字段信息。知道这个表后&#xff0c;获取任意表的字段就只需要一条select语句即可。 例如…

shadows a parameter

原因&#xff1a;函数内声明变量与参数名相同。 如&#xff1a; void print(int hello) {int hello;std::cout << hello << std::endl; }解决办法&#xff1a;改变参数参数名或者局部变量名

iOS 9之WatchKit for WatchOS 2

金田&#xff08;github示例源码&#xff09; 自AppleWatch发行的同时就可以为AppWatch开发相应的应用程序&#xff0c;不过最初的版本&#xff0c;能开发的功能极为有限&#xff0c;所以也只是有少数的App厂商为Apple定制了App&#xff0c;所以迄今为止&#xff0c;Apple Stor…