博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
12-五子棋游戏:享元模式
阅读量:4952 次
发布时间:2019-06-12

本文共 7858 字,大约阅读时间需要 26 分钟。

12.1 五子棋游戏

  本章以五子棋游戏为例,来学习享元模式。

12.2 模式定义

  享元模式(Flyweight Pattern),以共享的方式高效地支持大量的细粒度对象。通过复用内存中已经存在的对象,降低系统创建对象实例的性能消耗。享元的英文是Flyweight,它是一个来自于体育方面的专业术语,在拳击、摔跤和举重比赛中特指最轻量的级别。把这个单词移植到软件工程里,也是用来表示告别小的对象,即细粒度对象。至于为什么把Flyweight翻译为“享元”,可以理解为共享元对象,也就是共享细粒度对象。

  在面向对象的眼中,万事万物一切皆对象。但是不可避免的是,采用面向对象的编程方式,可能会增加一些资源和性能上的开销。不过,在大多数情况下,这种影响还不是太大,所以,它带来的空间和性能上的损耗相对于它的优点而言,基本上不用考虑。但是,在某些特殊情况下,大量细粒度对象的创建、销毁及存储所造成的资源和性能上的损耗,可能会在系统运行时形成瓶颈。那么该如何避免产生大量的细粒度对象,同时又不影响系统使用面向对象的方式进行操作呢?享元模式提供了一个比较好的解决方案。

12.3 模式分析

  下面,我们来分析上面的五子棋游戏。

  1)需要一个抽象棋子类,黑子和白子继承该类;

  2)需要一个获得棋子的工厂,用来获得棋子对象,并且缓存棋子对象实例,不至于每次都是new一个棋子出来。

  享元模式的要义就在于“避免产生大量的细粒度对象”。

  在棋子工厂的缓存中,如果存在棋子对象内容,则使用缓存当中的对象实例;如果不存在,则创建一个新的棋子对象,缓存之后返回。一般情况下,我们把工厂设置为单例模式,使用HashTable作为工厂的缓存结构,缓存中以“B”和“W”字母作为key,value中则存储“黑子”和“白子”对象实例。

12.4 模式实现

12.4.1 创建抽象棋子

package com.demo.flyweight.factory;/** * Created by lsq on 2018/3/20. * 抽象棋子类 */public abstract class AbstractChessman {    //棋子类别(黑|白)    protected String chess;    public AbstractChessman(String chess){        this.chess = chess;    }    //显示棋子信息    public void show(){        System.out.println(this.chess);    }}

12.4.2 棋子实现

1. 黑子实现——BlackChessman

package com.demo.flyweight.factory;/** * Created by lsq on 2018/3/20. * 黑子实现类 */public class BlackChessman extends AbstractChessman{    /**     * 构造方法:初始化黑棋子     */    public BlackChessman() {        super("○");        System.out.println("---BlackChessman Constructor Execute!");    }}

2. 白子实现——WhiteChessman

package com.demo.flyweight.factory;/** * Created by lsq on 2018/3/20. * 白子实现类 */public class WhiteChessman extends AbstractChessman{    /**     * 构造方法,初始化白棋子     */    public WhiteChessman() {        super("●");        System.out.println("---WhiteChessman Constructor Execute!");    }}

12.4.3 创建棋子工厂

package com.demo.flyweight.factory;import java.util.Hashtable;/** * Created by lsq on 2018/3/20. * 棋子工厂 */public class FiveChessmanFactory {    //单例模式工厂    private static FiveChessmanFactory fiveChessmanFactory = new FiveChessmanFactory();    //缓存存放共享对象    private final Hashtable
cache = new Hashtable<>(); //私有化构造方法 private FiveChessmanFactory(){} //获得单例工厂 public static FiveChessmanFactory getInstance(){ return fiveChessmanFactory; } /** * 根据字符获取棋子 * @param c (B:围棋,W:白棋) * @return */ public AbstractChessman getChessmanObject(char c){ //从缓存中获得棋子对象实例 AbstractChessman abstractChessman = this.cache.get(c); if (abstractChessman == null){ //缓存中没有棋子对象实例信息,则创建棋子对象实例,并放入缓存 switch (c){ case 'B': abstractChessman = new BlackChessman(); break; case 'W': abstractChessman = new WhiteChessman(); break; default: break; } //为防止非法字符的进入,返回null if (abstractChessman != null){ //放入缓存 this.cache.put(c, abstractChessman); } } return abstractChessman; }}

12.4.4 客户端测试

package com.demo.flyweight;import com.demo.flyweight.factory.AbstractChessman;import com.demo.flyweight.factory.FiveChessmanFactory;import java.util.Random;/** * Created by lsq on 2018/3/21. * 应用程序 */public class Client {    public static void main(String[] args) {        //创建五子棋工厂        FiveChessmanFactory fiveChessmanFactory = FiveChessmanFactory.getInstance();        //随机数,用来随机生成棋子对象        Random random = new Random();        int radom = 0;        AbstractChessman abstractChessman = null;        //随机获得棋子        for (int i=0;i<10;i++){            radom = random.nextInt(2);            switch (radom){                //获得黑棋                case 0:                    abstractChessman = fiveChessmanFactory.getChessmanObject('B');                    break;                //获得白棋                case 1:                    abstractChessman = fiveChessmanFactory.getChessmanObject('W');                    break;            }            if (abstractChessman != null){                abstractChessman.show();            }        }    }}

运行结果:

  从上面的运行结果可以看到,“黑子”和“白子”的构造方法分别只被执行了一次,其余的都是从缓存中获得的对象实例。

12.4.5 如何实现棋子的位置

  我们的享元对象已经生效了,共享了元对象,节省了内存空间。然而,我们现在只做到了共享棋子对象实例,还有一点不要忘记,那就是棋子的位置。虽然棋子对象是可以共享的,但是,每一个棋子的位置都是不一样的,是不能共享的。这也就是享元模式的两种状态:内蕴状态(Internal State)和外蕴状态(External State)。

  1)内蕴状态

  享元对象的内蕴状态是不会随着环境的改变而改变的,在存储在享元对象内部的状态信息,因此内蕴状态是可以共享的,对于任何一个享元对象来讲,它的值是完全相同的。就像上面例子中的“黑子”和“白子”,它代表的状态就是内蕴状态。

  2)外蕴状态

  外蕴状态会随环境的改变而改变,因此是不可以共享的状态,对于不同的享元对象来讲,它的值可能是不同的。享元对象的外蕴状态必须由客户端保存,在享元对象被创建之后,需要使用的时候再传入到享元对象内部。就像五子棋的位置信息,代表的状态就是享元对象的外蕴状态。

  享元模式的两种状态是相互独立的,彼此没有关联。

  3)实现棋子的外蕴状态——位置

  首先,我们需要声明的一点是,虽然外蕴状态的内容是会随着环境改变而改变的,但是外蕴状态变量还是需要的,我们需要在抽象棋子类中增加棋子位置信息,以及设置位置的方法内容。

  下面,我们就在抽象棋子类中增加棋子位置信息x,y,同时增加设置棋子位置的方法point,在显示棋子信息的方法中增添棋子的位置信息。修改后的AbstractChessman抽象棋子类内容如下所示:

package com.demo.flyweight.object;/** * Created by lsq on 2018/3/20. * 抽象棋子类 */public abstract class AbstractChessman {    //棋子坐标    protected int x,y;    //棋子类别(黑|白)    protected String chess;    public AbstractChessman(String chess){        this.chess = chess;    }    //点坐标设置    public abstract void point(int x, int y);    //显示棋子信息    public void show(){        System.out.println(this.chess+"("+this.x+","+this.y+")");    }}

完美黑子类“BlackChessman”,实现父类抽象方法point,内容如下所示:

package com.demo.flyweight.object;/** * Created by lsq on 2018/3/20. * 黑子实现类 */public class BlackChessman extends AbstractChessman {    /**     * 构造方法:初始化黑棋子     */    public BlackChessman() {        super("○");        System.out.println("---BlackChessman Constructor Execute!");    }    /**     * 实现父类方法,设置位置信息     */    @Override    public void point(int x, int y) {        this.x = x;        this.y = y;        this.show();    }}

完美白子类“WhiteChessman”,实现父类抽象方法point。

package com.demo.flyweight.object;/** * Created by lsq on 2018/3/20. * 白子实现类 */public class WhiteChessman extends AbstractChessman {    /**     * 构造方法,初始化白棋子     */    public WhiteChessman() {        super("●");        System.out.println("---WhiteChessman Constructor Execute!");    }    /**     * 实现父类方法,设置位置信息     */    @Override    public void point(int x, int y) {        this.x = x;        this.y = y;        this.show();    }}

12.4.6 测试棋子的外蕴状态

package com.demo.flyweight;import com.demo.flyweight.object.AbstractChessman;import com.demo.flyweight.object.FiveChessmanFactory;import java.util.Random;/** * Created by lsq on 2018/3/21. * 应用程序 */public class Client2 {    public static void main(String[] args) {        //创建五子棋工厂        FiveChessmanFactory fiveChessmanFactory = FiveChessmanFactory.getInstance();        //随机数,用来随机生成棋子对象        Random random = new Random();        int radom = 0;        AbstractChessman abstractChessman = null;        //随机获得棋子        for (int i=0;i<10;i++){            radom = random.nextInt(2);            switch (radom){                //获得黑棋                case 0:                    abstractChessman = fiveChessmanFactory.getChessmanObject('B');                    break;                //获得白棋                case 1:                    abstractChessman = fiveChessmanFactory.getChessmanObject('W');                    break;            }            if (abstractChessman != null){                //设置棋子位置信息(x:0~9,y:0~15的随机数产生)                abstractChessman.point(i, random.nextInt(15));            }        }    }}

运行结果如下:

  享元模式的重点在于共享元对象,降低内存的使用空间,提高系统性能。享元对象的外蕴状态是通过客户端来保存传入的,它是可能发生变化的。因此,在我们进行软件系统设计的时候,一定要区分享元对象的内蕴状态和外蕴状态,不能混淆,更不能互相关联,二者应是彼此分开的。

12.5 使用场合

  1)当系统中某个对象类型的实例较多的时候;

  2)在系统设计中,对象实例进行分类后,发现真正有区别的分类很少的时候。

扩展1:Java SDK中的享元模式

  享元模式是系统中经常用到的,特别是对于细粒度对象比较多的软件系统,使用起来非常有效,不但可以提高系统内在空间,还可以提高系统效率。在JDK中也存在着享元模式的身影,如java.lang.Integer。java.lang.Integer类中的valueOf方法就是享元模式的具体应用。在该方法中,首先判断参数的范围,如果在缓存范围内,则返回缓存中的内容,否则创建一个新对象返回。而缓存类IntegerCache作为java.lang.Integer类的内部类,已经在类初始化的时候,设置了Integer数组缓存cache[]内容。

 

 

转载于:https://www.cnblogs.com/danielleee/p/8619593.html

你可能感兴趣的文章
Lambda表达式中的表达式lambda和语句lambda区别
查看>>
Mac OS X Snow Leopard 开启Web共享,建立Web服务器:Apache+PHP+MySql
查看>>
20180104-高级特性-Slice
查看>>
6个SQL Server 2005性能优化工具介绍
查看>>
nginx启动、关闭命令、重启nginx报错open() "/var/run/nginx/nginx.pid" failed
查看>>
day14 Python 内置函数、匿名函数和递归函数
查看>>
BZOJ 3097 Hash Killer I
查看>>
UINavigationController的视图层理关系
查看>>
AX2009获取库存查询当前显示的维度
查看>>
web页面调用Linux脚本,apache – 如何从Web服务器调用本地shell脚本...
查看>>
html阴影效果怎么做,css 内阴影怎么做
查看>>
宏观经济
查看>>
生活清单
查看>>
李春雷 | 夜宿棚花村
查看>>
辅导员面试
查看>>
事业单位笔试题
查看>>
公文种类
查看>>
公共基础知识
查看>>
给普通话点穴
查看>>
公文格式
查看>>