设计模式(一):单例、工厂、原型、建造者、适配器
前言:本文为原创 若有错误欢迎评论!
一.UML基本介绍
1.概念:
统一建模语言 用于描述系统中的类(对象)本身的组成和类(对象)之间的各种静态关系
2.类之间的关系:
依赖(所有关系的本质):1) 类中用到了对方2) 如果是类的成员属性3) 如果是方法的返回类型4) 是方法接收的参数类型5) 方法中使用到泛化:实际上就是继承关系,他是依赖关系的特例实现:实现关系实际上就是A类实现B接口,他是依赖关系的特例关联:关联关系实际上就是类与类之间的联系,他是依赖关系的特例单向一对一关系双向一对一关系聚合:表示整体和部分的关系(整体与部分可以分开)聚合关系是关联关系的特例(即在类加载时的构造方法里没有实例化聚合的对象)组合:也是整体与部分的关系,但是整体与部分不可以分开。(即在类加载的时候就实例化要聚合的对象)
- 注意箭头方向依赖:
- 依赖:箭头指向的类是直线一段所要包含的
- 泛化和实现:箭头指向的是被继承的类
- 聚合和组合:菱形指向的类包含直线那一端的类)
二.七大原则
1.单一职责原则
即一个类只负责一项职责
2.接口隔离原则
- 一个类对另一个类的依赖应该建立再最小的接口上 不然就拆分成多个接口 然后继承多个接口
- 拆分为多个接口继承的话 因为设计的原则为了有个缓冲层 而不是直接new 一个继承后的类 所以应该再建立一个缓冲的类 这个类依赖多个接口 从而通过参数参入继承后的类
3.依赖倒转原则
高层模块不应该依赖低层模块,二者都应该依赖其抽象
抽象不应该依赖细节,细节应该依赖抽象
使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
原因:
- 相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多
- 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好.
- 变量的声明类型尽量是抽象类或接口, 这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化
4.里氏替换原则
所有引用基类的地方必须能透明地使用其子类的对象
在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法(有可能再调用父类方法的使用的子类已经改变了父类方法的作用)
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来解决问题。
通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系代替.
5.开闭原则 ocp
- 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
- 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
- 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则
6.迪米特法则
类与类关系越密切,耦合度越大 迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。
迪米特法则还有个更简单的定义:只与直接的朋友通信
- 直接的朋友:每个对象都会与其他对象有耦合关系(依赖,关联,组合,聚合等)其中,出现在成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
7.合成复用原则
尽量使用合成/聚合的方式,而不是使用继承
总结:1) 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。2) 针对接口编程,而不是针对实现编程。3) 为了交互对象之间的松耦合设计而努力接口的意义:1)解除了类和类之间的直接耦合 中间有一层接口(解耦的目的就是为了不只能依赖一个类 这样所有代码的编写都是针对一个类来实现的 没有办法拓展)2)兼容性和拓展性大幅增强 客户端面向接口编程 不再针对某个类 这样只要实现了接口的类 都可以和客户端无缝衔接 尤其在功能拓展上十分重要3)制定了规范 只要调用接口 不用理会其实现等 可以分模块协同开发
三.设计模式分类(23种)
1.创建型模式:
单例模式、工厂模式、抽象工厂模式、原型模式、建造者模式。
2.结构型模式:
适配器模式、桥接模式、装饰者模式、组合模式、外观模式、享元模式、代理模式。
3.行为型模式:
模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式(责任链模式)
四.单例模式
1.创建方式
饿汉式
// 饿汉式 如果肯定会使用到该对象推荐使用 不然可能造成内存浪费class Singleton {//1. 构造器私有化, 外部能newprivate Singleton() {}//2.本类内部创建对象实例private final static Singleton instance = new Singleton();//3. 提供一个公有的静态方法,返回实例对象public static Singleton getInstance() {return instance;}}
懒汉式(双重判断 只用一次synchronized)
// 懒汉式 通过双重判断使线程安全 并且不是整个代码块加锁 提高了效率 推荐使用class Singleton {private static volatile Singleton instance;private Singleton() {}//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题//同时保证了效率, 推荐使用public static Singleton getInstance() {if(instance == null) {synchronized (Singleton.class) {if(instance == null) {nstance = new Singleton();}}}return instance;}}
静态内部类
// 静态内部类完成, 推荐使用class Singleton {//构造器私有化private Singleton() {}//写一个静态内部类(静态内部类只有在调用的时候会被初始化)// 该类中有一个静态属性 Singleton(由于jvm在初始化静态变量时线程安全 所以只会有一个实例)private static class SingletonInstance {private static final Singleton INSTANCE = new Singleton();}//提供一个静态的公有方法,直接返回SingletonInstance.INSTANCEpublic static Singleton getInstance() {return SingletonInstance.INSTANCE;}}
枚举
//使用枚举,可以实现单例, java作者提倡的方式(枚举里列举的多个情况 永远一个情况只有一个实例化对象)enum Singleton {INSTANCE; //属性public void fun() {System.out.println("ok~");}}
2.注意事项和说明
单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
3.使用场景:
需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)
五.工厂模式(含抽象工厂模式)
1.简单工厂模式
介绍:
简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族
中最简单实用的模式
简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)例子uml(制作不同品种pizza):
其中:simplefactory没有实现接口 只是一个聚合了三个Pizza对象的类 每个订单又聚合了这个简单工厂 通过工厂返回对应的实例
2.工厂方法模式
介绍:
定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方
法模式将对象的实例化推迟到子类。例子uml(制作不同品种pizza):
其中:orderPizza的createPizza()抽象方法 返回pizza实例 由子类继承实现
3.抽象工厂模式
介绍:
- 抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的工厂簇,要使用工厂的类无需指明具体的类 只需要聚合这个工厂接口
- 抽象工厂模式是将简单工厂模式和工厂方法模式进行整合(从设计层面看,抽象工厂模式就是对简单工厂模式的改进 也是进一步的抽象)。
- 将工厂抽象成两层,AbsFactory(抽象工厂) 和 具体实现的工厂子类 可以根据创建对象类型使用对应的工厂子类(通过构造方法传入抽象工厂的实现类)。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。
例子uml(制作不同品种pizza):
其中:每个要使用的工厂的orderPizza类通过聚合absFactory 构造方法中传入其实现类BJFactory或者LDFactory 来实现对不同种类的工厂的使用
4.小结:
- 工厂模式的意义:将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目依赖关系的解耦。从而提高项目的扩展和维护性。
- 三种工厂模式 (简单工厂模式、工厂方法模式、抽象工厂模式)
- 符合设计模式的依赖抽象原则
- 注意:
- 创建对象实例时,不要直接 new 类, 而是把这个new 类的动作放在一个工厂的方法中并返回(有的书上说,变量不要直接持有具体类的引用)
- 不要让类继承具体类,而是继承抽象类或者是实现interface(接口)
- 不要覆盖基类中已经实现的方法。
5.使用场景:
当要用到大量的创建某种、某类或者某批对象时,即有大量相似的类时 就会使用到工厂模式管理到底创建哪个对象实例.(如一个接口下有多个实现类 通过工厂类来管理 决定返回哪个实例)
六.原型模式
1.基本介绍(Prototype Pattern)
- 原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
- 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
- 工作原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()
2.原理uml图
原理结构图说明:
1) Prototype : 原型类,声明一个克隆自己的接口
2) ConcretePrototype: 具体的原型类, 实现一个克隆自己的操作
3) Client: 让一个原型对象克隆自己,从而创建一个新的对象(属性一样)
通常情况为要有克隆功能的类implements Clonable 然后重写clone() 调用super.clone()
//克隆该实例,使用默认的clone方法来完成/** 例子:克隆羊*/@Overrideprotected Object clone() {Sheep sheep = null;try {sheep = (Sheep)super.clone();} catch (Exception e) {System.out.println(e.getMessage());}return sheep;}
3.浅拷贝
- 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
- 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
- 浅拷贝是使用默认的 clone()方法来实现 前面的"sheep = (Sheep) super.clone();" 就是浅拷贝
4.深拷贝
概念:
- 复制对象的所有基本数据类型的成员变量值
- 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
方式:
方式一:
重写clone方法来实现深拷贝(即:引用的属性也实现Cloneable接口并重写clone方法 然后调用该引用的clone()并赋给克隆出来的对象)
(省略) …方式二:
通过对象序列化实现深拷贝(推荐)
public Object deepClone() {//创建流对象ByteArrayOutputStream bos = null;ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;try {//序列化bos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);oos.writeObject(this); //当前这个对象以对象流的方式输出//反序列化bis = new ByteArrayInputStream(bos.toByteArray());ois = new ObjectInputStream(bis);DeepProtoType copyObj = (DeepProtoType)ois.readObject();return copyObj;} catch (Exception e) {e.printStackTrace();return null;} finally {//关闭流try {bos.close();oos.close();bis.close();ois.close();} catch (Exception e2) {System.out.println(e2.getMessage());}}}
5.小结
优点:
- 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
- 不用重新初始化对象,而是动态获得对象运行时的状态
- 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码(如果用get、set获得并设置每改变一个属性就要改变客户端代码)
缺点:
- 在实现深克隆的时候可能需要比较复杂的代码
- 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则
6.使用场景
总是需要频繁复制的类
七.建造者模式
1.基本介绍(Builder Pattern)
- 建造者模式又叫生成器模式,是一种对象构建模式。它将复杂对象的建造过程抽象出来(抽象类别),通过不同抽象建造过程的实现类可以构造出不同表现(属性)的对象。
- 建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定对象类型和构建过程就可以构建并返回建造过后的对象,用户不需要知道内部的具体构建细节。
2.原理uml图
建造者四个角色:
1) Product(产品角色): 一个具体的产品对象。
2) Builder(抽象建造者): 创建一个Product对象的各个部件指定的 接口或抽象类。
3) ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
4) Director(指挥者): 构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
3.例子uml(建房子)
- 其中在abstract HourseBulider中的buid方法如果有Director来指定建造过程 就可以不包含这个方法
- 可以没有Direct这个角色 将build方法不写为抽象 直接指定步骤 并返回被建造的对象
4.小结
- 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与创建过程解耦,使得相同的创建过程可以创建不同的产品对象
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同
的产品对象 - 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合 “开闭原则”
5.使用条件:
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式.
6.使用场景
如果为了加工(或在这个时间做些别的工作)、修改一个类 并且最后返回这个类
八.适配器模式
1.基本介绍(Adapter Pattern)
- 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名也为包装器(Wrapper)
- 适配器模式属于结构型模式
- 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
2.类适配器(例子:电压转化)
其中5v是接口且有一个未实现的输出方法 220v是类有输出220v的方法
适配器通过implements 5v extends 220v 从而达到接受5v参数的方法可以接受适配器
适配器在5v接口未实现的输出方法中调用220v的输出进行转化 使得外部可以正常使用转化后5v接口的输出方法
注意:类适配器不用在adapter的构造方法中传入被转化的对象
3.对象适配器(例子:电压转化)
- 注意:和类适配器的唯一区别是
- 适配器和被适配的类的关系由继承变为了聚合 所以在适配器的构造方法中必必须要传入被适配类的对象 但适配器同样是implements适配后的接口 并实现该接口的被调用方法
4.接口适配器(接口缺省适配器)
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
5.小结
三种命名方式,是根据 src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的。
类适配器:以:(类名)Adpater,在Adapter里 将src当做父类去继承extends
对象适配器:以:(对象名)Adpater,在Adapter里 将src作为一个adapter的属性 通过构造方法或者set方法传入
接口适配器:以:(接口名)Adpater,在Adapter里 将src作为一个接口,对于不想实现的方法用空实现
Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作,但这种思想也常被用作新建一个适配器类型的类 但名字不一定是…Adapter 总之其有一个方法的作用就是找到匹配的两个对象
(如springmvc的视图分发器DispatchServlet的doDispatch()就是为了匹配对应的controller和adpter)
实际开发中,实现起来不拘泥于三种经典形式
6.使用场景
主要还是用于接口的适配与转化 有些时候也会用于寻找一一匹配的两个对象(与工厂模式那种根据条件创建对应实例的区别是 工厂模式是条件和对象匹配 适配器模式是对象和对象匹配)
附学习视频和源码链接:https://pan.baidu.com/s/1ris3n-EF3lD1bxazK38oUw
提取码:qv48
复制这段内容后打开百度网盘手机App,操作更方便哦
设计模式(一):单例、工厂、原型、建造者、适配器相关推荐
- 【设计模式】创建者模式(单例工厂原型建造者)
文章目录 1.单例设计模式 1.1 单例模式概述 1.2 单例模式的实现 1.2.1 饿汉式(静态变量方式) 1.2.2 饿汉式(静态代码块方式) 1.2.3 懒汉式(线程不安全) 1.2.4 懒汉式 ...
- Java知识复习(六)常见的设计模式(单例、原型、工厂)
前言 发现无论是什么设计模式,其实总的原则就是减少耦合,增加可复用代码,使系统更易于扩展 参考书籍:<秒懂设计模式> 1.单例模式(Singleton) 单例模式:即单一的实例,同时提供几 ...
- PHP设计模式 二 (单例 工厂 注册)
单例模式 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源. ...
- C++设计模式之单例工厂模式
今天居家办公第65天,疫情期间,每天抢菜,抢菜..抢菜完成之后,就学习一会设计模式吧,今天学习的内容是工厂模式. 个人觉得工厂模式主要是为了把创建对象的代码放到专门的工厂类里,其实把new的工作延迟到 ...
- Java 设计模式归纳(观察者、工厂、单例、策略、适配器、命令、装饰者、外观、模板方法、状态
DesignPattern 项目地址:youlookwhat/DesignPattern 简介: Java 设计模式归纳 (观察者.工厂.单例.策略.适配器.命令.装饰者.外观.模板方法.状态). ...
- java软件设计模式只单例设计模式
概述 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计 ...
- 详略。。设计模式1——单例。。。。studying
设计模式1--单例 解决:保证了一个类在内存中仅仅能有一个对象. 怎么做才干保证这个对象是唯一的呢? 思路: 1.假设其它程序可以任意用new创建该类对象,那么就无法控制个数.因此,不让其它程序用ne ...
- spring中的单例工厂SingletonBeanRegistry设计与实现
单例工厂接口为SingletonBeanRegistry,主要是单例的注册,其默认实现为DefaultSingletonBeanRegistry 1.类层次图 2.单例工厂在循环依赖时的流程
- 游戏设计模式——C++单例类
前言: 本文将探讨单例类设计模式,单例类的懒汉模式/饿汉模式,单例类的多线程安全性,最后将利用C++模板减少单例类代码量. 本文假设有一个Manager管理类,并以此为探究单例类的设计模式. 懒汉模式 ...
- 高仿真的类-单例工厂的顶层设计
/*** 单例工厂的顶层设计*/ public interface V1BeanFactory {/*** 根据beanName从IOC容器中获得一个实例Bean* @param beanName* ...
最新文章
- 每日一博 - Review线程池
- 配置ntp时间服务器
- JavaScript: 取得 function 的所有参数名
- Unity3d通用工具类之定时触发器
- 沃尔什哈达玛变换Matlab,哈达玛变换矩阵-数字图像处理.ppt
- 运算符重载为类的友元函数
- 如何提高企业数据质量
- Elasticsearch Exception:The number of object passed must be even but was [1]
- Julia: Atom 来了!如何在Atom中操作Julia?
- 【美赛备赛】word编辑公式全攻略
- 【时间之外】金融数据中心机房应对监管(最新出炉)
- 【Python】绘制PR曲线
- word2007工具栏隐藏了怎样能一直显示?
- 一篇Chat(沉迷机器人操作系统(ROS)的一个理由和四种修仙秘籍)
- 北京东莞企业邮箱注册,外贸邮箱用哪个比较好?
- 第三届长安杯解析(2次修订版)镜像+具体解析+个人详细解题过程,涉及多个模块,我会努力把所有写好,可以做一下题目,提升很明显。
- Mixly 触摸开关的使用
- PCL 实战记录 (一)
- C语言,实现通讯录功能
- 2022年计算机软件水平考试多媒体应用设计师(中级)练习题及答案