首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >设计模式之——享元模式 Flyweight

设计模式之——享元模式 Flyweight

作者头像
wsuo
发布2020-12-28 14:28:47
发布2020-12-28 14:28:47
5520
举报
文章被收录于专栏:技术进阶之路技术进阶之路

引例

我们经常抱怨学校的选课系统非常卡,这是什么原因呢?

设想一下在选课高峰期,1 秒钟有 1000 人选课,程序在内存中同时创建 1000 个对象,服务器压力过大肯定卡啊,还有可能出现 内存溢出 的现象。

但是我们仔细想一下这 1000 个对象,他们有什么共同点,我们选课会选择任课老师、课程名称等等,那么很多同学有可能选的都是同一节课,现在是每个同学选课都会重新创建一个新的选课记录对象,但是我们没必要这么做,因为这些对象有共性,他们除了学生的名字是不一样的,有可能任课老师和课程名都是同一个,这个时候我们可以共享这些任课老师和课程名称的对象,从而减少创建对象的数量,这就是享元模式的思想。

享元模式的主要优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。

定义

享元模式的定义提出了两个要求,细粒度共享对象 。因为要求细粒度,所以不可避免地会使 对象数量多且性质相近 ,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态。

  • 内部状态指对象共享出来的信息,存储在享元信息内部,并且不会随环境的改变而改变,如本例中的任课老师和课程名称;
  • 外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享,如本例中的学生姓名。
  • 非享元角色:里面包含了非共享的 外部状态 信息 info;
  • 抽象享元角色:包含抽象方法,非享元的外部状态以参数的形式通过该方法传入;
  • 具体享元角色:包含了关键字 key,实现了抽象享元接口;
  • 享元工厂角色,他使用关键字 key 来管理具体享元;
  • 客户角色:环境类,通过享元工厂获取具体享元。

最佳实践

享元模式在五子棋游戏中的应用。

分析:五子棋同围棋一样,包含多个“黑”或“白”颜色的棋子,所以用享元模式比较好。

本实例中:

  • 棋子(ChessPieces)类是抽象享元角色,它包含了一个落子的 DownPieces(Graphics g,Point pt) 方法;
  • 白子(WhitePieces)和黑子(BlackPieces)类是具体享元角色,它实现了落子方法;
  • Point 是非享元角色,它指定了落子的位置;
  • WeiqiFactory 是享元工厂角色,它通过 ArrayList 来管理棋子,并且提供了获取白子或者黑子的 getChessPieces(String type) 方法;
  • 客户类(Chessboard)利用 Graphics 组件在框架窗体中绘制一个棋盘,并实现 mouseClicked(MouseEvent e) 事件处理方法,该方法根据用户的选择从享元工厂中获取白子或者黑子并落在棋盘上。
代码语言:javascript
复制
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;

public class WzqGame {
    public static void main(String[] args) {
        new Chessboard();
    }
}

//棋盘
class Chessboard extends MouseAdapter {
    WeiqiFactory wf;
    JFrame f;
    Graphics g;
    JRadioButton wz;
    JRadioButton bz;
    private final int x = 50;
    private final int y = 50;
    private final int w = 40;    //小方格宽度和高度
    private final int rw = 400;    //棋盘宽度和高度

    Chessboard() {
        wf = new WeiqiFactory();
        f = new JFrame("享元模式在五子棋游戏中的应用");
        f.setBounds(100, 100, 500, 550);
        f.setVisible(true);
        f.setResizable(false);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel SouthJP = new JPanel();
        f.add("South", SouthJP);
        wz = new JRadioButton("白子");
        bz = new JRadioButton("黑子", true);
        ButtonGroup group = new ButtonGroup();
        group.add(wz);
        group.add(bz);
        SouthJP.add(wz);
        SouthJP.add(bz);
        JPanel CenterJP = new JPanel();
        CenterJP.setLayout(null);
        CenterJP.setSize(500, 500);
        CenterJP.addMouseListener(this);
        f.add("Center", CenterJP);
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        g = CenterJP.getGraphics();
        g.setColor(Color.BLUE);
        g.drawRect(x, y, rw, rw);
        for (int i = 1; i < 10; i++) {
            //绘制第i条竖直线
            g.drawLine(x + (i * w), y, x + (i * w), y + rw);
            //绘制第i条水平线
            g.drawLine(x, y + (i * w), x + rw, y + (i * w));
        }
    }

    public void mouseClicked(MouseEvent e) {
        Point pt = new Point(e.getX() - 15, e.getY() - 15);
        if (wz.isSelected()) {
            ChessPieces c1 = wf.getChessPieces("w");
            c1.DownPieces(g, pt);
        } else if (bz.isSelected()) {
            ChessPieces c2 = wf.getChessPieces("b");
            c2.DownPieces(g, pt);
        }
    }
}

//抽象享元角色:棋子
interface ChessPieces {
    public void DownPieces(Graphics g, Point pt);    //下子
}

//具体享元角色:白子
class WhitePieces implements ChessPieces {
    public void DownPieces(Graphics g, Point pt) {
        g.setColor(Color.WHITE);
        g.fillOval(pt.x, pt.y, 30, 30);
    }
}

//具体享元角色:黑子
class BlackPieces implements ChessPieces {
    public void DownPieces(Graphics g, Point pt) {
        g.setColor(Color.BLACK);
        g.fillOval(pt.x, pt.y, 30, 30);
    }
}

//享元工厂角色
class WeiqiFactory {
    private ArrayList<ChessPieces> qz;

    public WeiqiFactory() {
        qz = new ArrayList<ChessPieces>();
        ChessPieces w = new WhitePieces();
        qz.add(w);
        ChessPieces b = new BlackPieces();
        qz.add(b);
    }

    public ChessPieces getChessPieces(String type) {
        if (type.equalsIgnoreCase("w")) {
            return (ChessPieces) qz.get(0);
        } else if (type.equalsIgnoreCase("b")) {
            return (ChessPieces) qz.get(1);
        } else {
            return null;
        }
    }
}

运行结果如下图:

享元模式其实是 工厂方法模式 的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法模式生成对象的,只不过享元模式为工厂方法模式增加了缓存这一功能。

扩展

在前面介绍的享元模式中,其结构图通常包含可以共享的部分和不可以共享的部分。

在实际使用过程中,有时候会稍加改变,即存在两种特殊的享元模式:单纯享元模式复合享元模式

  1. 单纯享元模式,这种享元模式中的 所有的具体享元类都是可以共享的 ,不存在非共享的具体享元类;
  2. 复合享元模式,这种享元模式中的有些享元对象是由一些单纯享元对象 组合而成 的,它们就是复合享元对象。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/12/24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引例
  • 定义
  • 最佳实践
  • 扩展
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档