设计模式简讲程序员

23. 享元模式

2018-07-09  本文已影响0人  Next_吴思成

定义

享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。

通俗理解

大家都去过图书馆,图书馆里面有书、桌子、电脑,这些东西都是公用的,只需要你注册一个读书证,就能够使用到这些公共资源。那如果这些资源并不是公用的,将会发生什么事情?首先,图书馆要统计某天进入图书馆的人数、他们对桌子、对电脑、对书籍的要求;然后,图书馆要拿着统计的清单,到市场进行采购,运回图书馆提供读者使用;最后,当读者使用过后,把这堆桌子和电脑销毁,烧掉或者回收。

这真是一个土豪的世界。

在编程的时候,也需要考虑到,我们系统的资源并不是无限增长的,总会有一个上限,到达上限之后还继续申请资源,只会发生溢出,整个系统都会挂掉。那么,在new一个对象的时候,是否想到这个对象也占内存?是否知道如果大量的对象堆积的时候,也会造成系统的崩溃?就像图书馆的公用资源一样,如果对每一个读者都定制服务,都购买一张桌子、买一台电脑,那么再多的资本都会白白消耗掉。

对于图书馆来讲,最佳的方式,就是买一批桌子,这一批桌子是大家公用的,这样才能解决公用资源无限增长的问题。对于程序而言,我们创建对象的时候,应该也有这样的实现,如果对象是类似的,那么我们就从公共资源(对象池)里面取,这样才能大大降低系统的资源开销。

示例

业务按照图书馆的桌子来定义。

渣渣程序

每一个用户都new一个对象。
桌子类

public class Desk {
    private String str;
    public Desk(String str) {
        this.str = str;
    }
    public void info() {
        System.out.println("这是一张桌子"+str);
    }
}

主入口

public class Main {
    public static void main(String[] args) {
        Desk desk1 = new Desk("desk1");
        Desk desk2 = new Desk("desk2");
        // ...
        desk1.info();
        desk2.info();
        //...
    }
}

优化

类图

程序

桌子通用抽象类和实现类

public abstract class BaseDesk {
    public abstract String getInfo();
    public void info() {
        System.out.println("桌子信息为:" + this.getInfo());
    }
}
public class Desk1 extends BaseDesk {
    @Override
    public String getInfo() {
        return "桌子1";
    }
}
public class Desk2 extends BaseDesk {
    @Override
    public String getInfo() {
        return "桌子2";
    }
}

桌子工厂

public class DeskFactory {
    private static DeskFactory factory = new DeskFactory();
    private static Map<String, BaseDesk> publicDesk;

    public DeskFactory() {
        publicDesk = new HashMap<>(2);
        BaseDesk desk1 = new Desk1();
        BaseDesk desk2 = new Desk2();
        publicDesk.put("desk1", desk1);
        publicDesk.put("desk2", desk2);
    }

    public static DeskFactory getFactory() {
        return factory;
    }

    public static BaseDesk getDesk(String desk) {
        return publicDesk.get(desk);
    }
}

调用主类

public class Main {
    public static void main(String[] args) {
        DeskFactory factory = DeskFactory.getFactory();
        BaseDesk desk1_1 = factory.getDesk("desk1");
        BaseDesk desk1_2 = factory.getDesk("desk1");
        System.out.println(desk1_1==desk1_2);//true
        desk1_1.info();//桌子信息为:桌子1
        desk1_2.info();//桌子信息为:桌子1
    }
}

从上面的程序可以知道,最重要的是就是对象池,所有的数据都是从对象池里面获取的。

存在一种情况,桌子都是定制的,张张都一样,如果我需要特殊的桌子,那么要怎么处理呢?其实这就涉及了对象的内部状态和外部状态了,内部状态是指内部的不随着环境改变而改变的状态,例如程序里面的桌子1就是桌子1,不可能变成桌子2;而外部状态是随着环境改变而改变的,例如有个人带了瓶颜料去图书馆,他可以把他那张桌子喷成白色的,程序上只需要在在BaseDesk相关的代码就可以了。

颜色

public class Color {
    private String color;
    public Color(String color) {
        this.color = color;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
}

基础桌子类

public abstract class BaseDesk {
    //...
    public void info(Color color) {
        System.out.println("桌子信息为:" + this.getInfo()+",颜色为:"+color.getColor());
    }
    //...
}

入口

desk1_1.info(new Color("白色"));
desk1_1.info(new Color("黑色"));

优点

  1. 减少内存中对象的数量,相似相同的对象中内存只有一份,节约系统资源;
  2. 内外部状态分开,相互不影响。

缺点

  1. 区分内外部状态,使系统的逻辑复杂;
  2. 外部状态使得系统的运行时间变长。

应用场景

  1. 大量的相同或者相似的对象,造成内存的大量消耗;
  2. 大部分状态都可以外部化的,不适合内部状态容易变动的;
  3. 池化的技术消耗资源,多次重复使用的享元对象才值得使用享元模式。

实例

JDK String,使用常量池存储

吐槽

就是池化技术,从Map里面获取对象。

代码

e23_flyweight_pattern

https://www.jianshu.com/p/e6384fd3ad1d

上一篇 下一篇

猜你喜欢

热点阅读