13,享元模式-连连看的图片共享

一,前言

7种结构型设计模式:桥接模式,适配器模式,装饰模式,组合模式,享元模式,外观模式,代理模式

上篇我们说了桥接模式:通过将实现和抽象放在两个不同的类层次中而使他们可以独立改变,桥接模式适用于那些多种情况排列组合发生的场景

这篇说说享元模式,顾名思义就是共享对象的一种模式
共享对象给我们带来的直接好处就是降低了内存的开销
这个模式并不难,但我们还是要把这个模式涉及到的内容都罗列出来

二,享元模式

1)享元模式的定义:

享元模式:以共享的方式高效的支持大量细粒度对象的重用

2)享元模式的UML图:

享元模式UML

3)享元模式的角色:

FlyWeight抽象享元类:
    接口或抽象类,声明公共方法,可向外界提供对象的内部状态,设置外部状态。
ConcreteFlyWeight具体享元类:
    为内部状态提供成员变量进行存储。
UnsharedConcreteFlyWeight非共享享元类:
    不能被共享的子类可以设计为非共享享元类。
FlyWeightFactory享元工厂类:
    创建并管理享元对象,享元池一般设计为键值对。

4)内部状态和外部状态

享元对象能做到共享的关键是区分了内部状态和外部状态。 

内部状态:
    可共享,不会随环境改变而改变
    存储在享元对象内部,构造时通过setter设置
外部状态:
    不可共享,会随环境改变而改变
    需要使用时通过客户端传入享元对象且由客户端保存

三,享元模式Demo

1,场景选择和分析

基于享元模式的特点我们选择连连看游戏作为模式场景

连连看游戏有以下几适合享元模式的特征:
    1,内部状态:连连看游戏包含大量重复的图片内容
    2,外部状态:重复图片中的差异点在于所在位置的不同
    3,大量重复图片加大了内存的开销,适合使用享元模式

连连看

为了讲解模式我们简化了连连看游戏的场景:
    1,限制了只有一排图片,单排随机生成10张图片
    2,客户端保存图片的位置信息(即外部状态)和对象的对应关系
    3,所有图片对象取自享元工厂
    4,随机选择两张图片进行比较是否可以消除

2,Demo源码

1)创建一个图片接口,包含获取图片类型(内部状态)和获取图片位置(外部状态)的方法

package com.brave.flyweight;

public interface ImageNode {

    // 获取图片类型-内部状态
    void getImageType();

    // 获取图片位置-外部状态
    void getImageCoordinate(int coordinate);
}

2,实现图片接口创建图片的具体享元类

package com.brave.flyweight;

public class CImageNode implements ImageNode {

    private String imageType;   // 图片类型

    public CImageNode(String imageType) {
        this.imageType = imageType;
    }

    @Override
    public void getImageType() {
        System.out.println("图片类型为 : " + imageType);
    }

    @Override
    public void getImageCoordinate(int coordinate) {
        System.out.println("图片位置为 : " + coordinate);
    }

}

3,创建享元工厂类,管理享元对象的实例集合

这部分我们就直接将实例初始化到工厂集合中,不再去做懒加载了

package com.brave.flyweight;

import java.util.Hashtable;

/**
 * 享元工厂类
 *  初始化享元工厂,将享元对象装入享元工厂
 * @author Brave
 *
 */
public class ImageNodeFactory {

    private Hashtable<String, CImageNode> imageNodes = new Hashtable<String, CImageNode>();

    public ImageNodeFactory(){

        imageNodes.put("图片1", new CImageNode("图片1"));
        imageNodes.put("图片2", new CImageNode("图片2"));
        imageNodes.put("图片3", new CImageNode("图片3"));
        imageNodes.put("图片4", new CImageNode("图片4"));

    }

    // 获取图片对象
    public ImageNode GetImage(String imageType){
        return imageNodes.get(imageType);
    }
}

4)客户端:

package com.brave.flyweight;

import java.util.Hashtable;

public class Client {

    public static void main(String[] args) {

        // 初始化享元工厂
        ImageNodeFactory imageNodeFactory = new ImageNodeFactory();

        // Hashtable保存游戏初始化后的图片位置和对象信息
        Hashtable<Integer, ImageNode> imageList = new Hashtable<Integer,ImageNode>();

        // 随机初始化游戏数据1-10的随机数
        int randomNumber = 0;
        for(int i=0; i<10; i++){
            randomNumber = (int)(Math.random() * 10);
            int a = randomNumber/2;//除法
            switch(a){
                case 0:
                    imageList.put(i, imageNodeFactory.GetImage("图片1"));
                    break;
                case 1:
                    imageList.put(i, imageNodeFactory.GetImage("图片2"));
                    break;
                case 2:
                    imageList.put(i, imageNodeFactory.GetImage("图片3"));
                    break;
                case 3:
                    imageList.put(i, imageNodeFactory.GetImage("图片4"));
                    break;
                default:
                    imageList.put(i, imageNodeFactory.GetImage("图片1"));
            }
        }
        System.out.println("随机初始化游戏数据完成,开始打印游戏数据");

        // 打印游戏数据
        for(int i=0; i<10; i++){
            // 外部状态
            imageList.get(i).getImageCoordinate(i);
            // 内部状态
            imageList.get(i).getImageType();
        }

        // 随机选取两个不同坐标的图进行比较
        int A = 0;
        int B = 0;
        while(A==B){
            A = (int)(Math.random() * 10);
            B = (int)(Math.random() * 10);
        }

        // 判断是否是相同对象
        if(imageList.get(A) == imageList.get(B)){
            System.out.println("坐标"+A+"和坐标"+B+"的图片相同,可以消除");
        }else{
            System.out.println("坐标"+A+"和坐标"+B+"的图片不相同,不可以消除");
        }
    }

}

5)打印测试日志:

随机初始化游戏数据完成,开始打印游戏数据
图片位置为 : 0
图片类型为 : 图片2
图片位置为 : 1
图片类型为 : 图片1
图片位置为 : 2
图片类型为 : 图片1
图片位置为 : 3
图片类型为 : 图片1
图片位置为 : 4
图片类型为 : 图片3
图片位置为 : 5
图片类型为 : 图片3
图片位置为 : 6
图片类型为 : 图片1
图片位置为 : 7
图片类型为 : 图片1
图片位置为 : 8
图片类型为 : 图片1
图片位置为 : 9
图片类型为 : 图片1
坐标8和坐标9的图片相同,可以消除

四,享元模式的优缺点

优点:

    由于实现了对象的共享所以极大的减少内存中对象的数量
    相同或相似的对象内存只保持一份,极大的节约资源,提高系统性能
    外部状态相对独立,不影响内部变化

缺点:

    共享对象节省内存,共享内部状态,分离了外部状态
    这使得程序的逻辑复杂化,同时也增加了状态维护成本
    每次需要对外部的状态进行读取,牺牲了运行时间
    很显然,这是一种用时间换空间的做法

五,享元模式的应用

JAVA中的String使用了常量池,也就是享元模式,当两个String变量的值都为”abc”时实际使用的是同一个引用

这里我们先标记一下,日后有时间会补充一个了例子

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BraveWangDev

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值