java设计模式---亨元模式(Flyweight Pattern)

java设计模式—亨元模式(Flyweight Pattern)

1. 概述
当一个软件系统在运行时产生的对象数量太多,将导致运行代价过高,带来系统性能下降等问题。例如在一个文本字符串中存在很多重复的字符,如果每一个字符都用一个单独的对象来表示,将会占用较多的内存空间,那么我们如何去避免系统中出现大量相同或相似的对象,同时又不影响客户端程序通过面向对象的方式对这些对象进行操作?享元模式正为解决这一类问题而诞生。享元模式通过共享技术实现相同或相似对象的重用,在逻辑上每一个出现的字符都有一个对象与之对应,然而在物理上它们却共享同一个享元对象,这个对象可以出现在一个字符串的不同地方,相同的字符对象都指向同一个实例,在享元模式中,存储这些共享实例对象的地方称为享元池(Flyweight Pool)。我们可以针对每一个不同的字符创建一个享元对象,将其放在享元池中,需要时再从享元池取出
2. 定义
2.1 定义
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种对象结构型模式。
2.2 内外部状态
享元模式以共享的方式高效地支持大量细粒度对象的重用,享元对象能做到共享的关键是区分了内部状态(Intrinsic State)和外部状态(Extrinsic State)。
(1) 内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享。如字符的内容,不会随外部环境的变化而变化,无论在任何环境下字符“a”始终是“a”,都不会变成“b”。
(2) 外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态通常由客户端保存,并在享元对象被创建之后,需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。如字符的颜色,可以在不同的地方有不同的颜色,例如有的“a”是红色的,有的“a”是绿色的,字符的大小也是如此,有的“a”是五号字,有的“a”是四号字。而且字符的颜色和大小是两个独立的外部状态,它们可以独立变化,相互之间没有影响,客户端可以在使用时将外部状态注入享元对象中。
正因为区分了内部状态和外部状态,我们可以将具有相同内部状态的对象存储在享元池中,享元池中的对象是可以实现共享的,需要的时候就将对象从享元池中取出,实现对象的复用。通过向取出的对象注入不同的外部状态,可以得到一系列相似的对象,而这些对象在内存中实际上只存储一份。
3. UML图
java设计模式---亨元模式(Flyweight Pattern)_第1张图片
从上图中我们可以看到享元模式包含四个角色:
(1)Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
(2)ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
(3)UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
(4)FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。
4. 简单的代码实现

//定义享元类
public class Flyweight {
        //内部状态intrinsicState作为成员变量,同一个享元对象其内部状态是一致的
        private String internalState;

        public  Flyweight(String internalState) {
            this.internalState=internalState;
        }

        //外部状态extrinsicState在使用时由外部设置,不保存在享元对象中,即使是同一个对象,在每一次调用时也可以传入不同的外部状态
        public void operation(String  extrinsicState) {
              //TODO: 设置或者获取外部状态相关操作
        }
}
//定义享元工厂类
public class FlyweightFactory {
    HashMap flyweights = new HashMap<>();
    public Flyweight getFlyweight(String key)
    {
        //如果对象存在,则直接从享元池获取
        if(flyweights.containsKey(key)){
            return(Flyweight)flyweights.get(key);
        }
        //如果对象不存在,先创建一个新的对象添加到享元池中,然后返回
        else {
            Flyweight fw = new ConcreteFlyweight("new");
            flyweights.put(key,fw);
            return fw;
        }
    }
}
//定义一个具体的享元子类
public class ConcreteFlyweight extends Flyweight {
    public ConcreteFlyweight(String internalState) {
        super(internalState);
    }

    @Override
    public void operation(String extrinsicState) {
        super.operation(extrinsicState);
    }
}

5. 具体示例
我们就以数据库连接池为例,代码如下:

//定义connection(flyweight)
public class Connection {
    private String name;
    private String type;
    public Connection(String name,String type)
    {
        this.name = name;
        this.type = type;
    }
    public void connect()
    {
        System.out.println("The :"+type+" Connection :"+name +" is connected");
    }
}
//定义具体的享元子类
public class MySqlConnection extends Connection {
    public MySqlConnection(String name, String type) {
        super(name, type);
    }

    @Override
    public void connect() {
        super.connect();
    }
}
//定义享元工厂类
public class ConnectionFactory {
    static Map sTicketMap = new ConcurrentHashMap<>();
    private static final String TAG = "ConnectionFactory";

    public static Connection getConnection(String name, String type) {
        String key = name + "-" + type;

        if (sTicketMap.containsKey(key)) {
            return sTicketMap.get(key);
        } else {
            Connection connection = new MySqlConnection(name, type);
            sTicketMap.put(key, connection);
            return connection;
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Connection connection = ConnectionFactory.getConnection("Con1","Type1");
        connection.connect();
    }
}

6. 优缺点
6.1 优点:
大幅度的降低了内存中对象的数量
6.2 缺点:
使系统变得更加复杂,为了对象共享,将一些状态外部化,使得程序逻辑复杂化,因为状态的外部化,使得读取外部状态的导致运行时间变长。
7. 使用场景
(1)系统中存在大量的相似对象
(2)细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,,也就是说对象没有特定身份.
(3)需要缓冲池的场景

你可能感兴趣的:(设计模式)