1. 概念
享元模式(Flyweight Pattern), 运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。
在享元模式中可以共享的相同内容称为内部状态(Intrinsic State),而那些需要外部环境来设置的不能共享的内容称为外部状态(Extrinsic State),由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。
在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池(Flyweight Pool)用于存储具有相同内部状态的享元对象。
享元模式包含如下角色:
- Flyweight:抽象享元类
- ConcreteFlyweight:具体享元类
- UnsharedConcreteFlyweight:非共享具体享元类
- FlyweightFactory:享元工厂类
2. 代码实现
在了解了上述享元模式的基本概念后,我将模拟以下场景需求来实现享元模式以便我们更加深刻理解这个设计模式。我们这里以饭店服务为例,在饭店中有服务员对象,每个服务员会需要对不同的客户进行服务。这里的服务员就被多个客户所共享(享元类),但是每个服务员在同一时间只能为一名客户服务,即一对一的关系,只有完成后才可以为另一个客户所服务。
这里我们先创建一个享元抽象类:
/*** 可共享和不可共享状态*/
public abstract class AbstractWaitressFlyweight {boolean canService = true;//能否服务//正在服务。 享元的不可共享属性留给外部进行改变的接口abstract void service();//服务完成。 享元的不可共享属性留给外部进行改变的接口abstract void end();public boolean isCanService() {return canService;}
}
同时我们也可以实现该抽象,创建一个具体享元类:
/*** 具体享元类*/
@AllArgsConstructor
public class BeautifulWaitress extends AbstractWaitressFlyweight{String id;//工号String name;//名字int age;//年龄//以上是不变的@Overridevoid service() {System.out.println("工号:"+id+";"+name+" "+age+" 正在为您服务...");//改变外部状态this.canService = false;}@Overridevoid end() {System.out.println("工号:"+id+";"+name+" "+age+" 服务结束...请给五星好评");this.canService = true;}
}
接下来我们编写饭店类,注意此类是享元模式重点关注类。我们在此类中需要构建一个享元池,将服务员享元类构建出来后放到该享元池,这样外部系统调用时可以直接从该享元池获取,使用完成后通过改变享元类的状态将其重新放回池等待其他线程获取调用。
public class Resaurant {private static Map<String,AbstractWaitressFlyweight> pool = new HashMap<>();//享元,池子中有对象static {BeautifulWaitress waitress =new BeautifulWaitress("1111","张三",18);BeautifulWaitress waitress2 =new BeautifulWaitress("9527","李四",20);pool.put(waitress.id,waitress);pool.put(waitress2.id,waitress2);}public void addWaitress(AbstractWaitressFlyweight waitressFlyweight){pool.put(UUID.randomUUID().toString(),waitressFlyweight);}public static AbstractWaitressFlyweight getWaitress(String name){AbstractWaitressFlyweight flyweight = pool.get(name);if(flyweight == null){for (AbstractWaitressFlyweight value : pool.values()) {//当前共享对象能否是否if(value.isCanService()){return value;}};return null;}return flyweight;}}
最后我们编写外部调用类进行测试:
public class MainTest {public static void main(String[] args) {//1、A客户AbstractWaitressFlyweight waitress = Restaurant.getWaitress("");waitress.service();System.out.println(waitress);//2、B客户AbstractWaitressFlyweight waitress1 = Restaurant.getWaitress("");waitress1.service();System.out.println(waitress1);waitress1.end();//3、C客户AbstractWaitressFlyweight waitress2 = Restaurant.getWaitress("");System.out.println(waitress2);}
}
3. 应用场景
- 数据库连接池
- 所有的池化技术
- ...
4. 总结
享元模式在平时我们编写代码时可能并不会设计太多,但是就想上述所提到的,几乎很多池化技术都是通过享元模式来实现的,所以在我们阅读一些优秀框架的源码时我们也会遇见该模式的实现。
至此,设计模式篇的结构型模式就全部编写完成(由于组合模式我们平时四处可见没有什么讲解的必要就略过了;其次过滤器模式整体的思路也较为简单也直接略过)。结构型设计模式重点就在于开发人员在编写需求前就要对类的结构考虑清楚,是该选择组合模式还是继承来实现多个类的组合是我们开发过程中的重难点,我们需要根据具体的业务场景以及整个项目的性能来看待,并且当我们充分理解了结构型模式后阅读一些优秀的框架源码时也会更加得轻松。