享元模式以共享的方式高效地支持大量细粒度对象的重用,享元对象能作到共享的关键是区分了内部状态(Intrinsic State)和外部状态(Extrinsic State)。下面将对享元的内部状态和外部状态进行简单的介绍:java
(1)内部状态是存储在享元对象内部而且不会随环境改变而改变的状态,内部状态能够共享。如字符的内容,不会随外部环境的变化而变化,不管在任何环境下字符“a”始终是“a”,都不会变成“b”。 (2)外部状态是随环境改变而改变的、不能够共享的状态。享元对象的外部状态一般由客户端保存,并在享元对象被建立以后,须要使用的时候再传入到享元对象内部。一个外部状态与另外一个外部状态之间是相互独立的。如字符的颜色,能够在不一样的地方有不一样的颜色,例若有的“a”是红色的,有的“a”是绿色的,字符的大小也是如此,有的“a”是五号字,有的“a”是四号字。并且字符的颜色和大小是两个独立的外部状态,它们能够独立变化,相互之间没有影响,客户端能够在使用时将外部状态注入享元对象中。
在享元模式结构图中包含以下几个角色:mysql
● Flyweight(抽象享元类):一般是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法能够向外界提供享元对象的内部数据(内部状态),同时也能够经过这些方法来设置外部数据(外部状态)。 ● ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。一般咱们能够结合单例模式来设计具体享元类,为每个具体享元类提供惟一的享元对象。 ● UnsharedConcreteFlyweight(非共享具体享元类):并非全部的抽象享元类的子类都须要被共享,不能被共享的子类可设计为非共享具体享元类;当须要一个非共享具体享元类的对象时能够直接经过实例化建立。 ● FlyweightFactory(享元工厂类):享元工厂类用于建立并管理享元对象,它针对抽象享元类编程,将各类类型的具体享元对象存储在一个享元池中,享元池通常设计为一个存储“键值对”的集合(也能够是其余类型的集合),能够结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已建立的实例或者建立一个新的实例(若是不存在的话),返回新建立的实例并将其存储在享元池中。
1.主要优势sql
(1) 能够极大减小内存中对象的数量,使得相同或类似对象在内存中只保存一份,从而能够节约系统资源,提升系统性能。 (2) 享元模式的外部状态相对独立,并且不会影响其内部状态,从而使得享元对象能够在不一样的环境中被共享。
2.主要缺点数据库
(1) 享元模式使得系统变得复杂,须要分离出内部状态和外部状态,这使得程序的逻辑复杂化。 (2) 为了使对象能够共享,享元模式须要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。
3.适用场景编程
(1) 一个系统有大量相同或者类似的对象,形成内存的大量耗费。 (2) 对象的大部分状态均可之外部化,能够将这些外部状态传入对象中。 (3) 在使用享元模式时须要维护一个存储享元对象的享元池,而这须要耗费必定的系统资源,所以,应当在须要屡次重复使用享元对象时才值得使用享元模式。
享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候能够减小内存的开销,一般与工厂模式一块儿使用。性能
一提到共享池,咱们很容易联想到Java里面的JDBC链接池,想一想每一个链接的特色,咱们不难总结出:适用于做共享的一些个对象,他们有一些共有的属性,就拿数据库链接池来讲,url、driverClassName、username、password及dbname,这些属性对于每一个链接来讲都是同样的,因此就适合用享元模式来处理,建一个工厂类,将上述相似属性做为内部数据,其它的做为外部数据,在方法调用时,当作参数传进来,这样就节省了空间,减小了实例的数量。看个例子:url
看下数据库链接池的代码:设计
public class ConnectionPool { /*享元池*/ private Vector<Connection> pool; /*内部状态*/ private String url = "jdbc:mysql://localhost:3306/test"; private String username = "root"; private String password = "root"; private String driverClassName = "com.mysql.jdbc.Driver"; private int poolSize = 100; private static ConnectionPool instance = null; Connection conn = null; /*构造方法,作一些初始化工做*/ private ConnectionPool() { pool = new Vector<Connection>(poolSize); for (int i = 0; i < poolSize; i++) { try { Class.forName(driverClassName); conn = DriverManager.getConnection(url, username, password); pool.add(conn); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } /* 返回链接到链接池 */ public synchronized void release() { pool.add(conn); } /* 返回链接池中的一个数据库链接 */ public synchronized Connection getConnection() { if (pool.size() > 0) { Connection conn = pool.get(0); pool.remove(conn); return conn; } else { return null; } } }
经过链接池的管理,实现了数据库链接的共享,不须要每一次都从新建立链接,节省了数据库从新建立的开销,提高了系统的性能!code