不做大哥好多年 不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核

逍遥子

不做大哥好多年
首页
  • MySQL
  • Redis
  • Elasticsearch
  • Kafka
  • Etcd
  • MongoDB
  • TiDB
  • RabbitMQ
  • 01.GO基础
  • 02.面向对象
  • 03.并发编程
  • 04.常用库
  • 05.数据库操作
  • 06.Beego框架
  • 07.Beego商城
  • 08.GIN框架
  • 09.GIN论坛
  • 10.微服务
  • 01.Python基础
  • 02.Python模块
  • 03.Django
  • 04.Flask
  • 05.SYL
  • 06.Celery
  • 10.微服务
  • 01.Java基础
  • 02.面向对象
  • 03.Java进阶
  • 04.Web基础
  • 05.Spring框架
  • 100.微服务
  • Docker
  • K8S
  • 容器原理
  • Istio
  • 数据结构
  • 算法基础
  • 算法题分类
  • 前置知识
  • PyTorch
  • 01.Python
  • 02.GO
  • 03.Java
  • 04.业务问题
  • 05.关键技术
  • 06.项目常识
  • 10.计算机基础
  • Linux基础
  • Linux高级
  • Nginx
  • KeepAlive
  • ansible
  • zabbix
  • Shell
  • Linux内核
  • Java基础

  • 面向对象

  • Java进阶

    • 01.文件处理
    • 02.异常处理
    • 03.反射
    • 04.注解
    • 05.泛型
    • 06.日期与时间
    • 07.多线程基础
    • 08.多线程进阶
    • 10.设计模式
      • 01.设计模式
        • 0、概述
        • 1、单例模式
        • 2、工厂模式
        • 0、结构
        • 1、Animal.java 接口
        • 2、Dog.java
        • 3、Cat.java
        • 4、AnimalFactory.java 工厂类
        • 5、Main.java 测试类
        • 3、观察者模式
        • 4、适配器模式
        • 1、只能播放MP3
        • 2、适配器模式
        • 5、装饰器模式
  • Web基础

  • Spring框架

  • 微服务

  • Java
  • Java进阶
xiaonaiqiang
2024-05-07
目录

10.设计模式

# 01.设计模式

# 0、概述

  • 单例模式:保证一个类仅有一个实例,并提供一个全局访问点

  • 工厂模式:定义一个创建对象的接口,让子类决定实例化哪一个类

  • 观察者模式:定义对象间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新

  • 适配器模式:将一个类的接口转换成客户希望的另外一个接口Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

  • 装饰器模式:动态地给一个对象添加一些额外的职责,就增加功能来说,Decorator模式比生成子类更为灵活

  • 代理模式:为其他对象提供一种代理以控制对这个对象的访问

  • 只需要了解的设计模式有

    • 桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化

    • 组合模式:将对象组合成树形结构以表示"部分-整体"的层次结构它使得客户对单个对象和复合对象的使用具有一致性

    • 享元模式:运用共享技术有效地支持大量细粒度的对象

    • 备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态这样以后就可将该对象恢复到原先保存的状态

# 1、单例模式

  • 单例模式:保证一个类仅有一个实例,并提供一个全局访问点

  • 单例模式的作用

    • 节省内存空间,避免创建多个相同的对象

    • 方便全局访问,提高代码的可维护性和可扩展性

    • 单例模式可以用来控制某些资源的访问,比如数据库连接池、线程池等

  • 1)饿汉式单例模式

    • 这种方式在类加载时就会初始化单例对象,线程安全,但可能会浪费内存空间
public class Main {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        System.out.println(singleton);
    }
}

class Singleton {
    private static final Singleton instance = new Singleton();
    private Singleton() {
        // 私有构造函数
    }
    public static Singleton getInstance() {
        return instance;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 2)懒汉式单例模式(线程不安全)
public class Singleton {
    private static Singleton instance = null;
    private Singleton() {
        // 私有构造函数
    }
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
  • 3)懒汉式单例模式(线程安全)
    • 这种方式在第一次调用getInstance方法时才会初始化单例对象,并加上了synchronized关键字,保证线程安全,但是会降低性能
public class Singleton {
    private static Singleton instance = null;
    private Singleton() {
        // 私有构造函数
    }
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 2、工厂模式

  • 工厂模式:定义一个创建对象的接口,让子类决定实例化哪一个类

作用

  • 提供了一种创建对象的最佳方式
    • 将创建对象的复杂逻辑封装在工厂类中,客户端只需要知道需要传入什么参数即可,不需要知道具体的创建细节
  • 工厂模式提供了代码的封装性,代码易于维护和修改
  • 一般使用场景
    • 当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数
    • 采用构造函数来创建对象的方式就会显得非常繁琐,此时可以使用工厂模式来创建对象
  • 通过使用工厂模式,我们可以在不修改原来代码的情况下引入新的产品,这符合开闭原则

# 0、结构

  • 假设我们有一个Animal接口和它的实现类Cat、Dog
src
└── factory
    ├── Animal.java
    ├── AnimalFactory.java
    ├── Cat.java
    ├── Dog.java
    └── Main.java
1
2
3
4
5
6
7

# 1、Animal.java 接口

public interface Animal {
    void sound();
}
1
2
3

# 2、Dog.java

public class Dog implements Animal {
    @Override
    public void sound() {
        System.out.println("Dog barks");
    }
}
1
2
3
4
5
6

# 3、Cat.java

public class Cat implements Animal{
    @Override
    public void sound() {
        System.out.println("Cat meows");
    }
}
1
2
3
4
5
6

# 4、AnimalFactory.java 工厂类

public class AnimalFactory {
    public Animal getAnimal(String animalType){
        if(animalType == null){
            return null;
        }
        if(animalType.equalsIgnoreCase("DOG")){
            return new Dog();
        } else if(animalType.equalsIgnoreCase("CAT")){
            return new Cat();
        }
        return null;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 5、Main.java 测试类

public class Main {
    public static void main(String[] args) {
        AnimalFactory animalFactory = new AnimalFactory();

        //获取 Dog 的对象,并调用它的 sound 方法
        Animal animal1 = animalFactory.getAnimal("DOG");
        animal1.sound();

        //获取 Cat 的对象,并调用它的 sound 方法
        Animal animal2 = animalFactory.getAnimal("CAT");
        animal2.sound();
    }
}
/*
Dog barks
Cat meows
 */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 3、观察者模式

  • 观察者模式:定义对象间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新
  • 下面代码中,Subject类是被观察者,其内部有一个PropertyChangeSupport对象,用来添加、删除观察者,以及在数据变化时通知观察者
  • 当setData()方法被调用时,PropertyChangeSupport对象会通知所有的观察者数据已经改变
  • 而MyObserver类就是观察者,其实现了PropertyChangeListener接口,在propertyChange()方法中更新观察者的数据
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public class Test {

    // 被观察者
    static class Subject {
        private String data;
        // `PropertyChangeSupport`对象,用来添加、删除观察者,以及在数据变化时通知观察者
        private final PropertyChangeSupport support;

        public Subject() {
            this.support = new PropertyChangeSupport(this);
        }

        public String getData() {
            return data;
        }

        // 当`setData()`方法被调用时,`PropertyChangeSupport`对象会通知所有的观察者数据已经改变
        public void setData(String data) {
            String oldData = this.data;
            this.data = data;
            support.firePropertyChange("data", oldData, data);
        }

        // 将观察者对象注册到被观察者对象中,以便在数据变化时能够接收到通知
        public void addPropertyChangeListener(PropertyChangeListener listener) {
            support.addPropertyChangeListener(listener);
        }

        public void removePropertyChangeListener(PropertyChangeListener listener) {
            support.removePropertyChangeListener(listener);
        }
    }

    // 观察者
    static class MyObserver implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            System.out.println("Data changed: " + evt.getNewValue());
        }
    }

    public static void main(String[] args) {
        Test t = new Test();
        // new一个 被观察者
        Subject subject = new Subject();
        // new一个 观察者
        MyObserver observer = new MyObserver();
        // 将观察者对象注册到被观察者对象中,以便在数据变化时能够接收到通知
        subject.addPropertyChangeListener(observer);
        // 改变被观察者对象的数据,这会触发PropertyChangeSupport对象通知所有注册的观察者对象
        // 从而观察者对象会更新自己的数据
        subject.setData("666");
    }
}
/*
Data changed: 666
 */
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

# 4、适配器模式

  • 适配器模式的主要作用是使两个不兼容的接口能够相互合作
  • 举一个具体的编程例子来说,假设你有一个音频播放器,它可以播放mp3文件,但是你希望它也能播放mp4和vlc文件

# 1、只能播放MP3

// 创建一个音频播放器接口
public interface MediaPlayer {
   void play(String audioType, String fileName);
}

// 创建一个实现了 MediaPlayer 接口的实体类。
public class AudioPlayer implements MediaPlayer {
   @Override
   public void play(String audioType, String fileName) {
      if(audioType.equalsIgnoreCase("mp3")){
         System.out.println("Playing mp3 file. Name: " + fileName);
      } else {
         System.out.println("Invalid media. " + audioType + " format not supported");
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 2、适配器模式

public class Main {
    public static void main(String[] args) {
        AudioPlayer audioPlayer = new AudioPlayer();
        audioPlayer.play("mp3", "beyond the horizon.mp3");
        audioPlayer.play("mp4", "alone.mp4");
        audioPlayer.play("vlc", "far far away.vlc");
        audioPlayer.play("avi", "mind me.avi");
    }
}

// 低级播放器接口(只能播放mp3)
interface MediaPlayer {
    void play(String audioType, String fileName);
}

// 创建一个更高级的媒体播放器接口(能播放mp4和vlc文件)
interface AdvancedMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}

// 这是我们的音频播放器,它能播放mp3文件,也能通过MediaAdapter播放vlc和mp4文件
class AudioPlayer implements MediaPlayer {
    MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String fileName) {

        //默认支持的音频类型
        if(audioType.equalsIgnoreCase("mp3")){
            System.out.println("Playing mp3 file. Name: " + fileName);
        }
        //mediaAdapter 提供了播放其他文件格式的支持
        else if(audioType.equalsIgnoreCase("vlc")
                || audioType.equalsIgnoreCase("mp4")){
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        }
        else{
            System.out.println("Invalid media. " + audioType + " format not supported");
        }
    }
}

// 这是我们的适配器,它实现了MediaPlayer接口,并在内部使用AdvancedMediaPlayer
class MediaAdapter implements MediaPlayer {

    AdvancedMediaPlayer advancedMusicPlayer;

    public MediaAdapter(String audioType){
        if(audioType.equalsIgnoreCase("vlc") ){
            advancedMusicPlayer = new VlcPlayer();
        } else if (audioType.equalsIgnoreCase("mp4")){
            advancedMusicPlayer = new Mp4Player();
        }
    }

    @Override
    public void play(String audioType, String fileName) {
        if(audioType.equalsIgnoreCase("vlc")){
            advancedMusicPlayer.playVlc(fileName);
        } else if(audioType.equalsIgnoreCase("mp4")){
            advancedMusicPlayer.playMp4(fileName);
        }
    }
}

// VlcPlayer实体类播放 vlc
class VlcPlayer implements AdvancedMediaPlayer{
    @Override
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file. Name: " + fileName);
    }

    @Override
    public void playMp4(String fileName) {
        //什么也不做
    }
}

// Mp4Player 播放mp4
class Mp4Player implements AdvancedMediaPlayer{
    @Override
    public void playVlc(String fileName) {
        //什么也不做
    }

    @Override
    public void playMp4(String fileName) {
        System.out.println("Playing mp4 file. Name: " + fileName);
    }
}
/*
Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported
 */
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

# 5、装饰器模式

  • 在不改变原有对象的情况下,动态地将行为附加到该对象上
public class Main {
    public static void main(String[] args) {
        Calculator calculator = new SimpleCalculator();
        calculator = new TimeCalculatorDecorator(calculator);
        int result = calculator.calculate(1, 2);
        System.out.println("Result: " + result);
    }
}

interface Calculator {
    int calculate(int a, int b);
}

class SimpleCalculator implements Calculator {
    @Override
    public int calculate(int a, int b) {
        System.out.println("sleep 1s");
        return a + b;
    }
}

class TimeCalculatorDecorator implements Calculator {
    private final Calculator calculator;
    public TimeCalculatorDecorator(Calculator calculator) {
        this.calculator = calculator;
    }
    @Override
    public int calculate(int a, int b) {
        long startTime = System.currentTimeMillis();
        int result = calculator.calculate(a, b);
        long endTime = System.currentTimeMillis();
        System.out.println("Time elapsed: " + (endTime - startTime) + "ms");
        return result;
    }
}
/*
sleep 1s
Time elapsed: 1ms
Result: 3
 */
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
上次更新: 2025/2/19 16:42:39
08.多线程进阶
01.Maven

← 08.多线程进阶 01.Maven→

最近更新
01
04.数组双指针排序_子数组
03-25
02
08.动态规划
03-25
03
06.回溯算法
03-25
更多文章>
Theme by Vdoing | Copyright © 2019-2025 逍遥子 技术博客 京ICP备2021005373号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式