结构型设计模式07-享元模式

享元模式

1、享元模式介绍

享元模式是一种结构型设计模式,旨在**通过共享对象来减少内存使用和提高性能。它主要用于处理大量细粒度对象**的情况,其中许多对象具有相似的属性和行为。

在享元模式中,对象分为两种类型:内部状态(Intrinsic State)和外部状态(Extrinsic State)。

  1. 内部状态是对象的固有属性,它们不随外部环境的改变而改变。
  2. 外部状态取决于外部环境,它们在运行时可以改变。

享元模式的核心思想是**将具有相同内部状态的对象共享**,以减少内存占用。当需要创建一个对象时,首先检查是否已经存在具有相同内部状态的对象。如果存在,则重用该对象,而不是创建一个新的对象。如果不存在,则创建一个新的对象并将其添加到共享池中,以供以后使用。

1.1 享元模式基本实现

享元模式代码结构图:

Flyweight类,是所有具体享元类的超类或接口,通过这个接口, Flyweight可以接受并作用于外部状态。

/*** @author Shier* CreateTime 2023/5/22 16:06* Flyweight类 - 接受外部状态*/
public abstract class Flyweight {public abstract void operation(int extrinsicState);
}

ConcreteFlyweight是继承Flyweight超类或实现Flyweight接口,并为内部状态增加存储空间。

/*** @author Shier* CreateTime 2023/5/22 16:09* 需要共享的具体Flyweight子类*/
public class ConcreteFlyweight extends Flyweight {@Overridepublic void operation(int extrinsicState) {System.out.println("需要共享的具体Flyweight子类:" + extrinsicState);}
}

UnsharedConcreteFlyweight是指那些不需要共享的Flyweight子类。因为Flyweight接口共享成为可能,但它并不强制共享。

/*** @author Shier* CreateTime 2023/5/22 16:09* 需要共享的具体Flyweight子类*/
public class UnsharedConcreteFlyweight extends Flyweight {@Overridepublic void operation(int extrinsicState) {System.out.println("不需要共享的具体Flyweight子类:" + extrinsicState);}
}

FlyweightFactory是一个享元工厂,用来创建并管理Flyweight对象。它主要是用来确保合理地共享Flyweight,当用户请求一个Flyweight时, FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)。

/*** @author Shier* CreateTime 2023/5/22 16:12* 享元工厂*/
public class FlyweightFactory {private Hashtable<String, Flyweight> flyweights = new Hashtable<String, Flyweight>();/*** 初始化工厂三个实例*/public FlyweightFactory() {flyweights.put("A", new ConcreteFlyweight());flyweights.put("B", new ConcreteFlyweight());flyweights.put("C", new ConcreteFlyweight());}/*** 根据客户端请求,获得已生成的实例** @param key* @return*/public Flyweight getFlyweight(String key) {return flyweights.get(key);}
}

客户端代码:

/*** @author Shier* CreateTime 2023/5/22 16:16*/
public class FlyweightClient {public static void main(String[] args) {int extrinsicState = 22;FlyweightFactory factory = new FlyweightFactory();Flyweight flyweightA = factory.getFlyweight("A");flyweightA.operation(--extrinsicState);Flyweight flyweightB = factory.getFlyweight("B");flyweightB.operation(--extrinsicState);Flyweight flyweightC = factory.getFlyweight("C");flyweightC.operation(--extrinsicState);// 不要共享的UnsharedConcreteFlyweight unsharedFly = new UnsharedConcreteFlyweight();unsharedFly.operation(--extrinsicState);}
}

输出结果:

2、具体例子说明

2.1 不使用享元模式 - 接单做网站

//网站
public class WebSite {private String name = "";public WebSite(String name) {this.name = name;}public void use() {System.out.println("网站分类:" + name);}
}

客户端:

public class Test {public static void main(String[] args) {WebSite fx = new WebSite("产品展示");fx.use();WebSite fy = new WebSite("产品展示");fy.use();WebSite fz = new WebSite("产品展示");fz.use();WebSite fl = new WebSite("博客");fl.use();WebSite fm = new WebSite("博客");fm.use();WebSite fn = new WebSite("博客");fn.use();}
}

结果显示:

2.2 适用享元模式 - 接单做网站

2.2.1 第一版

网站抽象类:

/*** @author Shier* CreateTime 2023/5/22 16:32*/
public abstract class WebSite {public abstract void use();
}

具体网站类:

/*** @author Shier* CreateTime 2023/5/22 16:32*/
public class ConcreteWebSite extends WebSite {private String name = "";public ConcreteWebSite(String name) {this.name = name;}@Overridepublic void use() {System.out.println("网站分类:" + name);}
}

网站工厂类:

/*** @author Shier* CreateTime 2023/5/22 16:33*/
public class WebSiteFactory {private Hashtable<String, WebSite> flyweights = new Hashtable<>();/*** 获得网站分类** @param key* @return*/public WebSite getWebSiteCategory(String key) {if (!flyweights.contains(key)) {flyweights.put(key, new ConcreteWebSite(key));}return flyweights.get(key);}/*** 获得网站实例个数** @return*/public int getWebSiteCount() {return flyweights.size();}
}

客户端:

public class WebStateClient1 {public static void main(String[] args) {WebSiteFactory webSiteFactory = new WebSiteFactory();WebSite siteCategory = webSiteFactory.getWebSiteCategory("产品展示");siteCategory.use();WebSite siteCategory1 = webSiteFactory.getWebSiteCategory("产品展示");siteCategory1.use();WebSite siteCategory2 = webSiteFactory.getWebSiteCategory("产品展示");siteCategory2.use();WebSite siteCategory3 = webSiteFactory.getWebSiteCategory("博客");siteCategory3.use();WebSite siteCategory4 = webSiteFactory.getWebSiteCategory("博客");siteCategory4.use();WebSite siteCategory5 = webSiteFactory.getWebSiteCategory("博客");siteCategory5.use();System.out.println("总共创建了 " + webSiteFactory.getWebSiteCount() + " 个实例");}
}

结果:

2.2.2 内部状态 - 外部状态 - 享元模式 - 接单做网站

代码结构图:

用户类,用于网站的客户账号,是"网站"类的外部状态。

/*** @author Shier* CreateTime 2023/5/22 16:49*/
public class User {private String name;public User(String name) {this.name = name;}public String getName() {return name;}
}

网站抽象类:

/*** @author Shier* CreateTime 2023/5/22 16:32*/
public abstract class WebSite {public abstract void use(User user);
}

具体网站类:

/*** @author Shier* CreateTime 2023/5/22 16:32*/
public class ConcreteWebSite extends WebSite {private String name = "";public ConcreteWebSite(String name) {this.name = name;}@Overridepublic void use(User user) {System.out.println("网站分类:" + name + " 来自客户:" + user.getName() + "的需求");}
}

网站工厂类不要改变同上。

客户端:

public class WebStateClient1 {public static void main(String[] args) {WebSiteFactory webSiteFactory = new WebSiteFactory();WebSite siteCategory = webSiteFactory.getWebSiteCategory("产品展示");siteCategory.use(new User("十二1号"));WebSite siteCategory1 = webSiteFactory.getWebSiteCategory("产品展示");siteCategory1.use(new User("十二2号"));WebSite siteCategory2 = webSiteFactory.getWebSiteCategory("产品展示");siteCategory2.use(new User("十二3号"));WebSite siteCategory3 = webSiteFactory.getWebSiteCategory("博客");siteCategory3.use(new User("十二4号"));WebSite siteCategory4 = webSiteFactory.getWebSiteCategory("博客");siteCategory4.use(new User("十二5号"));WebSite siteCategory5 = webSiteFactory.getWebSiteCategory("博客");siteCategory5.use(new User("十二6号"));System.out.println("总共创建了 " + webSiteFactory.getWebSiteCount() + " 个实例");}
}

最后得到的结果:

3、享元模式总结

3.1 享元模式应用

实际上在Java中,字符串String就是运用了Flyweight模式。举个例子吧。== 可以用来确定 titleA 与 titleB 是否是相同的实例,返回值为 boolean 值。当用 new String() 方法时,两个对象 titleA 和 titleB 的引用地址是不相同的,但当 titleC 和 titleD 都使用赋值的方式时,两个字符串的引用地址竟然是相同的。

得出结果:

3.2 优缺点、适用场景总结

优点

  1. 内存优化:通过共享对象的内部状态,可以大大减少对象的数量,从而节省内存空间。
  2. 性能提升:减少对象数量和内存占用可以提高系统的性能,特别是在处理大量细粒度对象时。
  3. 可扩展性:享元模式可以轻松扩展以支持新的外部状态,而无需修改现有的共享对象。
  4. 对象复用:通过共享对象,可以实现对象的复用,避免重复创建相同的对象,提高系统的效率和资源利用率。

缺点:

  1. 共享对象可能引入线程安全问题:如果多个线程同时访问和修改共享对象的状态,需要考虑线程安全性,以避免并发访问引发的问题。
  2. 对象状态的外部化:为了实现对象共享,部分对象状态需要外部化,这可能会增加代码复杂性和维护成本。

适用场景:

  1. 当系统中存在大量相似对象,且这些对象的内部状态相同或相似,而外部状态有所不同时,可以考虑使用享元模式。
  2. 当需要创建大量对象,但创建和销毁对象的代价很高时,可以使用享元模式来复用已有对象,提高系统性能。
  3. 当系统需要缓存对象以提高性能,并且可以忍受一定程度的对象状态变化时,享元模式也是一个不错的选择。
  4. 当对象的状态可以被多个对象共享,并且这些对象之间的外部状态可以相对独立时,可以使用享元模式。

总之,享元模式适用于大量细粒度对象的场景,通过共享对象的内部状态来减少对象数量和内存占用,从而提高系统性能和资源利用率。它在需要重复创建相似对象的情况下特别有用,并且适用于多线程环境,但需要注意线程安全性。

这可能会增加代码复杂性和维护成本。

适用场景:

  1. 当系统中存在大量相似对象,且这些对象的内部状态相同或相似,而外部状态有所不同时,可以考虑使用享元模式。
  2. 当需要创建大量对象,但创建和销毁对象的代价很高时,可以使用享元模式来复用已有对象,提高系统性能。
  3. 当系统需要缓存对象以提高性能,并且可以忍受一定程度的对象状态变化时,享元模式也是一个不错的选择。
  4. 当对象的状态可以被多个对象共享,并且这些对象之间的外部状态可以相对独立时,可以使用享元模式。

总之,享元模式适用于大量细粒度对象的场景,通过共享对象的内部状态来减少对象数量和内存占用,从而提高系统性能和资源利用率。它在需要重复创建相似对象的情况下特别有用,并且适用于多线程环境,但需要注意线程安全性。

本文链接:https://my.lmcjl.com/post/5356.html

展开阅读全文

4 评论

留下您的评论.