一,什么是享元模式
Flyweight 模式也叫享元模式,是构造模式之一,它通过与其他类似对象共享数据来减少内存占用。
说到享元模式,第一个想到的应该就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,所以说享元模式是池技术的重要实现方式。
二,享元模式的结构
享元模式中存在以下两种状态:
- 内部状态,即不会随着环境的改变而改变的可共享部分;
- 外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。下面来分析其基本结构和实现方法。
抽象享元角色 Flyweight): 所有具体享元类的父类,规定一些需要实现的公共接口。
具体享元角色 (Concrete Flyweight): 抽象享元的具体实现类,并实现了抽象享元角色规定的方法。
非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中
享元工厂角色 (Flyweight Factory): 负责创建和管理享元角色。
优点:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
缺点:
- 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
- 读取享元模式的外部状态会使得运行时间稍微变长。
四,简化代码实现:
4.1 抽象享元角色: 所有具体享元类的父类,规定一些需要实现的公共接口。
/**
* @ClassName Persion
* @Description TODO
* @Version 1.0
**/
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
/**
* @ClassName Flyweight
* @Description TODO
* @Version 1.0
**/
public interface Flyweight{
public void operation(UnsharedConcreteFlyweight state);
}
4.2 具体享元角色: 抽象享元的具体实现类,并实现了抽象享元角色规定的方法。
/**
* @ClassName Teacher
* @Description TODO
* @Version 1.0
**/
public class Teacher extends Person{
private String number;
public Teacher(String number) {
this.number = number;
}
public Teacher(String name, int age , String number) {
super(name, age);
this.number = number;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
/**
* @ClassName ConcreteFlyweight
* @Description TODO
* @Version 1.0
**/
public ConcreteFlyweight implements Flyweight {
private String key;
public ConcreteFlyweight(String key) {
this.key=key;
System.out.println("具体享元"+key+"被创建!");
}
public void operation(UnsharedConcreteFlyweight outState) {
System.out.print("具体享元"+key+"被调用,");
System.out.println("非享元信息是:"+outState.getInfo());
}
}
4.3 非享元角色
/**
* @ClassName UnsharedConcreteFlyweight
* @Description 非享元角色
* @Version 1.0
**/
public class UnsharedConcreteFlyweight {
private String info;
public UnsharedConcreteFlyweight(String info) {
this.info=info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info=info;
}
}
4.4 享元工厂角色: 负责创建和管理享元角色。
public class TeacherFactory {
Map<String , Teacher> pool ;
public TeacherFactory(Map<String, Teacher> pool) {
this.pool = pool;
}
public Teacher getTeacher(String number){
Teacher teacher = pool.get(number);
if(teacher == null ){
teacher = new Teacher("1");
pool.put(number,teacher);
}
return teacher;
}
}
/**
* @ClassName FlyweightFactory
* @Description TODO
* @Version 1.0
**/
public class FlyweightFactory{
private HashMap<String, Flyweight> flyweights=new HashMap<String, Flyweight>();
public Flyweight getFlyweight(String key) {
Flyweight flyweight=(Flyweight)flyweights.get(key);
if(flyweight!=null) {
System.out.println("具体享元"+key+"已经存在,被成功获取!");
}else {
flyweight=new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
}
return flyweight;
}
}
测试:
public class Test {
public static void main(String[] args) {
Map<String , Teacher> pool =new HashMap<String, Teacher> ();
TeacherFactory teacherFactory = new TeacherFactory(pool);
Teacher teacher1 = teacherFactory.getTeacher("1");
Teacher teacher2 = teacherFactory.getTeacher("2");
Teacher teacher3 = teacherFactory.getTeacher("1");
Teacher teacher4 = teacherFactory.getTeacher("2");
if(teacher1 == teacher3){
System.out.println( "teacher1 和 teacher3 是同一个对象");
}
if(teacher2 == teacher4){
System.out.println( "teacher2 和 teacher4 是同一个对象");
}
}
}
/**
* @ClassName FlyweightPattern
* @Description 测试
* @Version 1.0
**/
public class FlyweightPattern {
public static void main(String[] args) {
FlyweightFactory factory=new FlyweightFactory();
Flyweight f01=factory.getFlyweight("a");
Flyweight f02=factory.getFlyweight("a");
Flyweight f03=factory.getFlyweight("a");
Flyweight f11=factory.getFlyweight("b");
Flyweight f12=factory.getFlyweight("b");
f01.operation(new UnsharedConcreteFlyweight("第1次调用a。"));
f02.operation(new UnsharedConcreteFlyweight("第2次调用a。"));
f03.operation(new UnsharedConcreteFlyweight("第3次调用a。"));
f11.operation(new UnsharedConcreteFlyweight("第1次调用b。"));
f12.operation(new UnsharedConcreteFlyweight("第2次调用b。"));
}
}
五:享元模式的应用实例
【例1】享元模式在五子棋游戏中的应用。
分析:五子棋同围棋一样,包含多个“黑”或“白”颜色的棋子,所以用享元模式比较好。
本实例中的棋子
(ChessPieces)类是抽象享元角色,它包含了一个落子的 DownPieces(Graphics g,Point pt) 方法;
白子(WhitePieces)和黑子(BlackPieces)类是具体享元角色,它实现了落子方法;
Point 是非享元角色,它指定了落子的位置;
WeiqiFactory 是享元工厂角色,它通过 ArrayList 来管理棋子,并且提供了获取白子或者黑子的 getChessPieces(String type) 方法;客户类(Chessboard)利用 Graphics 组件在框架窗体中绘制一个棋盘,并实现 mouseClicked(MouseEvent e) 事件处理方法,该方法根据用户的选择从享元工厂中获取白子或者黑子并落在棋盘上。图 2 所示是其结构图。
抽象享元角色:棋子
/**
* @ClassName BlackPieces
* @Description 抽象享元角色:棋子
* @Version 1.0
**/
public interface ChessPieces {
/**
* @Description: 下子
* @param: [g, pt]
* @return: void
* @Author: wuchao
* @Date: 2020/6/18 23:26
**/
public void DownPieces(Graphics g, Point pt);
}
具体享元角色:黑子
/**
* @ClassName BlackPieces
* @Description 具体享元角色:黑子
* @Version 1.0
**/
public class BlackPieces implements ChessPieces {
@Override
public void DownPieces(Graphics g, Point pt) {
g.setColor(Color.BLACK);
g.fillOval(pt.x,pt.y,30,30);
}
}
具体享元角色:白子
/**
* @ClassName WhitePieces
* @Description 具体享元角色:白子
* @Version 1.0
**/
public class WhitePieces implements ChessPieces {
@Override
public void DownPieces(Graphics g, Point pt) {
g.setColor(Color.WHITE);
g.fillOval(pt.x,pt.y,30,30);
}
}
棋盘:
/**
* @ClassName Chessboard
* @Description 棋盘
* @Version 1.0
**/
public class Chessboard extends MouseAdapter {
WeiqiFactory wf;
JFrame f;
Graphics g;
JRadioButton wz;
JRadioButton bz;
private final int x=50;
private final int y=50;
private final int w=40; //小方格宽度和高度
private final int rw=400; //棋盘宽度和高度
public Chessboard(){
wf=new WeiqiFactory();
f=new JFrame("享元模式在五子棋游戏中的应用");
f.setBounds(100,100,500,550);
f.setVisible(true);
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel SouthJP=new JPanel();
f.add("South",SouthJP);
wz=new JRadioButton("白子");
bz=new JRadioButton("黑子",true);
ButtonGroup group=new ButtonGroup();
group.add(wz);
group.add(bz);
SouthJP.add(wz);
SouthJP.add(bz);
JPanel CenterJP=new JPanel();
CenterJP.setLayout(null);
CenterJP.setSize(500, 500);
CenterJP.addMouseListener(this);
f.add("Center",CenterJP);
try
{
Thread.sleep(500);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
g=CenterJP.getGraphics();
g.setColor(Color.BLUE);
g.drawRect(x, y, rw, rw);
for(int i=1;i<10;i++)
{
//绘制第i条竖直线
g.drawLine(x+(i*w),y,x+(i*w),y+rw);
//绘制第i条水平线
g.drawLine(x,y+(i*w),x+rw,y+(i*w));
}
};
@Override
public void mouseClicked(MouseEvent e)
{
Point pt=new Point(e.getX()-15,e.getY()-15);
if(wz.isSelected())
{
ChessPieces c1=wf.getChessPieces("w");
c1.DownPieces(g,pt);
}
else if(bz.isSelected())
{
ChessPieces c2=wf.getChessPieces("b");
c2.DownPieces(g,pt);
}
}
}
享元工厂角色
/**
* @ClassName WeiqiFactory
* @Description 享元工厂角色
* @Version 1.0
**/
public class WeiqiFactory {
private ArrayList<ChessPieces> qz;
public WeiqiFactory() {
qz=new ArrayList<ChessPieces>();
ChessPieces w=new WhitePieces();
qz.add(w);
ChessPieces b=new BlackPieces();
qz.add(b);
}
public ChessPieces getChessPieces(String type) {
if(type.equalsIgnoreCase("w")) {
return (ChessPieces)qz.get(0);
} else if(type.equalsIgnoreCase("b")) {
return (ChessPieces)qz.get(1);
} else {
return null;
}
}
}
测试:
public class WzqGame {
public static void main(String[] args) {
new Chessboard();
}
}
六:享元模式的扩展
其结构图通常包含可以共享的部分和不可以共享的部分。在实际使用过程中,有时候会稍加改变,即存在两种特殊的享元模式:单纯享元模式和复合享元模式,下面分别对它们进行简单介绍。
1.单纯享元模式,这种享元模式中的所有的具体享元类都是可以共享的,不存在非共享的具体享元类,
2.复合享元模式,这种享元模式中的有些享元对象是由一些单纯享元对象组合而成的,它们就是复合享元对象。虽然复合享元对象本身不能共享,但它们可以分解成单纯享元对象再被共享。