在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题。创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈。
例如,围棋和五子棋中的黑白棋子,图像中的坐标点或颜色,局域网中的路由器、交换机和集线器,教室里的桌子和凳子等。这些对象有很多相似的地方,如果能把它们相同的部分提取出来共享,则能节省大量的系统资源,这就是享元模式的产生背景。
享元(Flyweight)模式的定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
享元模式的主要优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
其主要缺点是:
享元模式的定义提出了两个要求,细粒度和共享对象。因为要求细粒度,所以不可避免地会使对象数量多且性质相近,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态。
比如,连接池中的连接对象,保存在连接对象中的用户名、密码、连接URL等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些为外部状态。
享元模式的本质是缓存共享对象,降低内存消耗。
享元模式的主要角色有如下。
图 1 是享元模式的结构图,其中:
/** * @Description:享元模式 * @Author: wang * @Date: 2021/5/24 8:53 */ public class XiangYuanMode { } class FlyweightPattern { public static void main(String[] args) { FlyweightFactory factory = new FlyweightFactory(); Flyweight f01 = factory.getFlyweight("a"); Flyweight f02 = factory.getFlyweight("a"); Flyweight f03 = factory.getFlyweight("a"); Flyweight f11 = factory.getFlyweight("b"); Flyweight f12 = factory.getFlyweight("b"); f01.operation(new UnsharedConcreteFlyweight("第1次调用a。")); f02.operation(new UnsharedConcreteFlyweight("第2次调用a。")); f03.operation(new UnsharedConcreteFlyweight("第3次调用a。")); f11.operation(new UnsharedConcreteFlyweight("第1次调用b。")); f12.operation(new UnsharedConcreteFlyweight("第2次调用b。")); } //非享元角色 static class UnsharedConcreteFlyweight { private String info; public UnsharedConcreteFlyweight(String info) { this.info = info; } public String getInfo() { return info; } public void setInfo(String info) { this.info = info; } } //抽象享元角色 interface Flyweight{ public void operation(UnsharedConcreteFlyweight state); } //具体享元角色 static class ConcreteFlyweight implements Flyweight{ private String key; public ConcreteFlyweight (String key) { this.key = key; System.out.println("具体享元" + key + "被创建!"); } @Override public void operation(UnsharedConcreteFlyweight outState) { System.out.print("具体享元" + key + "被调用,"); System.out.println("非享元信息是:" + outState.getInfo()); } } //享元工厂角色 static class FlyweightFactory { HashMap<String,Flyweight> flyweights = new HashMap<> (); public Flyweight getFlyweight(String key) { Flyweight flyweight = (Flyweight) flyweights.get(key); if (flyweight != null) { System.out.println("具体享元" + key + "已经存在,被成功获取!"); } else { flyweight = new ConcreteFlyweight(key); flyweights.put(key, flyweight); } return flyweight; } } }
【例1】享元模式在五子棋游戏中的应用。
分析:五子棋同围棋一样,包含多个“黑”或“白”颜色的棋子,所以用享元模式比较好。
本实例中:
class WzqGame { public static void main(String[] args) { new Chessboard(); } //棋盘 static 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); //下子 } //具体享元角色:白子 static class WhitePieces implements ChessPieces { public void DownPieces(Graphics g, Point pt) { g.setColor(Color.WHITE); g.fillOval(pt.x, pt.y, 30, 30); } } //具体享元角色:黑子 static class BlackPieces implements ChessPieces { public void DownPieces(Graphics g, Point pt) { g.setColor(Color.BLACK); g.fillOval(pt.x, pt.y, 30, 30); } } //享元工厂角色 static 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) 单纯享元模式,这种享元模式中的所有的具体享元类都是可以共享的,不存在非共享的具体享元类,其结构图如图 4 所示。
(2) 复合享元模式,这种享元模式中的有些享元对象是由一些单纯享元对象组合而成的,它们就是复合享元对象。虽然复合享元对象本身不能共享,但它们可以分解成单纯享元对象再被共享,其结构图如图 5 所示。
原文:https://www.cnblogs.com/sxw123/p/14803074.html