设计模式介绍

对于有经验的开发人员,学习设计模式有助于我们找到在软件开发过程中所面临的问题的最佳解决方案。一直以来软件都是为了用来解决现实生活中遇到的复杂问题而存在,设计模式(Design pattern)就像一套基础武功心法,每一式都代表了一类问题的最佳实践,且可根据实际情况组合使用。本系列文章笔者将带大家一起从零开始学习设计模式,后面会逐个剖析23种设计模式在Java中的具体实现,读者需具备基本的Java编程概念。

文末有福利放送,感谢读者的阅读。

设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的

设计模式可以通过提供经过测试和验证的开发范例来加快开发过程

重用设计模式有助于防止可能导致重大问题的微妙问题,同时也提高了熟悉模式的程序员和架构师的代码可读性

什么是GOF(四人帮,全拼Gang of Four)

在 1994 年,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版了一本名为 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 的书,该书首次提到了软件开发中设计模式的概念。

四位作者合称 GOF(四人帮,全拼 Gang of Four)。他们所提出的设计模式主要是基于以下的面向对象设计原则。

  • 对接口编程而不是对实现编程。
  • 优先使用对象组合而不是继承。

用途

设计模式的主要用途有两个,一是提供了一个标准的术语系统,且具体到特定的场景。如单例设计模式意味着使用单个对象,这样所有熟悉单例设计模式的开发人员都能使用单个对象,并且可以通过这种方式告诉对方,程序使用的是单例模式。二是提供了最佳的实践,设计模式已经经历了很长一段时间的发展,它们提供了软件开发过程中面临的一般问题的最佳解决方案。学习这些模式有助于经验不足的开发人员通过一种简单快捷的方式来学习软件设计

设计模式的类型

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

J2EE设计模式

。这些设计模式从易到难可以分为三个等级:Difficulty-Beginner(难度-初学者), Difficulty-Intermediate(难度-中级) & Difficulty-Expert(难度-专家),后续文章我将从易到难的为大家介绍不同类型的设计模式。

下面一张图片整体描述了一下设计模式之间的关系:

设计模式的六大原则

1、开闭原则(Open Close Principle)

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

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

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

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

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

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

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

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

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

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

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

设计模式的思考

在学习和理解设计模式之前,你应该熟悉一些编程/软件设计原则。 所有的设计都应该尽可能简单,从最简单的事情开始,慢慢延伸,这可能是工作学习的原理。

只有在实际要求可扩展性,需要复杂性和套路模式时,才应该引入它们(设计模式)。 一旦你熟悉了这些设计模式的概念,你就可以根据实际工程情况选择最适合的模式来优雅的实现工程目标。

工厂模式

工厂模式也被称之为虚拟构造函数(Virtual Constructor),是Java中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

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

意图

定义用于创建对象的接口,但是让子类决定实例化哪个类。factory方法允许类将实例化推迟到子类

主要解决:接口选择的问题。

何时使用:我们明确地计划不同条件下创建不同实例时。

如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。

关键代码:创建过程在其子类执行。

解释

现实世界的例子

铁匠制造武器。精灵需要精灵武器,兽人需要兽人武器。根据手头的顾客,召集合适类型的铁匠

简而言之

它提供了一种将实例化逻辑委托给子类的方法

维基百科说

In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor.(在基于类的编程中,factory方法模式是一种创建模式,它使用factory方法来处理创建对象的问题,而不必指定将要创建的对象的确切类。这是通过调用factory方法(在接口中指定并由子类实现,或者在基类中实现并可选地由派生类重写)来实现的,而不是通过调用构造函数来实现的。)

程序代码示例

以现实世界的例子铁匠制造武器为例子,需要什么样的武器类型我们就召唤对应类型的铁匠。程序类图如下:

铁匠接口实现类图.png

首先我们有一个铁匠接口(定义了一个制造武器的方法)和一些精灵铁匠、兽人铁匠实现类:

public interface Blacksmith {Weapon manufactureWeapon(WeaponType weaponType);
}
public class ElfBlacksmith implements Blacksmith {public Weapon manufactureWeapon(WeaponType weaponType) {return new ElfWeapon(weaponType);}
}
public class OrcBlacksmith implements Blacksmith {public Weapon manufactureWeapon(WeaponType weaponType) {return new OrcWeapon(weaponType);}
}
复制代码复制代码

其次我们有一个武器接口(定义了一个获取武器类型的方法)和一些精灵武器、兽人武器实现类:

/**
* Weapon interface.
*/
public interface Weapon {WeaponType getWeaponType();
}
/**
* ElfWeapon.
*/
public class ElfWeapon implements Weapon {private WeaponType weaponType;public ElfWeapon(WeaponType weaponType) {this.weaponType = weaponType;}@Overridepublic String toString() {return "Elven " + weaponType;}@Overridepublic WeaponType getWeaponType() {return weaponType;}
}
/**
* OrcWeapon.
*/
public class OrcWeapon implements Weapon {private WeaponType weaponType;public OrcWeapon(WeaponType weaponType) {this.weaponType = weaponType;}@Overridepublic String toString() {return "Orcish " + weaponType;}@Overridepublic WeaponType getWeaponType() {return weaponType;}
}
复制代码复制代码

最后,随着顾客的到来,正确类型的铁匠被召唤出来,要求制造武器

public class App {private static final Logger LOGGER = LoggerFactory.getLogger(App.class);private final Blacksmith blacksmith;/*** Creates an instance of <code>App</code> which will use <code>blacksmith</code> to manufacture * the weapons for war.* <code>App</code> is unaware which concrete implementation of {@link Blacksmith} it is using.* The decision of which blacksmith implementation to use may depend on configuration, or* the type of rival in war.* @param blacksmith a non-null implementation of blacksmith*/public App(Blacksmith blacksmith) {this.blacksmith = blacksmith;}/*** Program entry point* * @param args command line args*/public static void main(String[] args) {// Lets go to war with Orc weaponsApp app = new App(new OrcBlacksmith());app.manufactureWeapons();// Lets go to war with Elf weaponsapp = new App(new ElfBlacksmith());app.manufactureWeapons();}private void manufactureWeapons() {Weapon weapon;weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR);LOGGER.info(weapon.toString());weapon = blacksmith.manufactureWeapon(WeaponType.AXE);LOGGER.info(weapon.toString());}
}
复制代码复制代码

运行App程序结果:

app类运行结果输出.png

适用场景

当遇到如下三种情况时,应使用工厂模式:

  1. 一个类不能预测它必须创建的对象的类
  2. 一个类希望它的子类指定它创建的对象
  3. 类将责任委托给几个助手子类中的一个,并且你希望本地化哪个助手子类是委托的责任

Java中的现实例子

  • java.util.Calendar
  • java.util.ResourceBundle
  • java.text.NumberFormat
  • java.nio.charset.Charset
  • java.net.URLStreamHandlerFactory
  • java.util.EnumSet
  • javax.xml.bind.JAXBContext

优缺点

优点:

  1. 一个调用者想创建一个对象,只要知道其名称就可以了。
  2. 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
  3. 屏蔽产品的具体实现,调用者只关心产品的接口

缺点:

每次增加一个产品时,都需要增加一个具体实现类和修改对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事

最后

工厂模式作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂模式,比如设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个通讯接口。

对于简单对象,特别是只需要通过new就可以完成创建的对象,无需使用工厂模式,因为使用工厂模式必然要引入一个工厂类,这会增加系统的复杂度,切不可为了设计模式而模式。

之后我们将在工厂模式的基础上继续延伸介绍抽象工厂模式,难度系统为中级

推荐一个交流学习圈子群:697-57-9751 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多:

阿里P7大牛细说架构——设计模式专栏相关推荐

  1. 数字式声纳设计原理 pdf_阿里P7大牛,深入剖析JVM底层设计原理+高级特性pdf,附46页ppt...

    前言 JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的. 引入J ...

  2. java 底层ppt_阿里P7大牛,深入剖析JVM底层设计原理+高级特性pdf,附46页ppt

    前言 JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的. 引入J ...

  3. 阿里P7大牛整理!BAT大厂面试基础题集合,成功入职字节跳动

    都说大厂面试必问源码,可很多人看完MMKV 源码.Handler 源码.Binder 源码.OkHttp 源码等源码记不住,是脑子有问题吗?当然不是!是因为你没有掌握学习源码的技巧. 我的朋友子路,很 ...

  4. 阿里P7大牛手把手教你!mysql数据库应用案例教程

    四面阿里 面试岗位是研发工程师,直接找蚂蚁金服的大佬进行内推,参与了阿里巴巴中间件部门的提前批面试,一共经历了四次面试,拿到了口头offer. 一面: 自我介绍 项目中做了什么,难点呢. Java的线 ...

  5. 阿里P7大牛亲自教你!面试竟然被这31道Java基础题难倒了

    一.概述 本文主要来分析JMM内存模型,英文名JAVA Memory Model,它是与计算机硬件有关的一个概念.为了保证共享内存的正确性(可见性.有序性.原子性),内存模型定义了共享内存系统中多线程 ...

  6. 阿里P7大牛整理!原来SqlSession只是个甩手掌柜?爱了爱了

    我有话要说,请仔细看完 我发现一个现象,很多开发5年的程序员仍然停留在crud的阶段,这是什么原因? 最主要的原因就是基础很差,尤其对于JVM和并发编程这方面掌握的比较差,而JVM和并发编程就是非常非 ...

  7. 阿里P7大牛手把手教你!一眼就能看懂的Android自学手册,真香!

    前言 曾听过很多人说Android学习很简单,做个App就上手了,工作机会多,毕业后也比较容易找工作.这种观点可能是很多Android开发者最开始入行的原因之一. 在工作初期,工作主要是按照业务需求实 ...

  8. 阿里P7大牛亲自教你!BAT这种大厂履历意味着什么?积累总结

    金九银十过后各大网络平台都是各种面经分享,包括已收offer,或面试失败的都有,相信大部分人都拿到了自己心仪的大厂offer,不过也有没有少数没能进到自己内心向往的大厂而懊恼的,那么到底如何才能进大厂 ...

  9. 阿里P7大牛整理!啃完999页Android面试高频宝典,附面试题答案

    背景 首先我是个菜鸡,工资也低的一笔. 刚毕业时候在一家国企上班干 app 开发,干了快两年的时候,跳槽到了一家伪大厂干安全.投了不少简历都没有回音,只有这加伪大厂要我就来了.当时说好了会接触一些底层 ...

最新文章

  1. c语言gcno文件位置,makefile - 具有覆盖率的CMake Ninja将gcno文件放在根二进制目录中 - 堆栈内存溢出...
  2. 网页里显示访问的那台服务器,在web服务器中把网页放在那里,才能被访问
  3. 信息系统项目管理师-成本管理知识点
  4. Discuz常见小问题-_如何修改网站LOGO
  5. 细水长flow之f-VAEs:Glow与VAEs的联姻
  6. GROUP BY 和 ORDER BY 同时使用问题
  7. c++用WinForm做界面的实现
  8. verycd重整——CBT系列
  9. cat3 utp是不是网线_小科普 | 网线也有高低?聊聊网线的差别
  10. 重新分析connection reset by peer, socket write error错误原因
  11. oracle 12c sp2 0667,关于数据库安装的问题SP2-0667: Message file sp1.msb not found
  12. Node.js 解压版 安装配置
  13. 学机器学习怎么可以不知道最小二乘法
  14. Docker——网络
  15. Unity笔记-29-ARPG游戏项目-05-简易的战斗系统
  16. 领导看了会炸毛的溢出理论
  17. Resistors in Parallel(Gym - 102028E 2018 ICPC 焦作E题 大数+规律C++版)
  18. 【dojo】dojo.ready(dojo.addOnLoad) “前传”
  19. 2021计算机考研408计算机学科专业基础综合冲刺复习提纲
  20. 线性CCD——从原理到跑路,适用于Arduino的底层驱动分析

热门文章

  1. 一段MySQL的小总结
  2. 武汉科技大学计算机院考研大纲,2021武汉科技大学考研大纲参考书目
  3. redis缓存雪崩、缓存击穿和缓存穿透还傻傻分不清?看这篇就够了
  4. Linux文件打包及压缩、解包及解压
  5. 王欣回应微信封禁,解释为何取名叫马桶MT
  6. c语言入门(个人自我介绍数据模板)
  7. python 音频特征提取工具库
  8. 2021-2027全球与中国电动助力车市场现状及未来发展趋势
  9. DBCO-PEG-Silane,DBCO-PEG-SIL,二苯并环辛炔-聚乙二醇-硅烷可用于修饰糖肽
  10. 判断mask是否包含孔洞的小trick