如果在一个系统中存在多个相同的对象,那么只需要共享一份对象的拷贝,而不必为每一次使用都创建新的对象。目的是提高系统性能。
上面的概念乍一听好像单例模式其实不是,单例模式只保存一个对象,但是这里可以有很多个不同对象,但是每个对象只有一个实例而已。也就是说享元模式使用了单例模式。
例子解释张三去借书,然后阅读完了还回去了,过一段时间发现还是不懂,又去借了同样的书,但是这本书其实和上一次借的书是同一本。李四也去借书,发现书架上没有,就去图书管理员那边拿出来了一本全新的书。这就是享元模式。
享元模式的主要角色由享元工厂、抽象享元、具体享元类几部分组成。
我们使用这个例子以类图的角度来观察一下:
从上面这个例子我们可以看到,这里其实有四个角色:
(1)享元工厂(Llibrary):用于创建具体享元类,维护相同的享元对象。当请求对象已经存在时,直接返回对象,不存在时,在创建对象。在例子中的解释就是图书馆,保存了所有的书,当学生借书时,有就拿走,没有买一本新书。这里面其实是使用了单例模式的。
(2)抽象享元(Book):定义需要共享的对象业务接口。享元类被创建出来总是为了实现某些特定的业务逻辑.
(3)具体享元(ConcreteBook):实现抽象享元类的接口,完成某一具体逻辑。在这里表示可以被借出。
在这里享元工厂是享元模式的核心,它需要确保系统可以共享相同的对象。它会维护一个对象列表,当我们想要获取享元类时,如果请求的享元类已经被创建,则直接返回已有的享元类:若没有,则创建一个新的享元对象,并将它加入到维护队列中。
二、代码实现 第一步:定义抽象享元类(Book)public abstract class Book { //借书的学生姓名 //内部状态 private String stuName; //外部状态 public String name;` public Book(String name){ this.name = name; } public abstract void borrow(); public String getStuName() { return stuName; } public void setStuName(String stuName) { this.stuName = stuName; } }第二步:定义具体享元类(ConcreteBook)
public class ConcreteBook extends Book { //被借出的书名 public ConcreteBook(String name){ super(name); } @Override public void borrow() { System.out.println(getStuName()+"借了一本书名为:"+this.name); } }第三步:享元工厂(Llibrary)
public class Library { //图书馆维护一个图书列表 private Map第四步:模拟学生去借书bookPools = new HashMap<>(); private static Library factory = new Library(); //图书馆只有一个 public static Library getInstance(){ return factory; } //图书馆外借图书 public Book libToBorrow(String bookName){ Book order = null; //如果书架有书,直接借出 if (bookPools.containsKey(bookName)){ order = bookPools.get(bookName); } //如果没有,那就拿本新书 else{ //根据外部状态创建享元对象 order = new ConcreteBook(bookName); //放到池中 bookPools.put(bookName,order); } return order; } //书架上书的数量 public int getAllBookSize(){ return bookPools.size(); } }
public class Student { //图书馆书架上的书 private static Listbooks = new ArrayList (); private static Library library; public static void main(String[] args) { library = Library.getInstance(); studentBorrow("java编程思想"); studentBorrow("java核心卷一"); studentBorrow("java从入门到精通"); //把每一本书借出去 for (Book book : books) { book.setStuName("张三"); book.borrow(); } System.out.println("后两本没学会,又借了一次 "); studentBorrow("java核心卷一"); studentBorrow("java从入门到精通"); Book java核心卷一 = library.libToBorrow("java核心卷一"); java核心卷一.setStuName("张三"); java核心卷一.borrow(); Book java从入门到精通 = library.libToBorrow("java从入门到精通"); java从入门到精通.borrow(); //王五去借书 studentBorrow("java核心卷一"); Book java核心卷一2 = library.libToBorrow("java核心卷一"); java核心卷一2.setStuName("王五"); java核心卷一2.borrow(); //输出一些学生一共借多少本书 System.out.println("学生一共借了 " + books.size() + " 本书! "); //输出一下图书馆一共借出多少本书 System.out.println("图书馆实际借出" + library.getAllBookSize() + " 本书"); } private static void studentBorrow(String bookName){ books.add(library.libToBorrow(bookName)); } }
在上面其实学生一共借了5次书,但是有两本是重复的,所以对于图书馆来说,其实是借出去了三本书。
输出结果:
图书馆借出一本书,书名为:java编程思想
图书馆借出一本书,书名为:java核心卷一
图书馆借出一本书,书名为:java从入门到精通
后两本没学会,又借了一次
图书馆借出一本书,书名为:java核心卷一
图书馆借出一本书,书名为:java从入门到精通
学生一共借了 5 本书!
图书馆实际借出3 本书
1.String中的intern()方法
public static void main(String[] args) { String str1 = "和谐"; String str2 = "社会"; String str3 = "和谐社会"; String str4; str4 = str1 + str2; System.out.println(str3 == str4); // 如果是String的对象池中有该类型的值,则直接返回对象池中的对象 str4 = (str1 + str2).intern(); System.out.println(str3 == str4); } 输出结果:false true
2.Integer中的vauleOf()方法
Integer中的缓存数据cache
Integer i1 = Integer.valueOf(99); Integer i2 = Integer.valueOf(99); System.out.println(i1==i2); Integer i3 = Integer.valueOf(128); Integer i4 = Integer.valueOf(128); System.out.println(i3==i4); 输出结果:true false四、享元模式特点 1、优点
(1)节省内存空间,对于可重复的对象只会被创建一次,对象比较多时,就会极大的节省空间。
(2)提高效率,由于创建对象的数量减少,所以对系统内存的需求也减小,使得速度更快,效率更高。
提高了系统复杂性,需要分离出外部状态(k)和内部状态(v),而且外部状态具有固化特性,不应该随内部状态改变而改变,否则导致系统的逻辑混乱,为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
● 内部状态
内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变,如我们例子中的stuName,它们可以作为一个对象的动态附加信息,不必直接储存在具体某个对象中,属于可以共享的部分。
● 外部状态
外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态,如我们例子中的书名name,它是一批对象的统一标识,是唯一的一个索引值。
(1)享元设计模式是一个类有很多对象,而单例是一个类仅一个对象。
(2)享元模式是为了节约内存空间,提升程序性能,而单例模式则主要是出于共享状态的目的。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)