设计模式-结构型之享元模式

本文深入解析享元模式的核心概念、结构和应用案例,通过对比普通对象和享元对象,阐述如何利用享元模式实现大量细粒度对象的高效复用,减少内存开销并提升性能。重点介绍享元模式中的内部状态与外部状态的区分、享元工厂的作用及代码示例,最后探讨单纯享元模式与复合享元模式的区别。

模式动机

   万事万物皆对象,使用面向对象技术可以很好地解决一些灵活性或可扩展性问题,但很多时候会增加类的个数和对象的个数。当对象数量太多时,将导致运行代价过高,带来性能上的问题。享元模式正是为解决这一类问题而诞生的。享元模式通过共享技术实现相同或相似对象的重用。
  首先,享元模式中有两个概念需要理解,内部状态和外部状态。在享元模式中可以共享的相同内容称为内部状态,而那些不能共享的、需要额外设置的内容称为外部状态,由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而我们要做的就是把内部状态进行共享,进行复用。在实际使用中,能够共享的内部状态是有限的,因此享元对象一般都设计为较小的对象,它所包含的内部状态较少,这种对象也称为细粒度对象。

模式定义

  运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池用于存储具有相同内部状态的享元对象。

模式结构

模式结构
  抽象享元类:Flyweight,抽象出一个接口,通过这个接口可以接受外部状态
  具体享元类:ConcreteFlyweight,具体享元对象
  非共享具体享元类:UnsharedConcreteFlyweight,非享元对象。并不是所有的flyweight子类都需要被共享,flyweight是共享成为可能,但也不是强制要求共享。所以可能会存在不需要共享的子类。
  享元工厂类:FlyweightFactory,负责创建和管理享元对象

代码示例

  假设我们需要开发一个文本编辑器,要求可以输入可以带有样式的文本信息。首先我们不考虑任何设计模式的情况下,采用面向对象的思想,可以实现如下:

//抽象接口
public interface Text {
    public void show();//把文本内容呈现出来
}
//Text接口的一个具体实现类,此类代表26个英文字母
public class Character implements Text{
    private String key;//字母的值,a-z,一共26个
    private String fontSize;//字体大小
    private String fontColor;//字体颜色
    private String backgroudColor;//背景颜色
    //有可能还有一大堆的样式属性,如对齐方式,是否粗体,是否斜体,是否下划线、超链接等等,就不一一写了……
    public Character(String key){
        this.key = key;
    }

    @Override
    public void show() {
        System.out.println(this.key);
    }
    //get、set方法省略....
}

  这种方式确实是可以的,但却会损失掉的一定的性能,假设一篇文档出现了成千上万甚至更多的英文字母,那么就会创建出无数多个Character实例,大大提高了内存开销。我们稍加分析可以发现,虽然这些实例很多,但是这些实例却是有公共部分的。也就是我们的key,我们的key总共也就a-z这26种情况,其他的只是样式不一样罢了。也就是说我们的key是可以共享的。读到这里应该可以发现,其实key就是我们享元模式中的内部状态,而其他的样式就是外部状态。
  下面我们以享元模式的方式来重构一下这个代码。把外部状态剥离出来,让内部状态作为享元对象,外部对象通过参数的方式注入。
  首先把外部状态抽离出来,封装到一个Style中

public class Style {
    private String fontSize;//字体大小
    private String fontColor;//字体颜色
    private String backgroudColor;//背景颜色
    //get、set方法省略……
}
//修改Text类,把Style作为参数传入show方法
public interface Text {
    public void show(Style style);//把字母显示出来
}
//享元对象
public class Character implements Text{
    private String key;
    public Character(String key){
        this.key = key;
    }
    @Override
    public void show(Style style) {
        System.out.println(style.getFontSize()+style.getFontColor()+style.getBackgroudColor()+":"+this.key);
    }
}
//这个类是关键,这个是享元工厂类,负责创建和管理享元对象
import java.util.HashMap;
public class CharacterFactory {
    private HashMap<String,Character> map = new HashMap<String,Character>();
    public Character getCharacter(String key){
        Character character = (Character)map.get(key);
        if(character==null){
            character = new Character(key);
            map.put(key, character);
        }
        return character;
    }
}
//客户端测试类
public class Client {
    public static void main(String[] args) {
        CharacterFactory factory = new CharacterFactory();
        Character a1 = factory.getCharacter("A");
        Character a2 = factory.getCharacter("A");
        System.out.println(a1==a2);//true,说明第二次是直接取的享元池中的对象,而不是重新创建的对象

        Style s = new Style();
        s.setFontSize("字号12");
        s.setFontColor("字体颜色红色");
        s.setBackgroudColor("背景颜色黄色");
        a1.show(s);//字号12字体颜色红色背景颜色黄色:A
    }
}

单纯享元模式和复合享元模式

  单纯享元模式:在单纯享元模式中,所有的享元对象都是可以共享的,即所有抽象享元类的子类都可共享,不存在非共享具体享元类。如下图:
单纯享元模式
  复合享元模式:将一些单纯享元使用组合模式加以组合,可以形成复合享元对象,这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享。如下图:
  复合享元模式

总结

  享元模式主要是运用共享技术支持大量细粒度对象的复用,享元模式的核心就在于享元对象工厂,该工厂负责创建和管理享元对象。享元模式会使系统结构变得复杂,在实际开发中用得不是特别多,所以使用享元模式时也要三思而后行,切忌为了使用设计模式而使用设计模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值