一、基础简介
1、定义
运用共享技术有效地支持大量细颗粒度的对象。主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式。
2、使用场景
- 1、系统中有大量对象。
- 2、这些对象消耗大量内存。
- 3、这些对象的状态大部分可以外部化。
- 4、这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。
- 5、系统不依赖于这些对象身份,这些对象是不可分辨的。
3、优缺点
优点:
- 可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以节约系统资源,提高系统性能。
- 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享
缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
4、模式结构分析
- Flyweight(抽象享元类):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
- ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
- UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
- FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在的话),返回新创建的实例并将其存储在享元池中。
二、实例实现
1、实例场景
比如“五子棋”,有白色和黑色的棋子:
- 棋子:就是一个 Flyweight(抽象享元类)
- 棋子的颜色:就属于“内部状态”
- 棋子的位置:就属于“外部状态”
- 白棋和黑棋,就是ConcreteFlyweight(具体享元类)
- 而坏了的棋子,就是UnsharedConcreteFlyweight(非共享具体享元类)
- 五子棋加工厂,就是 FlyweightFactory(享元工厂类)
2、“五子棋”:Flyweight(抽象享元类)
package com.mfc.design.享元模式.实例;
/**
* @author MouFangCai
* @date 2019/10/25 14:29
*
* @description 五子棋棋子类:Flyweight(抽象享元类)
*/
public abstract class Chessman_Flyweight {
public abstract void display();
// 通过该接口,Flyweight可以接受并作用于外部状态
public abstract void display(Coord_Extrinsic extrinsic);
}
3、外部状态类
package com.mfc.design.享元模式.实例;
import lombok.Data;
/**
* @author MouFangCai
* @date 2019/10/25 14:33
* @description 棋子的外部属性:坐标类
*/
@Data
public class Coord_Extrinsic {
private int x;
private int y;
public Coord_Extrinsic(int x, int y) {
this.x = x;
this.y = y;
}
}
4、白棋 / 黑棋:ConcreteFlyweight(具体享元类)
package com.mfc.design.享元模式.实例;
/**
* @author MouFangCai
* @date 2019/10/25 14:48
*
* @description 具体的棋子:ConcreteFlyweight(具体享元类):它实现了抽象享元类,其实例称为享元对象
*/
public class Chessman_ConcreteFlyweight extends Chessman_Flyweight{
// 为内部状态增加存储空间
// 棋子的内部属性:颜色
private String color_intrinsic;
// 要求享元角色 必须接受内部状态
public Chessman_ConcreteFlyweight(String color_intrinsic) {
this.color_intrinsic = color_intrinsic;
}
@Override
public void display() {
System.out.println("棋子颜色:" + color_intrinsic + " 在棋盒里");
}
@Override
public void display(Coord_Extrinsic extrinsic) {
System.out.println("棋子颜色:" + color_intrinsic +
" 位置:(" + extrinsic.getX() + "," + extrinsic.getY() + ")");
}
}
5、坏了的棋子:UnsharedConcreteFlyweight(非共享具体享元类)
package com.mfc.design.享元模式.实例;
/**
* @author MouFangCai
* @date 2019/10/25 14:58
* @description UnsharedConcreteFlyweight(非共享具体享元类)
* 坏了的棋子就不管他的颜色了
*/
public class UnsharedConcreteFlyweight extends Chessman_Flyweight {
@Override
public void display() {
System.out.println("这个棋子已坏");
}
@Override
public void display(Coord_Extrinsic extrinsic) {
System.out.println("这个棋子已坏");
}
}
6、五子棋加工厂: FlyweightFactory(享元工厂类)
package com.mfc.design.享元模式.实例;
import java.util.HashMap;
/**
* @author MouFangCai
* @date 2019/10/25 15:01
* @description FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象
*/
public class Chessman_Factory {
// 使用HashMap存储享元对象,充当享元池
private static HashMap<String,Chessman_Flyweight> chessmanMaps;
// 使用单例模式,保证Chessman_Factory 类只有一个实例
private static class FactoryClass {
private static final Chessman_Factory chessman = new Chessman_Factory();
}
// 初始化常用的棋子
private Chessman_Factory() {
chessmanMaps = new HashMap<>();
Chessman_Flyweight white = new Chessman_ConcreteFlyweight("白色");
chessmanMaps.put("w",white);
Chessman_Flyweight black = new Chessman_ConcreteFlyweight("黑色");
chessmanMaps.put("b",black);
Chessman_Flyweight unshared = new UnsharedConcreteFlyweight();
chessmanMaps.put("no",unshared);
}
// 得到 享元工厂类的唯一实例
public static Chessman_Factory getFactory() {
return FactoryClass.chessman;
}
public Chessman_Flyweight getFlyweight(String color){
return chessmanMaps.get(color);
}
// 实例数
public int getCount(){
return chessmanMaps.size();
}
}
7、客户端
package com.mfc.design.享元模式.实例;
/**
* @author MouFangCai
* @date 2019/10/25 15:01
* @description
*/
public class Client_Flyweight {
public static void main(String[] args) {
// 获取享元工厂对象
Chessman_Factory factory = Chessman_Factory.getFactory();
// 获取2枚黑棋、2枚白棋、2枚坏了的棋子
Chessman_Flyweight black1 = factory.getFlyweight("b");
Chessman_Flyweight black2 = factory.getFlyweight("b");
System.out.println("判断黑棋是否相同:" + (black1 == black2));
Chessman_Flyweight white1 = factory.getFlyweight("w");
Chessman_Flyweight white2 = factory.getFlyweight("w");
System.out.println("判断白棋是否相同:" + (white1 == white2));
Chessman_Flyweight bad1 = factory.getFlyweight("no");
Chessman_Flyweight bad2 = factory.getFlyweight("no");
System.out.println("判断坏了的棋子是否相同:" + (bad1 == bad2));
System.out.println();
System.out.println("棋子的实例数:" + factory.getCount());
System.out.println();
// 显示棋子
black1.display();
white1.display();
bad1.display();
// 显示棋子,同时设置坐标位置
System.out.println();
black2.display(new Coord_Extrinsic(1,2));
white2.display(new Coord_Extrinsic(2,3));
bad2.display(new Coord_Extrinsic(1,2));
}
}
8、结果展示
判断黑棋是否相同:true
判断白棋是否相同:true
判断坏了的棋子是否相同:true棋子的实例数:3
棋子颜色:黑色 在棋盒里
棋子颜色:白色 在棋盒里
这个棋子已坏棋子颜色:黑色 位置:(1,2)
棋子颜色:白色 位置:(2,3)
这个棋子已坏Process finished with exit code 0
我们可以看到,棋子的实例数为3,但我们已有6颗棋子了。减少了创建对象的数量