• 设计模式(一)| 创建型模式(单例模式、原型模式等)


    UML以及七大原则总结

    !!!!

    设计模式总览

    在这里插入图片描述

    单例模式

    • 概述

    确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。

    • 饿汉式(无法实现延迟加载,系统一开始就存在这个对象无论你用不用)

    直接赋值

    public class EagerSingleton { 
        private static final EagerSingleton instance = new EagerSingleton(); 
        private EagerSingleton() { } 
     
        public static EagerSingleton getInstance() {
            return instance; 
        }
    }
    
    
    public class EagerSingleton { 
        private static final EagerSingleton instance ;
        static {
                instance=new EagerSingleton(); 
        }  
        private EagerSingleton() { } 
     
        public static EagerSingleton getInstance() {
            return instance; 
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    枚举

    public enum EnumSingleton {
        /**
         * 单例对象
         */
        instance;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 懒汉式(受制于锁的性能限制)

    这里对于懒汉式的双重锁定做了很详细的解析

    /**
     * @author lhj
     * @create 2022/4/13 19:26
     * 双重锁检测
     */
    public class VUGDoubleLock {
        private static volatile VUGDoubleLock vugDoubleLock;
    
        private VUGDoubleLock() {
        }
    
        public static VUGDoubleLock getInstance(){
            if(vugDoubleLock == null)
                synchronized (VUGDoubleLock.class){
                    if(vugDoubleLock == null)
                        vugDoubleLock = new VUGDoubleLock();
                }
            System.out.println("双重检测锁!");
            return vugDoubleLock;
        }
    
        public static void main(String[] args) {
            VUGDoubleLock instance = VUGDoubleLock.getInstance();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • IoDH

    汉式单例类不能实现延迟加载,不管将来用不用始终占据内存;懒汉式单例类线程安全控制烦琐,而且性能受影响

    Initialization Demand Holder (IoDH):在IoDH中,我们在单例类中增加一个静态(static)内部类,在该内部类中创建单例对象,再将该单例对象通过getInstance()方法返回给外部使用

    由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。

    通过使用IoDH,我们既可以实现延迟加载,又可以保证线程安全,不影响系统性能,不失为一种最好的Java语言单例模式实现方式(其缺点是与编程语言本身的特性相关,很多面向对象语言不支持IoDH)。

    public class VUGIoDH {
        private VUGIoDH() {}
    
        private static class HolderClass{
            private static final VUGIoDH instance = new VUGIoDH();
        }
    
        public static VUGIoDH getInstance(){
            System.out.println("IoDH单例对象建立");
            return HolderClass.instance;
        }
    
        public static void main(String[] args) {
            VUGIoDH instance = VUGIoDH.getInstance();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 优点
    • 提供了对唯一实例的受控访问
    • 可以节约系统资源,提高系统的性能
    • 允许可变数目的实例(多例类)

    缺点

    • 扩展困难(缺少抽象层)
    • 单例类的职责过重

    原型模式

    • 概述
    • 复制一个对象,从而克隆出多个与原型对象一模一样的对象——原型模式
    • 有些对象的创建过程较为复杂,而且需要频繁创建
    • 通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象

    在这里插入图片描述

    • Java中的克隆方式
    • 所有的Java类都继承自java.lang.Object,而Object类提供一个clone()方法,可以将一个Java对象复制一份
    • Java中可以直接使用Object提供的clone()方法来实现对象的克隆(浅克隆)
    • 能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持复制
    • 如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个CloneNotSupportedException异常
    this.clone() == this;		//false,浅拷贝
    
    • 1

    代码演示

    先要实现Cloneable接口,然后可以利用这个机制去得到存储在缓存中的对象,而对实际的对象不做任何影响,但是实际上有spring的Bean复制方法会更快

    class User implements Cloneable{
        String username;
        String password;
    
        /**
         * 实现克隆方法,通过原型创建一个克隆对象
         * @return
         * @throws CloneNotSupportedException
         */
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return new User(username, password);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 浅克隆:当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制

    在这里插入图片描述

    • 深克隆:除了对象本身被复制外,对象所包含的所有成员变量也将被复制

    在这里插入图片描述

    • 优点
    • 简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率
    • 扩展性较好
    • 简化创建结构,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品
    • 可以使用深克隆的方式保存对象的状态,以便在需要的时候使用,可辅助实现撤销操作
    • 缺点
    • 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了开闭原则
    • 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦

    工厂模式

    1.简单工厂模式

    • 概述
    • 简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式
    • 在简单工厂模式中,可以根据参数的不同返回不同类的实例
    • 简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类
    • 角色构成
      在这里插入图片描述

    • 代码实例

    /**
     * @author lhj
     * @create 2022/4/13 16:19
     */
    public class SimplyFactory {
        public static void main(String[] args) {
            Shape circle = ShapeFactory.initShape("Circle");
            circle.draw();
            circle.erase();
            System.out.println("-----------------");
            Shape triangle = ShapeFactory.initShape("Triangle");
            triangle.draw();
            triangle.erase();
        }
    }
    
    /**
     * @author lhj
     * @create 2022/4/13 16:04
     * 图形工厂
     */
    public class ShapeFactory {
        public static Shape initShape(String arg){
            if(arg.equals("Circle"))
                return new Circle();
            else if(arg.equals("Triangle"))
                return new Triangle();
            else if(arg.equals("Rectangle"))
                return new Rectangle();
            else
                try {
                    throw new UnsupportedShapeException("图形不存在!");
                } catch (UnsupportedShapeException e) {
                    e.printStackTrace();
                }
            return null;
        }
    }
    
    /**
     * @author lhj
     * @create 2022/4/13 16:01
     * 图形接口类
     */
    public interface Shape {
        void draw();
        void erase();
    }
    
    /**
     * @author lhj
     * @create 2022/4/13 16:03
     * 三角形类
     */
    public class Triangle implements Shape{
        @Override
        public void draw() {
            System.out.println("画三角形");
        }
    
        @Override
        public void erase() {
            System.out.println("擦三角形");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 优点
    • 实现了对象创建和使用的分离
    • 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可
    • 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性
    • 缺点
    • 工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响
    • 增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度
    • 系统扩展困难,一旦添加新产品不得不修改工厂逻辑
    • 由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构,工厂类不能得到很好地扩展

    2.工厂方法模式

    • 概述
    • 工厂方法模式(Factory Method Pattern)简称工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。
    • 在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
    • 可以说工厂方法模式就是为了解决简单工厂模式无法实现开闭原则而诞生的
      在这里插入图片描述
    • 成员构造
      在这里插入图片描述
    • 代码实例
    /**
     * @author lhj
     * @create 2022/4/13 16:31
     * 把工厂抽象成父类,然后创建具体的连接类型
     * Http之后的同理创建就好了
     */
    public class FactoryMethod {
        public static void main(String[] args) {
            System.out.println("创建HTTP连接");
    
            Connection http = new HTTPFactory().getConnection();
            http.connect();
    
            System.out.println("---------------------");
            System.out.println("创建IMAP连接");
            Connection imap = new IMAPFactory().getConnection();
            imap.connect();
    
            System.out.println("---------------------");
            System.out.println("创建pop3连接");
            Connection pop3 = new POP3Factory().getConnection();
            pop3.connect();
    
        }
    }
    
    /**
     * @author lhj
     * @create 2022/4/13 16:25
     * 抽象连接类
     */
    public interface Connection {
        void connect();
    
        void disconnect();
    
    }
    
    /**
     * @author lhj
     * @create 2022/4/13 16:27
     * http连接类
     */
    public class HTTPConnection implements Connection{
        @Override
        public void connect() {
            System.out.println("开始连接");
        }
    
        @Override
        public void disconnect() {
            System.out.println("断开连接");
        }
    }
    
    /**
     * @author lhj
     * @create 2022/4/13 16:28
     * 抽象连接工厂
     */
    public interface ConnectionFactory {
        Connection getConnection();
    }
    
    /**
     * @author lhj
     * @create 2022/4/13 16:30
     * http工厂类
     */
    public class HTTPFactory implements ConnectionFactory{
        @Override
        public Connection getConnection() {
            return new HTTPConnection();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 优点
    • 工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节
    • 能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部
    • 在系统中加入新产品时,完全符合开闭原则
    • 缺点
    • 系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销
    • 增加了系统的抽象性和理解难度

    3.抽象工厂模式

    • 概述

    为了更好地体现不同品牌产品的结构
    在这里插入图片描述

    • 模式结构
      在这里插入图片描述
    • 代码实例

    创造不同游戏场景的抽象工厂,某个场景的天气、地图、背景音乐

    • 横坐标划分产品族为:某个具体场景的天气、地图、背景音乐
    • 纵坐标划分相同的产品等级结构(不同的天气、不同的地图)
      在这里插入图片描述在这里插入图片描述
    /**
     * @author lhj
     * @create 2022/4/15 16:14
     * 抽象工厂类
     */
    public interface AbstractFactory {
        AbstractMap getMap();
        AbstractWeather getWeather();
        AbstractSound getSound();
    }
    
    /**
     * @author lhj
     * @create 2022/4/15 16:18
     * 抽象地图类
     */
    public interface AbstractMap {
        void map();
    }
    
    /**
     * @author lhj
     * @create 2022/4/15 16:18
     * 抽象声音类
     */
    public interface AbstractSound {
        void sound();
    }
    
    /**
     * @author lhj
     * @create 2022/4/15 16:18
     * 抽象天气类
     */
    public interface AbstractWeather {
        void weather();
    }
    
    /**
     * @author lhj
     * @create 2022/4/15 16:16
     * 具体场景工厂类
     */
    public class DesertFactory implements AbstractFactory{
    
        @Override
        public AbstractMap getMap() {
            return null;
        }
    
        @Override
        public AbstractWeather getWeather() {
            return null;
        }
    
        @Override
        public AbstractSound getSound() {
            return null;
        }
    }
    
    /**
     * @author lhj
     * @create 2022/4/15 16:22
     */
    public class DesertMap implements AbstractMap {
        @Override
        public void map() {
        }
    }
    
    /**
     * @author lhj
     * @create 2022/4/15 16:22
     */
    public class DesertSound implements AbstractSound {
        @Override
        public void sound() {
        }
    }
    
    /**
     * @author lhj
     * @create 2022/4/15 16:22
     */
    public class DesertWeather implements AbstractWeather {
        @Override
        public void weather() {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 优点
    • 隔离了具体类的生成,使得客户端并不需要知道什么被创建
    • 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象
    • 增加新的产品族很方便,无须修改已有系统,符合开闭原则
    • 缺点

    增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了开闭原则

    建造者模式

    • 概述
    • 建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
    • 建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。建造者模式属于对象创建型模式。
    • 模式结构

    在这里插入图片描述

    • 代码演示
    /**
     * @author lhj
     * @create 2022/4/13 17:22
     */
    public class Builder {
        public static void main(String[] args) {
            CarWaiter carWaiter = new CarWaiter();
            //这里可以动态加载
            carWaiter.setCarBuilder(new FormulaCarBuilder());
            Car formulaCar = carWaiter.init();
            formulaCar.show();
        }
    }
    
    /**
     * @author lhj
     * @create 2022/4/13 17:33
     * 赛车类
     */
    @Data
    @ToString
    public class Car {
        //车身
        private String body;
        //发动机
        private String engine;
        //轮胎
        private String tyre;
        //变速箱
        private String gearbox;
    }
    
    /**
     * @author lhj
     * @create 2022/4/13 17:36
     * 抽象赛车创建类
     */
    public abstract class CarBuilder {
        protected Car car = new Car();
    
        public abstract void buildBody();
        public abstract void buildEngine();
        public abstract void buildTyre();
        public abstract void buildGearbox();
    
        public Car getCar(){
            return car;
        }
    }
    
    /**
     * @author lhj
     * @create 2022/4/13 17:45
     * 汽车指挥者类,即初始化部件
     */
    public class CarWaiter {
        private CarBuilder carBuilder;
    
        public void setCarBuilder(CarBuilder carBuilder){
            this.carBuilder = carBuilder;
        }
    
        public Car init(){
            carBuilder.buildBody();
            carBuilder.buildEngine();
            carBuilder.buildTyre();
            carBuilder.buildGearbox();
            return carBuilder.getCar();
        }
    }
    
    /**
     * @author lhj
     * @create 2022/4/13 17:39
     */
    public class FormulaCarBuilder extends CarBuilder{
        @Override
        public void buildBody() {
            car.setBody("方程式赛车车身");
        }
    
        @Override
        public void buildEngine() {
            car.setEngine("方程式赛车引擎");
        }
    
        @Override
        public void buildTyre() {
            car.setTyre("方程式赛车轮胎");
        }
    
        @Override
        public void buildGearbox() {
            car.setGearbox("方程式赛车变速箱");
        }
    }
    
    /**
     * @author lhj
     * @create 2022/4/13 17:42
     * 场地越野赛车
     */
    public class OffRoadRacingCarBuilder extends CarBuilder{
    
        @Override
        public void buildBody() {
            car.setBody("场地越野赛车车身");
        }
    
        @Override
        public void buildEngine() {
            car.setEngine("场地越野赛车引擎");
        }
    
        @Override
        public void buildTyre() {
            car.setTyre("场地越野赛车轮胎");
        }
    
        @Override
        public void buildGearbox() {
            car.setGearbox("场地越野赛车变速箱");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 优点
    1. 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
    2. 每一个具体建造者都相对独立,与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,扩展方便,符合开闭原则
    3. 可以更加精细地控制产品的创建过程
    • 缺点
    1. 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,不适合使用建造者模式,因此其使用范围受到一定的限制
    2. 如果产品的内部变化复杂,可能会需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加了系统的理解难度和运行成本
  • 相关阅读:
    【Docker】【IDEA】springboot在服务器docker容器化部署,IDEA远程debug
    【数据结构与算法】时间复杂度&&空间复杂度
    2023年-10月-第1周周报
    MyBatus-Plus保姆级快速上手教程
    启用redis缓存,让齐博x1访问速度更快
    全球著名化妆品品牌--SEPHORA丝芙兰验厂
    logback日志框架学习(2)logback的构造
    RapidSSL证书便宜吗?如何申请?
    【Git】01-Git基础
    公众号h5授权链接显示redirect_uri域名与后台配置不一致
  • 原文地址:https://blog.csdn.net/weixin_49258262/article/details/125492830