面对复杂的业务场景,千变万化的客户需求,如何以一变应万变,以最小的开发成本快速落地实现,同时保证系统有着较低的复杂度,能够保证系统后续de持续迭代能力,让系统拥有较高的可扩展性。

这些是一个合格的架构师必须修炼的基础内功,但是如何修炼这门神功???

不要着急,慢慢看下去。学了真本事,拿了阿里、头条的offer,女神还会远吗!❤️????????

接下来我们来系统性汇总下,软件架构设计需要知晓的设计模式,主要是提炼精髓、核心设计思路、代码示例、以及应用场景等。

CRUD很多人都会,不懂设计模式也可以开发软件,但是当开发及维护大型软件系统过程中就痛苦不堪,懂了人自然听得懂我在说什么,不懂的人说了你也不会懂。

我将常用的软件设计模式,做了汇总,目录如下:

考虑到内容篇幅较大,为了便于大家阅读,将软件设计模式系列(共23个)拆分成四篇文章,每篇文章讲解六个设计模式,采用不同的颜色区分,便于快速消化记忆

本文是首篇,主要讲解单例模式建造者模式抽象工厂工厂方法原型模式适配器模式,共6个设计模式。

1、单例模式

定义:

单例模式(Singleton)允许存在一个和仅存在一个给定类的实例。它提供一种机制让任何实体都可以访问该实例。

核心思路:

1️⃣ 保证一个类只有一个实例。如果该对象已经被创建, 则返回已有的对象。为什么要这样设计呢?因为某些业务场景要控制共享资源 (例如数据库或文件) 的访问权限。

2️⃣ 为该实例提供一个全局访问入口, 提供一个static访问方法。

代码示例:

/*** @author 微信公众号:微观技术*/
public class Singleton {private static Singleton instance = new Singleton();// 让构造函数为 private,这样该类就不会被实例化private Singleton() {}// 获取唯一可用的对象public static Singleton getInstance() {return instance;}
}

在类中添加一个私有静态成员变量用于保存单例实例,声明一个公有静态构建方法用于获取单例实例。

注意事项:

多个业务场景,多个线程访问同一个类实例的全局变量,频发的写操作,可能会引发线程安全问题。另外,为了防止其他对象使用单例类的 new 运算符,编码时需要将默认构造函数设为私有。

如果想要采用延迟初始化对象,多线程并发初始化时,可能会有并发安全问题。假如:线程A,线程B都阻塞在了获取锁的步骤上,其中线程A获得锁---实例化了对象----释放锁;之后线程B---获得锁---实例化对象,此时违反了我们单例模式的初衷。

如何解决?

采用双重判空检查。首先保证了安全,且在多线程情况下能保持高性能,第一个if判断避免了其他无用线程竞争锁造成性能浪费,第二个if判断能拦截除第一个获得对象锁线程以外的线程。

/*** @author 微信公众号:微观技术*/
public class SingleonLock {private static SingleonLock doubleLock;private SingleonLock() {}// 双重校验锁public static SingleonLock getInstance() {if (doubleLock == null) {synchronized (SingleonLock.class) {if (doubleLock == null) {doubleLock = new SingleonLock();}}}return doubleLock;}
}

2、建造者模式

定义:

建造者模式,也称 Builder 模式。

将复杂对象的构造与其表示分离,以便同一构造过程可以创建不同的表示。

简单来说,建造者模式就是如何一步步构建一个包含多个组成部件的对象,相同的构建过程可以创建不同的产品

核心思路:

角色 类别 说明
Builder 接口或抽象类 抽象的建造者,不是必须的
ConcreteBuilder 具体的建造者 可以有多个「因为每个建造风格可能不一样」,必须要有
Product 普通类 最终构建的对象,必须要有
Director 指挥者 统一指挥建造者去建造目标,不是必须的

代码示例:

/*** @author 微信公众号:微观技术*/
public class Person {private String name;private int age;private String address;public static PersonBuilder builder() {return new PersonBuilder();}private Person(PersonBuilder builder) {this.name = builder.name;this.age = builder.age;this.address = builder.address;}// 建造者static class PersonBuilder {private String name;private int age;private String address;public PersonBuilder() {}public PersonBuilder name(String name) {this.name = name;return this;}public PersonBuilder age(int age) {this.age = age;return this;}public PersonBuilder address(String address) {this.address = address;return this;}public Person build() {return new Person(this);}}}
  • Person 中创建一个静态内部类 PersonBuilder,然后将 Person 中的参数都复制到 PersonBuilder类中。

  • Person中创建一个private的构造函数,入参为 PersonBuilder类型

  • PersonBuilder中创建一个public的构造函数

  • PersonBuilder中创建设置函数,对Person 中那些可选参数进行赋值,返回值为PersonBuilder类型的实例

  • PersonBuilder中创建一个build()方法,在其中构建Person 的实例并返回

/*** @author 微信公众号:微观技术*/
public class PersonBuilderTest {public static void main(String[] args) {Person person = Person.builder().name("Tom哥").age(18).address("杭州").build();System.out.println(JSON.toJSONString(person));}}

客户端使用链式调用,一步一步的把对象构建出来。

适用场景:

  • 分阶段、分步骤的方法更适合多次运算结果类创建场景。比如创建一个类实例的参数并不会一次准备好,有些参数可能需要调用多个服务运算后才能拿得到,这时,我们可以根据已知参数,预先对类进行创建,待后续的参数准备好了后,再设置。

  • 不关心特定类型的建造者的具体算法实现。比如,我们并不关心StringBuilder的具体代码实现,只关心它提供了字符串拼接功能。

使用建造者模式能更方便地帮助我们按需进行对象的实例化,避免写很多不同参数的构造函数,同时还能解决同一类型参数只能写一个构造函数的弊端。

最后,实际项目中,为了简化编码,通常可以直接使用lombok@Builder 注解实现类自身的建造者模式

3、抽象工厂模式

定义:

抽象工厂模式围绕一个超级工厂创建其他工厂,又称为其他工厂的工厂。是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。

抽象工厂模式的关键点:如何找到正确的抽象。

对于软件调用者来说,他们更关心软件提供了什么功能。至于内部如何实现的,他们并不关心。另外,考虑到安全问题,一般内部具体的实现细节通常会隐藏掉。

我们以电视、冰箱、洗衣机等家用电器生产为例,很多厂商像HaierSony小米Hisense等能生产上述电器,不过在外观、性能、功率、智能化、特色功能等方面会有差异。面对这样的需求,我们如何借助抽象工厂模式来实现编码。

抽象工厂模式体现为定义一个抽象工厂类,多个不同的具体工厂继承这个抽象工厂类后,再各自实现相同的抽象功能,从而实现代码上的多态性

代码示例:

/*** @author 微信公众号:微观技术*/
public abstract class AbstractFactory {// 生产电视abstract Object createTV();// 生产洗衣机abstract Object createWasher();// 生产冰箱abstract Object createRefrigerator();}public class HaierFactory extends AbstractFactory {@OverrideObject createTV() {return null;}@OverrideObject createWasher() {return null;}@OverrideObject createRefrigerator() {return null;}
}public class XiaomiFactory extends AbstractFactory {@OverrideObject createTV() {return null;}@OverrideObject createWasher() {return null;}@OverrideObject createRefrigerator() {return null;}
}

AbstractFactory是抽象工厂类,能够创建电视、洗衣机、冰箱抽象产品;而HaierFactoryXiaomiFactory 是具体的工厂,负责生产具体的产品。当我们要生产具体的产品时,只需要告诉AbstractFactory即可。

解决问题:

  • 对于不同产品系列有比较多共性特征时,可以使用抽象工厂模式,有助于提升组件的复用性。

  • 当需要提升代码的扩展性并降低维护成本时,把对象的创建和使用过程分开,能有效地将代码统一到一个级别上。

适用场景:

  • 解决跨平台兼容性的问题。当一个应用程序需要支持Windows、Mac、Linux等多套操作系统。

  • 电商的商品、订单、物流系统,需要根据区域政策、用户的购买习惯,差异化处理

  • 不同的数据库产品,JDBC 就是对于数据库增删改查建立的抽象工厂类,无论使用什么类型的数据库,只要具体的数据库组件能够支持 JDBC,就能对数据库进行读写操作。

4、工厂方法模式

工厂方法模式与抽象工厂模式类似。工厂方法模式因为只围绕着一类接口来进行对象的创建与使用,使用场景更加单一,项目中更常见些。

定义

定义一个创建对象的接口,让其子类自己决定实例化哪一个类,工厂模式使其创建过程延迟到子类进行。

核心点:封装对象创建的过程,提升创建对象方法的可复用性。

工厂方法模式包含三个关键角色:抽象产品、具体产品、工厂类。

定义一个抽象产品接口ITVHaierTVXiaomiTV是具体产品类,TVFactory是工厂类,负责生产具体的对象实例。

代码示例:

/*** @author 微信公众号:微观技术*/
public interface ITV {// 描述Object desc();
}public class HaierTV implements ITV {@Overridepublic Object desc() {return "海尔电视";}
}public class XiaomiTV implements ITV {@Overridepublic Object desc() {return "小米电视";}
}public class TVFactory {public static ITV getTV(String name) {switch (name) {case "haier":return new HaierTV();case "xiaomi":return new XiaomiTV();default:return null;}}
}public class Client {public static void main(String[] args) {ITV tv = TVFactory.getTV("xiaomi");Object result = tv.desc();System.out.println(result);}
}

工厂方法模式是围绕着特定的抽象产品(接口)来封装对象的创建过程,Client只需要通过工厂类来创建具体对象实例,然后就可以使用其功能。

工厂方法模式将对象的创建和使用过程分开,降低代码耦合性。

5、原型模式

原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的“原型”,这个原型是可定制的。

定义:

使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。

代码示例:

/*** @author 微信公众号:微观技术*/
public interface Prototype extends Cloneable {public Prototype clone() throws CloneNotSupportedException;
}public class APrototype implements Prototype {@Overridepublic Prototype clone() throws CloneNotSupportedException {System.out.println("开始克隆《微观技术》对象");return (APrototype) super.clone();}
}public class Client {@SneakyThrowspublic static void main(String[] args) {Prototype a = new APrototype();Prototype b = a.clone();System.out.println("a的对象引用:" + a);System.out.println("b的对象引用:" + b);}
}

执行结果:

开始克隆《微观技术》对象
a的对象引用:course.p14.p5.APrototype@7cc355be
b的对象引用:course.p14.p5.APrototype@6e8cf4c6

打印出两个对象的地址,发现不相同,在内存中为两个对象。

Cloneable 接口本身是空方法,调用的 clone() 方法其实是 Object.clone() 方法

优点:

  • 性能优良。不用重新初始化对象,而是动态地获取对象运行时的状态。

  • 可以摆脱构造函数的约束。

特别注意:

clone()浅复制,也就是基本类型数据,会给你重新复制一份新的。但是引用类型(对象中包含对象),他就不会重新复制份新的。引用类型如:bean实例引用、集合等一些引用类型。

如何解决?

你需要在执行完super.clone() 获得浅复制对象后,再手动对其中的全局变量重新构造对象并赋值。当然,经过这个过程,得到的对象我们称之为深复制

适用场景:

  • 反序列化,比如 fastjson的JSON.parseObject() ,将字符串转变为对象

  • 每次创建新对象资源损耗较大

  • 对象中的属性非常多,通过get和set方法创建对象,复制黏贴非常痛苦

加餐:

Spring 框架中提供了一个工具类,BeanUtils.copyProperties 可以方便的完成对象属性的拷贝,其实也是浅复制,只能对基本类型数据对象引用拷贝。使用时特别要注意,如果全局变量有对象类型,原型对象和克隆的对象会二次修改,要特殊处理,采用深复制,否则会引发安全问题。

6、适配器模式

我们都知道美国的电压是110V,而中国是220V,如果你去要美国旅行时,一定要记得带电源适配器,将不同国家使用的电源电流标准转化为适合我们自己电器的标准,否则很容易烧坏电子设备。

定义:

将类的接口转换为客户期望的另一个接口,适配器可以让不兼容的两个类一起协同工作。核心点在于转换!

核心思路:

在原有的接口或类的外层封装一个新的适配器层,以实现扩展对象结构的效果,并且这种扩展可以无限扩展下去。

  • Adaptee:源接口,需要适配的接口

  • Target:目标接口,暴露出去的接口

  • Adapter:适配器,将源接口适配成目标接口

适用场景:

  • 原有接口无法修改时,又必须快速兼容部分新功能

  • 需要依赖外部系统时,一般会单独封装防腐层,降低外部系统的突发风险带来的影响

  • 适配不同数据格式,不同接口协议转换

  • 旧接口过渡升级

案例:

比如查物流信息,由于物流公司的系统都是各自独立,在编程语言和交互方式上有很大差异,需要针对不同的物流公司做单独适配,同时结合不同公司的系统性能,配置不同的响应超时时间

适配器模式号称为“最好用打补丁模式”,就是因为只要是一个接口,都可以用它来进行适配。

写在最后

设计模式很多人都学习过,但项目实战时总是晕晕乎乎,原因在于没有了解其核心是什么,底层逻辑是什么,《设计模式:可复用面向对象的基础》有讲过,

在设计中思考什么应该变化,并封装会发生变化的概念。

软件架构的精髓:找到变化,封装变化。

业务千变万化,没有固定的编码答案,千万不要硬套设计模式。无论选择哪一种设计模式,尽量要能满足SOLID原则,自我review是否满足业务的持续扩展性。有句话说的好,“不论白猫黑猫,能抓老鼠就是好猫。”

面对复杂业务架构,阿里架构师是如何做的?(第一期)相关推荐

  1. 大话架构”阿里架构师的笔记——多研究些架构,少谈些框架

    2019独角兽企业重金招聘Python工程师标准>>> 引言:微服务现在辣么火,业界流行的对比的却都是所谓的Monolithic单体应用,而大量的系统在十几年前都是已经是分布式系统了 ...

  2. “大话架构”阿里架构师分享的Java程序员需要突破的技术要点

    一.源码分析 源码分析是一种临界知识,掌握了这种临界知识,能不变应万变,源码分析对于很多人来说很枯燥,生涩难懂. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 我认为是阅读源码的最核心 ...

  3. 阿里架构阿里架构师手把手带你学Mybatis开源ORM框架“

    简介 前段时间利用空闲时间,参照mybatis的基本思路手写了一个ORM框架.一直没有时间去补充相应的文档,现在正好抽时间去整理下.通过思路历程和代码注释,一方面重温下知识,另一方面准备后期去完善这个 ...

  4. 阿里架构师谈中台系列(二):阿里业务中台的方法论

    Photo @ David Emrich 文 |焦先 编者按: 阿里巴巴业务中台系列文章第一篇--<阿里巴巴架构师:十问业务中台和我的答案>自 2019 年 11 月 28 日发布至今日, ...

  5. 阿里架构师,谈对技术架构的理解,以及架构师角色的思考

    我叫道延, 2014 年加入阿里,在阿里通信工作了近两年.2016 年年底加入业务平台团队,当时 Leader 找我的第一件事就是要解决大促的问题,第二件事就是解决安全生产的问题. 我带着这个命题进入 ...

  6. 【十年磨一剑】我们能从阿里架构师的身上学到什么?

    前言 做技术的,一定不能放弃技术.在精进技术的同时完善其他方面的能力,十年如一日.不忘初心,方得始终. 正文 本文是看到阿里巴巴系统架构师黄勇的采访记录有感而发,如有侵权,请联系我.下面就一起来看看阿 ...

  7. 阿里架构师钟华,揭秘《阿里中台战略思想与实战》,ppt分享!

    点击"技术领导力"关注∆  每天早上8:30推送 本文整理了,阿里中间件首席架构师--钟华(古谦),在云栖大会上的分享,原主题为<企业核心业务数字化转型最佳实践>.如果 ...

  8. 阿里架构师,讲述分布式架构云平台解决方案(附学习路线)...

    技术架构 分布式架构云平台在充分分析IT技术发展趋势,遵循集中化.标准化.集成化.可靠化和可扩展化的设计原则,以价值创造为使命,以规范化.一体化.智能化的云平台为支撑,实现信息的透明共享.业务的敏捷协 ...

  9. 阿里Java架构师精通资料:性能优化+亿级并发架构汇总+架构选型

    分布式并发架构 微服务.Docker容器的基本原理.架构设计,以及应用场景. 缓存:Redis.Memcached.CDN.本地缓存 搜索引擎的选型:Lucene.Solr等选型与比较 应用服务器雪崩 ...

  10. 进阶阿里架构师:算法、编程语言、数据库、架构设计!书单推荐!

    阿里架构师必读书单 数据结构与算法:算法.算法导论等. 编程语言:java编程思想.java核心技术等 模式与设计:设计模式.代码重构.深入理解java虚拟机 数据库:mysql优化.oracle.r ...

最新文章

  1. Android Dialog 的使用总结(AlertDialog)
  2. 《数学之美》第16章 信息指纹及其应用
  3. JavaScript中的HTTP GET请求?
  4. 零基础开始学 Web 前端开发,有什么建议吗?--知
  5. 转载:工程师笔记|STM32F030在低温下无法启动
  6. python字符串应用
  7. 【渝粤题库】国家开放大学2021春3894理工英语1题目
  8. date数据要在前台显示
  9. Java里面遍历list的方式
  10. uml 类图_UML-类图
  11. c语言常用库函数使用方法,c语言常用库函数使用方法及用途
  12. php 的定界符 eof
  13. 浅析C#的事件处理和自定义事件 (转载)
  14. 微服务容错限流Hystrix入门
  15. 【架构】高可用高并发系统设计原则
  16. 嵌入式程序和FW的区别
  17. NIK插件-托马斯教程1-color efex pro 4
  18. u盘中毒文件为html文档,u盘中毒文件被隐藏怎么办【解决方法】
  19. 简易示波器c语言程序,单片机简易数字示波器仿真+源程序+电路原理图
  20. 微信和 QQ 终于可以互通了

热门文章

  1. java qq ui界面,java UI之QQ登录
  2. 与猜数问题有关的游戏C语言,猜数字游戏(C语言版)
  3. php自动轮播图代码,JavaScript如何实现动态轮播图效果?(代码示例)
  4. html文件 excel,怎么把html文件转成excel,excel表格代码
  5. 【Java并发编程】一、为什么需要学习并发编程?
  6. Android HAL模块的加载过程
  7. 03-vue-router
  8. MySql 高可用架构Atlas
  9. Selenium + Python + Chrome 自动化测试 环境搭建
  10. Nginx日志格式设置