前言

🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。
🍊支持作者: 点赞👍、关注💖、留言💌~

大聪明在写代码的过程中发现设计模式的影子是无处不在,设计模式也是软件开发人员在软件开发过程中面临的一般问题的解决方案。大聪明本着“独乐乐不如众乐乐”的宗旨与大家分享一下设计模式的学习心得。

享元模式

🍓🍓什么是享元模式🍓🍓

在讲解享元模式之前,我们先来看一下它的定义👇

在《设计模式:可复用面向对象软件的基础》中,有这么一句描述享元模式的话:运用共享技术有效地支持大量细粒度的对象。简单来说就是享元模式(Flyweight Pattern)主要用于减少创建对象的数量,也就是通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而减少内存占用、提高系统资源的利用率。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的处理方式。

我们可以用一句话来归纳享元模式的意义:使用对象池来减少来减少创建重复的对象。在享元模式的定义有两个关键词,细粒度和共享对象。因为要求细粒度,所以不可避免地会使对象数量多且对象间的性质相近,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态:

  • 内部状态指对象共享出来的信息,存储在享元信息内部,并且不回随环境的改变而改变
  • 外部状态指对象得以依赖的一个标记,随环境的改变而改变,外部状态不可共享

说到这可能有些小伙伴就想到了一个现成的享元模式的例子~没错!就是我们在开发时经常用到的线程池和数据库连接池。线程池或数据库连接池可以避免不停地创建和销毁多个对象而导致的性能下降问题,享元模式就是为了减少内存占用而使用的,从而避免出现大量重复的创建销毁对象的场景。其中连接池中的连接对象,保存在连接对象中的用户名、密码等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些就是内部状态;而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些就是外部状态,所以说线程池和数据库连接池就是享元模式的一种良好体现。

除此之外,还有一个我们的“老朋友”也用到了享元模式,这位老朋友就是我们常用的 String 对象,我们在声明一个字符串时,它会先去字符串缓存池中寻找,如果有相同的字符串就直接返回,如果没有才会创建一个新的字符串返回,同时将新创建的字符串保存在缓存池里面。

🍓🍓享元模式的实现🍓🍓

各位小伙伴都下过五子棋或者围棋吧,这两种棋都包含了很多个棋子,但是每个棋子之间的区别很小(仅仅是颜色不同、在棋盘上摆放的位置不同)。很显然,它们就符合享元模式的应用场景,接下来我们就用代码简单的实现一下👇

🍎定义下棋接口🍎

/**
 * 下棋接口
 * @description: Piece
 * @author: 庄霸.liziye
 * @create: 2022-04-11 11:14
 **/
public interface Piece {
    void playChess();
}

🍎实现下棋接口🍎

/**
 * 下棋接口实现类
 * @description: PieceImpl
 * @author: 庄霸.liziye
 * @create: 2022-04-11 11:15
 **/
public class PieceImpl implements Piece{
    /**
     * 棋子颜色
     */
    private String color;

    /**
     * 棋盘X坐标轴
     */
    private int x;

    /**
     * 棋盘Y坐标轴
     */
    private int y;

    public PieceImpl(String color){
        this.color = color;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    @Override
    public void playChess() {
        System.out.println("开始下棋 [棋子颜色 : " + color
                +", 棋盘X坐标轴 : " + x +", 棋盘Y坐标轴 :" + y);
    }
}

🍎创建一个工厂,用来生成实体类的对象🍎

import java.util.HashMap;

/**
 * 棋子工厂类
 * @description: PieceFactory
 * @author: 庄霸.liziye
 * @create: 2022-04-11 11:19
 **/
public class PieceFactory {
    private static final HashMap<String, Piece> pieceMap = new HashMap<>();

    public static Piece getPiece(String color) {
        PieceImpl piece = (PieceImpl)pieceMap.get(color);

        if(piece == null) {
            piece = new PieceImpl(color);
            pieceMap.put(color, piece);
            System.out.println("创建了【" + color+ "】色的棋子");
        }
        return piece;
    }
}

🍎使用刚刚创建的工厂,通过传递颜色信息来获取实体类的对象🍎

/**
 * @description: Test
 * @author: 庄霸.liziye
 * @create: 2022-04-11 11:31
 **/
public class Test {
    private static final String colors[] =
            {"白", "黑" };
    public static void main(String[] args) {

        for(int i=0; i < 20; ++i) {
            PieceImpl piece =
                    (PieceImpl)PieceFactory.getPiece(getRandomColor());
            piece.setX(getRandomX());
            piece.setY(getRandomY());
            piece.playChess();
        }
    }
    private static String getRandomColor() {
        return colors[(int)(Math.random()*colors.length)];
    }
    private static int getRandomX() {
        return (int)(Math.random()*100 );
    }
    private static int getRandomY() {
        return (int)(Math.random()*100);
    }
}

🍎运行结果如下所示🍎

java技术分享可以讲什么内容 java分享会分享啥呢_享元模式

🍓🍓享元模式的优、缺点🍓🍓

最后我们总结一下享元模式的优点与缺点👇

🍌🍌优点🍌🍌

极大地减少了内存中对象的数量,降低系统的内存,使效率得到提高;外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

🍌🍌缺点🍌🍌

提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

小结

本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇

希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●’◡’●)

如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。

你在被打击时,记起你的珍贵,抵抗恶意;
你在迷茫时,坚信你的珍贵,抛开蜚语;
爱你所爱 行你所行 听从你心 无问东西