设计模式之享元模式(Flyweight Pattern)【结构性模式】
为什么用结构性模式?
结构性模式
关注点“怎样组合对象/类
?”所以我们关注下类的组合关系类结构型模式
关心类的组合,由多个类可以组合成一个更大的(继承)对象结构型模式
关心类与对象的组合,通过关联关系
在一个类中定义另一个类的实例对象(组合)- 根据“
合成复用原则
”,在系统中尽量使用关联关系来替代继承关系
,因此大部分结构型模式都是对象结构型模式
。
- 适配器模式(Adapter Pattern):两个不兼容接口之间适配的桥梁。
- 桥接模式(Bridge Pattern):相同功能抽象化与实现化解耦,抽象与实现可以独立升级。
- 过滤器模式(Filter、Criteria Pattern):使用不同的标准来过滤一组对象。
- 组合模式(Composite Pattern):相似对象进行组合,形成树形结构。
- 装饰器模式(Decorator Pattern):向一个现有的对象添加新的功能,同时又不改变其结构。
- 外观模式(Facade Pattern):向现有的系统添加一个接口,客户端访问此接口来隐藏系统的复杂性。
- 享元模式(Flyweight Pattern):尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。
- 代理模式(Proxy Pattren):一个类代表另一个类的功能。
🍭结构性模式之享元模式(Flyweight Pattern)
🍎享元模式
- 享元模式(Flyweight Pattern),运用共享技术有效地支持大量细粒度对象的服用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。对象结构型。
- 在享元模式中
可以共享的相同内容为内部状态(IntrinsicState)
,而那些需要外部环境来设置的不能共享的内容称为外部状态(Extrinsic State)
,由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。 - 在享元模式中通常会出现工厂模式,需要创建一个
享元工厂来负责维护一个享元池
(Flyweight Pool)用于存储具有相同内部状态的享元对象。
】
享元模式包含如下角色:
- Flyweight:抽象享元类
- ConcreteFlyweight:具体享元类
- UnsharedConcreateFlyweight:非共享具体享元类
- FlyweightFactory:享元工厂类
🍔代码实现
/**
* 可共享状态和不可共享状态
*
* 享元和原型
* 1、享元返回的是这个人本身
* 2、原型返回的是克隆人
*/
public abstract class AbstractWaitressFlyweight {
boolean canService = true; // 能否服务
// 正在服务。享元的不可共享属性留给外部进行改变的接口。
abstract void service();
// 服务完成。享元的不可共享属性留给外部进行改变的接口。
abstract void end();
public boolean isCanService() {
return canService;
}
}
/**
* 具体享元类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BeautifulWaitress extends AbstractWaitressFlyweight {
String id; // 工号
String name; // 名字
int age; // 年龄
// 以上是共享的,不变的
@Override
void service() {
System.out.println("工号:" + id + " 名字: " + name + " 年龄:" + age + " 正在为您服务");
// 改变外部状态
this.canService = false;
}
@Override
void end() {
System.out.println("工号:" + id + " 名字: " + name + " 年龄:" + age + " 服务结束");
this.canService = true;
}
}
/**
* 足道店:这相当于享元工厂。店里有很多服务员
*/
public class ZuDao {
private static Map<String, AbstractWaitressFlyweight> pool = new HashMap<>();
public void addWaitress(AbstractWaitressFlyweight waitress) {
pool.put(UUID.randomUUID().toString(), waitress);
}
// 享元,池子中有对象
static {
BeautifulWaitress waitress1 = new BeautifulWaitress("1111", "张三", 1);
BeautifulWaitress waitress3 = new BeautifulWaitress("9527", "张三", 3);
pool.put(waitress1.getId(), waitress1);
pool.put(waitress3.getId(), waitress3);
}
public static AbstractWaitressFlyweight getWaitress(String name) {
AbstractWaitressFlyweight flyweight = pool.get(name);
if (flyweight == null) {
for (AbstractWaitressFlyweight waitressFlyweight : pool.values()) {
// 当前共享对象能否使用
if (waitressFlyweight.isCanService()) {
return waitressFlyweight;
}
}
return null;
} else {
return flyweight;
}
}
}
public class MainTest {
public static void main(String[] args) {
// 1.我
AbstractWaitressFlyweight waitress1 = ZuDao.getWaitress("");
waitress1.service();
System.out.println(waitress1);
// 2.张三
AbstractWaitressFlyweight waitress2 = ZuDao.getWaitress("");
waitress2.service();
System.out.println(waitress2);
waitress2.end();
// 3.李四
AbstractWaitressFlyweight waitress3 = ZuDao.getWaitress("");
waitress3.service();
System.out.println(waitress3);
}
}
工号:1111 名字: 张三 年龄:1 正在为您服务
BeautifulWaitress(id=1111, name=张三, age=1)
工号:9527 名字: 张三 年龄:3 正在为您服务
BeautifulWaitress(id=9527, name=张三, age=3)
工号:9527 名字: 张三 年龄:3 服务结束
工号:9527 名字: 张三 年龄:3 正在为您服务
BeautifulWaitress(id=9527, name=张三, age=3)
🍕应用场景
- 典型的代表:数据库连接池
- 所有的池化技术
- 享元和原型模式有什么区别?享元是预先准备好的对象进行复用,原型没法确定预先有哪些。