「补课」进行时:设计模式(21)——享元模式
共 5158字,需浏览 11分钟
·
2020-12-27 06:17
1. 前文汇总
2. 享元模式
2.1 定义
享元模式(Flyweight Pattern)很简单,它解决的需求也很直接,同时它也是池技术的重要实现方式,先看下它的定义:
Use sharing to support large numbers of fine-grained objects efficiently.(使用共享对象可有效地支持大量的细粒度的对象。)
2.2 通用类图
Flyweight 抽象享元角色:它是一个产品的抽象类, 同时定义出对象的外部状态和内部状态的接口或实现。 ConcreteFlyweight 具体享元角色:具体的一个产品类, 实现抽象角色定义的业务。 unsharedConcreteFlyweight 不可共享的享元角色:不存在外部状态或者安全要求(如线程安全) 不能够使用共享技术的对象, 该对象一般不会出现在享元工厂中。 FlyweightFactory 享元工厂:它的职责非常简单, 就是构造一个池容器, 同时提供从池中获得对象的方法。
2.3 通用代码
抽象享元角色:
public abstract class Flyweight {
// 内部状态
private String intrinsic;
// 外部状态
protected final String extrinsic;
// 要求享元角色必须接受外部状态
protected Flyweight(String extrinsic) {
this.extrinsic = extrinsic;
}
// 定义业务操作
abstract void operate();
public String getIntrinsic() {
return intrinsic;
}
public void setIntrinsic(String intrinsic) {
this.intrinsic = intrinsic;
}
}
具体享元角色:
public class ConcreteFlyweight1 extends Flyweight{
protected ConcreteFlyweight1(String extrinsic) {
super(extrinsic);
}
@Override
void operate() {
}
}
public class ConcreteFlyweight2 extends Flyweight{
protected ConcreteFlyweight2(String extrinsic) {
super(extrinsic);
}
@Override
void operate() {
}
}
享元工厂:
public class FlyweightFactory {
// 定义一个池容器
private static HashMap pool = new HashMap<>();
// 享元工厂
public static Flyweight getFlyweight(String Extrinsic) {
// 需要返回的对象
Flyweight flyweight = null;
// 在池中没有该对象
if(pool.containsKey(Extrinsic)) {
flyweight = pool.get(Extrinsic);
} else {
// 根据外部状态创建享元对象
flyweight = new ConcreteFlyweight1(Extrinsic);
// 放置到池中
pool.put(Extrinsic, flyweight);
}
return flyweight;
}
}
2.4 优缺点
享元模式是一个非常简单的模式, 它可以大大减少应用程序创建的对象, 降低程序内存的占用, 增强程序的性能, 但它同时也提高了系统复杂性, 需要分离出外部状态和内部状态, 而且外部状态具有固化特性, 不应该随内部状态改变而改变, 否则导致系统的逻辑混乱。
3. 一个小例子
享元模式很简单,上面的通用代码其实就是一个很好的示例,类似于 Java 中的 String 常量池,没有的对象创建后存在池中,若池中存在该对象则直接从池中取出。
我这里还是再举一个简单的例子,比如接了我一个小型的外包项目,是做一个产品展示网站,后来他的朋友们也希望做这样的网站,但要求都有些不同,我们当然不能直接复制粘贴再来一份,有人希望是视频站,有人希望是图文站等等,而且因为经费原因不能每个网站租用一个空间。
这种事情在生活中很长见,不过大多数情况都是直接 copy 一份代码,再做做改动,但是在享元模式中,就不存在这种情况啦~~~
网站抽象类:
public abstract class WebSite {
abstract void use();
}
具体网站类:
public class ConcreteWebSite extends WebSite {
private String name;
public ConcreteWebSite(String name) {
this.name = name;
}
@Override
void use() {
System.out.println("网站分类:" + name);
}
}
网络工厂类:
public class WebSiteFactory {
private HashMap pool = new HashMap<>();
//获得网站分类
public WebSite getWebSiteCategory(String key) {
if(!pool.containsKey(key)) {
pool.put(key, new ConcreteWebSite(key));
}
return pool.get(key);
}
//获得网站分类总数
public int getWebSiteCount() {
return pool.size();
}
}
Client 客户端:
public class Client {
public static void main(String[] args) {
WebSiteFactory factory = new WebSiteFactory();
WebSite fx = factory.getWebSiteCategory("视频站");
fx.use();
WebSite fy = factory.getWebSiteCategory("视频站");
fy.use();
WebSite fz = factory.getWebSiteCategory("视频站");
fz.use();
WebSite fa = factory.getWebSiteCategory("图文站");
fa.use();
WebSite fb = factory.getWebSiteCategory("图文站");
fb.use();
WebSite fc = factory.getWebSiteCategory("图文站");
fc.use();
System.out.println("网站分类总数为:" + factory.getWebSiteCount());
}
}
执行结果:
网站分类:视频站
网站分类:视频站
网站分类:视频站
网站分类:图文站
网站分类:图文站
网站分类:图文站
网站分类总数为:2
可以看出,虽然我们做了 6 个网站,但网站分类只有 2 个。
这样基本算是实现了享元模式的共享对象的目的,但是这里实际上没有体现对象间的不同。
我们再加入一个用户类:
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
然后对 WebSite
和 ConcreteWebSite
的 use()
方法进行修改,添加 User 参数:
public abstract class WebSite {
abstract void use(User user);
}
public class ConcreteWebSite extends WebSite {
private String name;
public ConcreteWebSite(String name) {
this.name = name;
}
@Override
void use(User user) {
System.out.println("网站分类:" + name + " 用户:" + user.getName());
}
}
最后修改一下 Client 类:
public class Client {
public static void main(String[] args) {
WebSiteFactory factory = new WebSiteFactory();
WebSite fx = factory.getWebSiteCategory("视频站");
fx.use(new User("tom"));
WebSite fy = factory.getWebSiteCategory("视频站");
fy.use(new User("cat"));
WebSite fz = factory.getWebSiteCategory("视频站");
fz.use(new User("nginx"));
WebSite fa = factory.getWebSiteCategory("图文站");
fa.use(new User("apache"));
WebSite fb = factory.getWebSiteCategory("图文站");
fb.use(new User("netty"));
WebSite fc = factory.getWebSiteCategory("图文站");
fc.use(new User("jboss"));
System.out.println("网站分类总数为:" + factory.getWebSiteCount());
}
}
最终结果:
网站分类:视频站 用户:tom
网站分类:视频站 用户:cat
网站分类:视频站 用户:nginx
网站分类:图文站 用户:apache
网站分类:图文站 用户:netty
网站分类:图文站 用户:jboss
网站分类总数为:2
这样就可以协调内部与外部状态,哪怕接手了上千个网站的需求,只要要求相同或类似,实际开发代码也就是分类的那几种。