Java 设计模式 - 享元模式

发布于 2024-11-03 01:57:56 字数 6582 浏览 8 评论 0

享元模式 (Flyweight): 主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。享元模式的设计思想是尽量复用已创建的对象,常用于工厂方法内部的优化。

如果一个对象实例一经创建就不可变,那么反复创建相同的实例就没有必要,直接向调用方返回一个共享的实例就行,这样即节省内存,又可以减少创建对象的过程,提高运行速度。

总是使用工厂方法而不是 new 操作符创建实例,可获得享元模式的好处。

在学习享元模式之前需要先了解一下 细粒度 和享元对象中的 内部状态、外部状态 这三个概念:

  • 内部状态:不随环境改变而改变的状态,内部状态可以共享,例如人的性别,不管任何环境下都不会改变
  • 外部状态:随着环境改变而改变的状态,不可以共享的状态,享元对象的外部状态通常由客户端保存,并在享元对象创建后,需要的时候传入享元对象内部,不同的外部状态是相互独立的。例如衣服和鞋子,人在不同的环境下会穿不同的衣服和鞋子,但是衣服和鞋子又是相互独立不受彼此影响的
  • 细粒度:较小的对象,所包含的内部状态较小

java 中的 string 字符的不变性其实就是享元模式的应用!

通过查看 Integer 类的源码我们可以看到 Integer 会把 [-128, 127] 之间的数字直接返回共享池中的对象:

//IntegerCache.low = -128
//IntegerCache.high = 127
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

实现

示例一

/**
* 抽象享元角色
* 父接口,以规定出所有具体享元角色需要实现的方法
*/
public interface Flyweight {

void operation(String state);

}


/**
* 具体享元角色
* 实现抽象享元角色所规定出的接口
*/
public class ConcreteFlyweight implements Flyweight{

private Character intrinsicState = null;

public ConcreteFlyweight(Character state) {
this.intrinsicState = state;
}

@Override
public void operation(String state) {
//内部状态不变
System.out.println(this.toString() + "intrinsicState is : " + intrinsicState);
//外部状态实时变化
System.out.println(this.toString() + "extrinsicState is : " + state);
}
}

/**
* 享元工厂
* 本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。
* 当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有一个符合要求的享元对象。
* - 如果已经有了,享元工厂角色就应当提供这个已有的享元对象;
* - 如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。
*/
public class FlyweightFactory {

private static final FlyweightFactory factory = new FlyweightFactory();

public static FlyweightFactory getInstance() {
return factory;
}

private FlyweightFactory() {}

private Map<Character, Flyweight> cache = new HashMap<>();

public Flyweight factory(Character character) {
Flyweight flyweight = cache.get(character);
if(flyweight == null) {
flyweight = new ConcreteFlyweight(character);
cache.put(character, flyweight);
}
return flyweight;
}

}



/**
* 享元模式测试类
*/
public class FlyweightTest {

public static void main(String[] args) {
Flyweight fly = FlyweightFactory.getInstance().factory('a');
fly.operation("First Call");

fly = FlyweightFactory.getInstance().factory('b');
fly.operation("Second Call");

fly = FlyweightFactory.getInstance().factory('a');
fly.operation("Third Call");
}

}

//输出
com.jonesun.tool.pattern.flyweight.ConcreteFlyweight@78e03bb5intrinsicState is : a
com.jonesun.tool.pattern.flyweight.ConcreteFlyweight@78e03bb5extrinsicState is : First Call
com.jonesun.tool.pattern.flyweight.ConcreteFlyweight@6ae40994intrinsicState is : b
com.jonesun.tool.pattern.flyweight.ConcreteFlyweight@6ae40994extrinsicState is : Second Call
com.jonesun.tool.pattern.flyweight.ConcreteFlyweight@78e03bb5intrinsicState is : a
com.jonesun.tool.pattern.flyweight.ConcreteFlyweight@78e03bb5extrinsicState is : Third Call

虽然客户端申请了三个享元对象,但是实际创建的享元对象只有两个,这就是共享的含义

示例二

我们再看一个具体一点的例子

/**
* 图书-相当于 Flyweight
*/
public interface Book {

void borrowBy(String studentName);

void backFrom(String studentName);
}

/**
* 具体实现的图书-相当于 ConcreteFlyweight
*/
public class ConcreteBook implements Book {

private String bookName;

public ConcreteBook(String bookName) {
this.bookName = bookName;
}


@Override
public void borrowBy(String studentName) {
System.out.println(studentName + "借了: " + bookName);
}

@Override
public void backFrom(String studentName) {
System.out.println(studentName + "归还了: " + bookName);
}
}

/**
* 图书馆-相当于 FlyweightFactory
*/
public class BookLibrary {

private Map<String, Book> bookMap = new HashMap<>();

private static final BookLibrary instance = new BookLibrary();

public static BookLibrary getInstance() {
return instance;
}

private BookLibrary() {}

public Book getBook(String bookName) {
if(bookMap.containsKey(bookName)) {
return bookMap.get(bookName);
}
Book book = new ConcreteBook(bookName);
bookMap.put(bookName, book);
return book;
}

public int bookSize() {
return bookMap.size();
}

}


public class BookLibraryTest {

public static void main(String[] args) {
Book book1 = BookLibrary.getInstance().getBook("图书 1");
book1.borrowBy("张三");
Book book2 = BookLibrary.getInstance().getBook("图书 2");

book2.borrowBy("张三");
Book book3 = BookLibrary.getInstance().getBook("图书 3");
book3.borrowBy("张三");

//一段时间后张三还完书,李四过来借阅
book1.backFrom("张三");
book2.backFrom("张三");
book3.backFrom("张三");

Book book4 = BookLibrary.getInstance().getBook("图书 1");
book4.borrowBy("李四");
Book book5 = BookLibrary.getInstance().getBook("图书 2");
book5.borrowBy("李四");

System.out.println("总共借出: " + BookLibrary.getInstance().bookSize() + "本图书");
}

}

//输出
张三借了: 图书 1
张三借了: 图书 2
张三借了: 图书 3
张三归还了: 图书 1
张三归还了: 图书 2
张三归还了: 图书 3
李四借了: 图书 1
李四借了: 图书 2
总共借出: 3 本图书

在使用的时候只需要记住享元模式的核心思想,然后根据自己的业务需求来选择,因为大多情况下都不会使用一种设计模式,而是多种设计模式的组合

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

呆头

暂无简介

0 文章
0 评论
24 人气
更多

推荐作者

巷子口的你

文章 0 评论 0

微信用户

文章 0 评论 0

神妖

文章 0 评论 0

7460852697

文章 0 评论 0

ligengkai

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文