设计模式——享元模式
1- 享元模式的定义
享元模式:享元模式(FlyweightPattern)即共享对象的模式。在实际业务场景中,经常出现这样的一种情况——一个对象的属性值从对象被创建出来后基本就是不变的,并且在很多地方都需要使用到这个对象,此时就可以考虑使用享元模式了!享元模式主要解决的就是减少相同对象的创建,以便节省内存空间,从而提高系统的性能。使用享元模式时,如果需要某一个对象,那么就会尝试找到可以重用的对象,如果没有找到的话就会创建出这样的一个对象并返回,同时将创建的对象保存起来,以便下一次需要相同的对象的时候直接返回,这样就可以达到共享的目的,从而节省系统内存空间,提高性能!
No BB,Show Code!
2- 享元模式的代码实现
为了更好的理解享元模式,定义如下一些业务类,Book,Publisher!
定义Book类,表示图书,代码如下:package designPattern.test.flyweight;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 图书类
*/
public class Book implements Serializable{
//图书ID
private Long id;
//作者
private String author;
//书名
private String bookName;
//价格
private BigDecimal price;
//ISBN号
private String isbn;
//出版社
private Publisher publisher;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public Publisher getPublisher() {
return publisher;
}
public void setPublisher(Publisher publisher) {
this.publisher = publisher;
}
}
定义Publisher类,表示出版社,每一本图书都会有一个出版社对象,代码如下:
package designPattern.test.flyweight;
import java.io.Serializable;
/**
* 出版社类,一个出版社可能对应很多的图书
* 出版社相对来说就是一个固定的类,该对象的信息很少变化
* 适合应用享元模式
*/
public class Publisher implements Serializable {
//出版社ID
private Long id;
//出版社名称
private String publisherName;
//出版社联系人
private String contactName;
//出版社联系人电话
private String contactPhone;
//出版社地址
private String address;
//出版社邮编
private String postCode;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPublisherName() {
return publisherName;
}
public void setPublisherName(String publisherName) {
this.publisherName = publisherName;
}
public String getContactName() {
return contactName;
}
public void setContactName(String contactName) {
this.contactName = contactName;
}
public String getContactPhone() {
return contactPhone;
}
public void setContactPhone(String contactPhone) {
this.contactPhone = contactPhone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPostCode() {
return postCode;
}
public void setPostCode(String postCode) {
this.postCode = postCode;
}
}
图书对象可能非常多,但是出版社的数量却不是很多,且出版社的信息一般很少变动,所以可以考虑将出版社对象作为共享对象,定义一个管理共享对象的工厂,PublisherFactory,代码如下:
package designPattern.test.flyweight;
import com.google.common.base.Preconditions;
import java.util.HashMap;
import java.util.Map;
/**
* 出版社工厂:将出版社对象放在该类中持有的Map集合中
*/
public class PublisherFactory {
//保存出版社ID和出版社对象键值对的集合
private static Map<Long, Publisher> publisherMap = new HashMap<>();
/**
* 根据ID获取出版社对象,如果获取到就返回,否则新建一个Publisher对象返回
* 并放在publisherMap中以便下次使用
*/
public Publisher getPublisher(Long id) {
Preconditions.checkNotNull(id);
if (null != publisherMap.get(id)) {
return publisherMap.get(id);
}
//如果集合中没有此信息的话,可能是从数据库中查询出来,或者通过其他方式获取到
System.out.print("publisherMap没有此对象,模拟创建一个对象");
Publisher publisher = new Publisher();
publisher.setId(Long.valueOf(publisherMap.size() + 1));
publisher.setPublisherName("第" + publisherMap.size() + 1 + "出版社");
publisher.setContactName("第" + publisherMap.size() + 1 + "出版社联系人");
publisher.setContactPhone(getRandomPhone());
publisher.setAddress("北京"+publisherMap.size() + 1+"地址");
publisher.setPostCode("100010");
publisherMap.put(publisher.getId(),publisher);
return publisher;
}
private String getRandomPhone() {
StringBuffer sb = new StringBuffer("1");
for (int i = 0; i < 10; i++) {
sb.append((int) (10 * Math.random()));
}
return sb.toString();
}
}
在PublisherFactory中维护一个publisherMap集合,将创建出来的(实际项目中可能是从数据库中查询出来的,也可能是从外部获取的),publisherMap中存放id和id对应的Publisher对象,每次调用PublisherFactory的getPublisher方法时就会查看publisherMap中是否存在对象,有就直接返回了,没有的话才去创建或者获取返回并存放在publisherMap中,以便下次获取!
3- 测试
测试的时候需要确定共享的对象确实是一个对象即可,测试代码如下:
package designPattern.test.flyweight;
import org.junit.Test;
import java.math.BigDecimal;
/**
* 享元模式测试
*/
public class FlyWeightTest {
private static PublisherFactory factory = new PublisherFactory();
@Test
public void testFlyWeight(){
Book book1 = new Book();
Book book2 = new Book();
Book book3 = new Book();
Book book4 = new Book();
book1.setId(1l);
book1.setAuthor("罗贯中");
book1.setBookName("三国演义");
book1.setIsbn("1234567001");
book1.setPrice(new BigDecimal(20.50));
book1.setPublisher(factory.getPublisher(1l));
book2.setId(2l);
book2.setAuthor("曹雪芹");
book2.setBookName("红楼梦");
book2.setIsbn("1234567002");
book2.setPrice(new BigDecimal(25.50));
book2.setPublisher(factory.getPublisher(2l));
book3.setId(1l);
book3.setAuthor("施耐庵");
book3.setBookName("水浒传");
book3.setIsbn("1234567003");
book3.setPrice(new BigDecimal(30.50));
book3.setPublisher(factory.getPublisher(1l));
book4.setId(1l);
book4.setAuthor("吴承恩");
book4.setBookName("西游记");
book4.setIsbn("1234567004");
book4.setPrice(new BigDecimal(23.50));
book4.setPublisher(factory.getPublisher(2l));
System.out.println(book1.getPublisher() == book3.getPublisher());
System.out.println(book2.getPublisher() == book4.getPublisher());
}
}
打印结果:
publisherMap没有此对象,模拟创建一个对象
publisherMap没有此对象,模拟创建一个对象
true
true
可知共享对象确实是创建了两次,book1和book3的Publisher属性确实是一个对象,book2和book4的Publisher属性确实是一个对象!
总结:享元模式可以共享完全相同的对象,如果系统中确实需要大量的完全相同的对象使用此模式确实可以节省很多内存,但是实际项目中并非如此,这就需要将一些固定不变的属性提取出来作为共享对象,这就需要根据具体情况而定了!