24种设计模式详解(下)

简介: 24种设计模式详解(下)

23种设计模式详解(上)+https://developer.aliyun.com/article/1625049

例子

package com.linghu.demo05;
/**
 * @author linghu
 * @date 2024/2/4 11:12
 */
public class Client {
    public static void main(String[] args) {
        //创建经纪人类
        Agent agent = new Agent();
        //创建明星类
        Star star = new Star("李小龙");
        agent.setStar(star);
        //创建粉丝类
        Fans fans = new Fans("令狐");
        agent.setFans(fans);
        //创建公司对象
        Company company = new Company("好莱坞");
        agent.setCompany(company);
        //开会和洽谈业务~
        agent.meeting();
        agent.business();
    }
}

合成复用原则

基本介绍

合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

例子

创建者模式

基本介绍

创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。

创建型模式分为:

  • 单例模式
  • 工厂方法模式
  • 抽象工程模式
  • 原型模式
  • 建造者模式

单例设计模式

饿汉式-方法1:静态变量
package com.linghu.demo06.pattern;
/**
 * 饿汉式
 * 静态变量-创建类对象
 * @author linghu
 * @date 2024/2/4 14:41
 */
public class Singleton {
    //1、私有化构造器,防止外部创建该类对象,只能让外部通过get方法访问本类
    private Singleton(){
    }
    //2、在成员位置创建该类对象
    private static Singleton instance=new Singleton();
    //3、对外提供静态方法获取该类对象
    public static Singleton getInstance(){
        return instance;
    }
}

饿汉式-方法2:静态代码块

这个方法跟上面的方法其实差不多。

package com.linghu.demo06.pattern;
/**
 * 饿汉式
 * 静态变量-创建类对象
 * @author linghu
 * @date 2024/2/4 14:41
 */
public class Singleton {
    //1、私有化构造器,防止外部创建该类对象,只能让外部通过get方法访问本类
    private Singleton(){
    }
    //2、在成员位置创建该类对象
    private static Singleton instance;//null
    static {
       instance=new Singleton();   
    }
    //3、对外提供静态方法获取该类对象
    public static Singleton getInstance(){
        return instance;
    }
}
总结

以上 Singleton类保证了只能对外创建一个对象。

懒汉式-方式1(线程不安全)

这个方式就是用饿汉式的第一种方式进行调整就可以了:

public class Singleton {
    //1、私有化构造器,防止外部创建该类对象,只能让外部通过get方法访问本类
    private Singleton(){
    }
    //2、在成员位置创建该类对象
    private static Singleton instance;
    //3、对外提供静态方法获取该类对象
    public static Singleton getInstance(){
        //instance=new Singleton();//在这里做了调整,使用该对象的时候再调用,就不会浪费了
        if (instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

如上的代码中,我们加入了一个if判断,这里的操作叫做 懒加载!

在开发中,如果某个实例的创建需要消耗很多系统资源,那么我们通常会使用惰性加载机制,也就是说只有当使用到这个实例的时候才会创建这个实例,这个好处在单例模式中得到了广泛应用。这个机制在单线程环境下的实现非常简单,然而在多线程环境下却存在隐患

懒汉式-方式2(线程安全)

在上面懒加载的情况下加入一个 synchronized同步锁就可以了:

package com.linghu.demo06.pattern01;
/**
 * @author linghu
 * @date 2024/2/4 17:01
 */
public class Singleton {
    //1、构造器私有化
    private Singleton(){
    }
    //2、创建类对象
    private static Singleton instance;
    //3、对外提供静态方法获取该对象
    public static synchronized Singleton getInstance(){
        if (instance==null){//当线程1进入以后,先判断,线程2在方法外面等待
            //等待到线程1对象创建或者返回完毕释放锁以后,线程2才开始进入
            instance=new Singleton();
        }
        return instance;
    }
}

**该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效果特别低。 **

懒汉式-方式3(双重检查锁)

**懒汉模式中加锁的问题,对于 getInstance() 方法来说,绝大部分的操作都是读操作,读操作是线程安全的,所以我们没必让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。由此也产生了一种新的实现模式:双重检查锁模式 **

package com.linghu.demo07;
/**
 * 双重检查方式
 * @author linghu
 * @date 2024/2/5 10:33
 */
public class Singleton {
    //1、私有构造器
    private Singleton(){}
    private static Singleton instance;
    //对外提供静态方法获取该对象
    public static Singleton getInstance(){
        //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
        if (instance==null){
            synchronized (Singleton.class){
                //第二次判断,抢到锁以后是否为null
                if (instance==null){
                    instance=new Singleton();
                }
            }
        }
        return instance;
    }
}

双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。要解决双重检查锁模式带来空指针异常的问题,只需要使用 volatile 关键字, volatile 关键字可以保证可见性和有序性。

懒汉式-方式4(静态内部类)

**静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。静态属性由于被static 修饰,保证只被实例化一次,并且严格保证实例化顺序。 **

package com.linghu.demo08;
/**
 * @author linghu
 * @date 2024/2/5 11:21
 */
public class Singleton {
    //1、私有化构造器
    private Singleton(){}
    //2、静态内部类提供实例
    private static class SingletonHolder{//这个类只能内部用,外界访问不了
        //调用getInstance方法的时候才能初始化INSTANCE,而且只会被调用一次
        private static final Singleton INSTANCE=new Singleton();
    }
    //3、对外提供静态方法获取该对象
    public static Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance,虚拟机加载SingletonHolder并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。

枚举方式-饿汉式(不考虑内存空间)

**枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。 **

package com.linghu.demo09;
/**
 * 枚举方式
 * @author linghu
 * @date 2024/2/5 11:34
 */
public enum Singleton {
    INSTANCE;
}

JDK源码解析-RunTime类的单例设计模式

在jdk源码中,RunTime类的设计其实就用到了单例设计模式中的恶汉式来实现的。

如上可以看出代码中提供了私有化构造器、静态访问对象的方法、静态变量对象:

public class Runtime {
    private static Runtime currentRuntime = new Runtime();
    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }
    /** Don't let anyone else instantiate this class */
    private Runtime() {}

工厂模式

在Java中万物皆对象,创建对象通过都是new出来的,但这么干会出现一些问题:比如说,当我们需要更换对象的时候,所有new对象的地方我们都需要修改一遍,这就违背了软件设计的 开闭原则!

引入工厂模式的目的是为了:解耦!

也就是说,我们可以利用工厂来生产对象,我们开发者只需要和工厂打交道即可,彻底和对象解耦!

三种工厂:

  • 简单工厂模式(不属于GOF的23种经典设计模式)
  • 工厂方法模式
  • 抽象工厂模式
引例

package com.linghu.demo10;
/**
 * @author linghu
 * @date 2024/2/5 16:20
 */
public abstract class Coffee {
    //获取咖啡的名字,定义成抽象方法
    public abstract String getName();
    public void addMilk(){
        System.out.println("加奶~");
    }
    public void addSugar(){
        System.out.println("加糖~");
    }
}
package com.linghu.demo10;
/**
 * @author linghu
 * @date 2024/2/5 16:31
 */
public class CoffeeStore {
    //根据不同的咖啡类型生成并返回不同的咖啡对象
    public Coffee orderCoffee(String type){
        Coffee coffee=null;
        if ("america".equals(type)){
            coffee=new AmericaCoffee();
        }else if ("latte".equals(type)){
            coffee=new LatteCoffee();
        }
        //添加配料
        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
}
package com.linghu.demo10;
/**
 * 美式咖啡
 * @author linghu
 * @date 2024/2/5 16:24
 */
public class AmericaCoffee extends Coffee{
    @Override
    public String getName() {
        return "美式咖啡";
    }
}
package com.linghu.demo10;
/**
 * @author linghu
 * @date 2024/2/5 16:29
 */
public class LatteCoffee extends Coffee{
    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}
package com.linghu.demo10;
/**
 * @author linghu
 * @date 2024/2/5 16:38
 */
public class Client {
    public static void main(String[] args) {
        //1、创建咖啡店类
        CoffeeStore store=new CoffeeStore();
        //2、点咖啡
        Coffee orderCoffee = store.orderCoffee("america");
        System.out.println(orderCoffee.getName());
    }
}
简单工厂模式

简单工厂模式不是一种模式,而是一种编码习惯。

结构

简单工厂模式包含结构如下:

  • 抽象产品:定义了产品的规范,描述了产品的主要特征和功能。
  • 具体产品:实现或者继承抽象产品的子类。
  • 具体工厂:提供创建产品的方法,调用者通过该方法获取产品。

实现

对上述咖啡案例的改进就在于: 要将咖啡店和咖啡产品进行解耦工作,引入工厂模式。

工厂类代码如下:

package com.linghu.demo10;
/**
 * 这个工厂就负责制造咖啡,里面没有咖啡产品,你要替换咖啡
 * 产品,不需要动这里的代码,这就实现了咖啡店和咖啡产品的解耦!
 * 咖啡店只需要调用这个工厂就可以生产咖啡了,不需要关注咖啡产品了!
 * @author linghu
 * @date 2024/2/5 17:11
 */
public class SimpleCoffeeFactory {
    public Coffee createCoffee(String type){
        Coffee coffee=null;
        if ("americano".equals(type)){
            coffee=new AmericaCoffee();
        }else if ("latte".equals(type)){
            coffee=new LatteCoffee();
        }
        return coffee;
    }
}

法,调用者通过该方法获取产品。


实现

对上述咖啡案例的改进就在于: 要将咖啡店和咖啡产品进行解耦工作,引入工厂模式。

[外链图片转存中…(img-EbAXEhcI-1711960898263)]

工厂类代码如下:

package com.linghu.demo10;
/**
 * 这个工厂就负责制造咖啡,里面没有咖啡产品,你要替换咖啡
 * 产品,不需要动这里的代码,这就实现了咖啡店和咖啡产品的解耦!
 * 咖啡店只需要调用这个工厂就可以生产咖啡了,不需要关注咖啡产品了!
 * @author linghu
 * @date 2024/2/5 17:11
 */
public class SimpleCoffeeFactory {
    public Coffee createCoffee(String type){
        Coffee coffee=null;
        if ("americano".equals(type)){
            coffee=new AmericaCoffee();
        }else if ("latte".equals(type)){
            coffee=new LatteCoffee();
        }
        return coffee;
    }
}

**后期如果再加新品种的咖啡,我们势必要需求修改SimpleCoffeeFactory的代码,违反了开闭原则。工厂类的客户端可能有很多,比如创建美团外卖等,这样只需要修改工厂类的代码,省去其他的修改操作。 **

目录
相关文章
|
1月前
|
机器学习/深度学习 人工智能 算法
乘AIGC浪潮:把握万亿级机遇
AIGC正加速从技术走向产业落地,万亿市场规模催生全链条人才需求。北京、上海政策加码,算力基建完善,2025-2027年成关键窗口期。七大核心岗位——AIGC工程师、大模型训练师、AI工程师等全面爆发,覆盖技术到应用各层级,高薪抢人成常态。工信部认证加持,职业前景广阔,人人皆可入局,抢占AI时代新风口。
201 1
|
8月前
|
资源调度 监控 物联网
《深入探秘:分布式软总线自发现、自组网技术原理》
分布式软总线是实现设备高效互联的关键技术,其自发现与自组网功能为多设备协同奠定了基础。通过融合Wi-Fi、蓝牙、NFC等通信技术,设计针对性发现协议,并采用统一接口封装,简化开发复杂度。自组网技术解决异构网络互联互通问题,支持混合拓扑结构,优化通信资源调度,引入软时钟确保时间同步。这些特性使分布式软总线成为构建万物互联智能时代的核心支撑,推动智能家居、智能办公等领域创新发展,提升生活与工作效率。
527 8
|
12月前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
12月前
|
设计模式 Java 程序员
【23种设计模式·全精解析 | 概述篇】设计模式概述、UML图、软件设计原则
本系列文章聚焦于面向对象软件设计中的设计模式,旨在帮助开发人员掌握23种经典设计模式及其应用。内容分为三大部分:第一部分介绍设计模式的概念、UML图和软件设计原则;第二部分详细讲解创建型、结构型和行为型模式,并配以代码示例;第三部分通过自定义Spring的IOC功能综合案例,展示如何将常用设计模式应用于实际项目中。通过学习这些内容,读者可以提升编程能力,提高代码的可维护性和复用性。
2517 1
【23种设计模式·全精解析 | 概述篇】设计模式概述、UML图、软件设计原则
|
Oracle Java 应用服务中间件
地图服务器GeoServer的安装与配置
地图服务器GeoServer的安装与配置
767 0
|
安全 Linux 数据安全/隐私保护
Windows 部署 Elasticsearch + kibana 8.0 指南
Windows 部署 Elasticsearch + kibana 8.0 指南
|
Ubuntu Linux 芯片
Linux(2)ubuntu rootfs根文件系统制作
Linux(2)ubuntu rootfs根文件系统制作
1052 0
|
设计模式 算法 关系型数据库
手撕设计模式
设计模式相信很多人都应该听过,但是具体是什么可能就有点说不出个所以然,因为你只是听过,但是没有经过系统性的学习,所以很难说出设计模式到底是什么,在接下来的一段时间我将带领大家一起进入手撕设计模式阶段的学习。
362 0
手撕设计模式
|
机器学习/深度学习 算法框架/工具
LSTM时间序列预测案例实战 天气降水量预测
LSTM时间序列预测案例实战 天气降水量预测
647 0
|
设计模式 算法
什么是设计模式,有哪些设计模式,设计模式用来做什么
什么是设计模式,有哪些设计模式,设计模式用来做什么

热门文章

最新文章