一.模式定义
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。
Flyweight Pattern: Use sharing to support large numbers of fine-grained objects efficiently.
二.模式要素
Flyweight: 抽象享元类
ConcreteFlyweight: 具体享元类
UnsharedConcreteFlyweight: 非共享具体享元类
FlyweightFactory: 享元工厂类
三.举例说明
享元模式的本质是为了实现大量细粒度对象的复用。
要想实现这样的功能就需要一个享元池,如果该元素已经在池子中有了直接拿出来用,不需要新建。如果元素在池中没有才需要新建。
假设有工厂代工手机生产,代工完出厂的手机都有其对应的价格,需要对外展示各种品牌手机的出厂价格。假设该工厂可以代工华为手机,也可以代工苹果手机。如果一旦代工了某种品牌的手机,该工厂的享元池就增加了一个元素,查询该品牌的手机信息就直接从池子里拿。如果该工厂从来没代工过某手机,就在享元池里新建一个元素,之后可以获取相关的信息。这样的功能借助享元模式的思路是很好实现的。
四.具体代码
IProduct.java 产品接口
package flyweightPattern;
/**
* 描述:
* 产品接口
*
* @author Lei Dong
* @create 2019-04-29 10:14
*/
public abstract class IProduct {
public String name;
public IProduct(String name) {
this.name = name;
}
abstract int obtainPrice();
abstract String showName();
}
HuaWeiMobile.java 华为手机
package flyweightPattern;
/**
* 描述:
* 华为手机
*
* @author Lei Dong
* @create 2019-04-29 10:15
*/
public class HuaWeiMobile extends IProduct {
public HuaWeiMobile() {
super("huawei");
}
@Override
public int obtainPrice() {
return 5000;
}
@Override
String showName() {
return name;
}
}
AppleMobile.java 苹果手机
package flyweightPattern;
/**
* 描述:
* 苹果手机
*
* @author Lei Dong
* @create 2019-04-29 13:59
*/
public class AppleMobile extends IProduct {
public AppleMobile() {
super("apple");
}
@Override
int obtainPrice() {
return 8000;
}
@Override
String showName() {
return name;
}
}
ProductPool.java 产品池
package flyweightPattern;
import java.util.HashMap;
/**
* 描述:
* 产品池
*
* @author Lei Dong
* @create 2019-04-29 10:22
*/
public class ProductPool {
private static HashMap<String, IProduct> productPool = new HashMap<>();
public static IProduct obtainProducts(String name) {
if (productPool.containsKey(name)) {
return productPool.get(name);
} else {
IProduct product = null;
if (name.equalsIgnoreCase("huawei")) {
product = new HuaWeiMobile();
}
if (name.equalsIgnoreCase("apple")) {
product = new AppleMobile();
}
productPool.put(name, product);
return productPool.get(name);
}
}
public static int obtainSize() {
return productPool.size();
}
}
Main.java
package flyweightPattern;
/**
* 描述:
* Main 享元模式
*
* @author Lei Dong
* @create 2019-04-29 10:14
*/
public class Main {
public static void main(String[] args) {
System.out.println();
IProduct products1 = ProductPool.obtainProducts("huawei");
System.out.println(products1.showName() + " : " + products1.obtainPrice());
System.out.println("Pool size:" + ProductPool.obtainSize());
IProduct products2 = ProductPool.obtainProducts("apple");
System.out.println(products2.showName() + " : " + products2.obtainPrice());
System.out.println("Pool size:" + ProductPool.obtainSize());
IProduct products3 = ProductPool.obtainProducts("huawei");
System.out.println(products3.showName() + " : " + products3.obtainPrice());
System.out.println("Pool size:" + ProductPool.obtainSize());
}
}
运行结果:
可以看到之前塞进产品池华为手机,之后再次调用华为手机的属性时并没有新建元素,而是复用了已有的元素。
五.总结
1.享元模式的优点
(1)享元模式的优点在于它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。
(2)享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。
2.享元模式的缺点
(1)享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
(2)为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。
3.模式适用场景
在以下情况下可以使用享元模式:
(1)一个系统有大量相同或者相似的对象,由于这类对象的大量使用,造成内存的大量耗费。
(2)对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
(3)使用享元模式需要维护一个存储享元对象的享元池,而这需要耗费资源,因此,应当在多次重复使用享元对象时才值得使用享元模式。