设计模式-7-享元模式

68 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第九天,点击查看活动详情

PS:已经更文多少天,N就写几。一定要写对文案,否则文章不计入在内;模板句子需要带超链接。

享元模式(Flyweight Pattern)就是搞一个缓存池,找对象先在池中找,如果未找到匹配的对象,再创建新对象。通过重用现有的同类对象减少创建对象的数量,以减少内存占用和提高性能。也是属于结构型模式。

        专业点说,享元模式就是运用共享技术有效地支持大量细粒度的对象。

        在Java的JDK中,提供了一个字符串缓存池,我们在new一个字符串对象的时候,程序会自动去池中查找,如果有就返回,没有就再去new,然后放入缓存池中。还有线程池,也是运用享元模式的典型案例。

代码实现一下:

一、五子棋下棋操作代码:

1.新建一个接口,用来进行对象的共有操作

package com.xing.design.flyweight.chess;
/** *  共享对象通用的接口 黑棋和白棋都通过这个方法画到棋盘上 * @author xing */public interface Chess {//下棋操作void draw(int x,int y);}

2.分别实现不同的子类(黑棋和白棋)

package com.xing.design.flyweight.chess;
/** * 黑棋 * @author xing */public class BlackChess implements Chess {// 共性 圆形    private final String sharp = "圆形";public BlackChess() {      System.out.println("new一个新黑棋对象");    }@Overridepublic void draw(int x, int y) {    System.out.println("黑色"+sharp+"棋子置于"+x+","+y+"处");  }}
package com.xing.design.flyweight.chess;
public class WhiteChess implements Chess {// 内部状态 圆形private final String sharp = "圆形";public WhiteChess() {      System.out.println("new一个新白棋对象");    }@Overridepublic void draw(int x, int y) {    System.out.println("白色"+sharp+"棋子置于"+x+","+y+"处");  }}

        注意这里的圆形,这个是每个对象都有的内部状态。

3.上面对象有了,再建立一个工厂来生成对象

package com.xing.design.flyweight.chess;
import java.util.ArrayList;import java.util.List;
/** * 客户端 * @author xing */public class FlyweightClient {
public static void main(String[] args) {// 棋盘        List<Chess> chessList = new ArrayList<>();
//下黑子        Chess backChess1 = ChessFactory.getChess("黑色");        backChess1.draw(2, 5);        chessList.add(backChess1);
//下白子        Chess whiteChess = ChessFactory.getChess("白色");        whiteChess.draw(3, 5);        chessList.add(whiteChess);
//下黑子        Chess backChess2 = ChessFactory.getChess("黑色");        backChess2.draw(2, 6);        chessList.add(backChess2);
//下白子        Chess whiteChess2 = ChessFactory.getChess("白色");        backChess2.draw(3, 8);        chessList.add(whiteChess2);        System.out.println(String.format("backChess1:%d | backChess2:%d | whiteChess:%d  | whiteChess2:%d",            backChess1.hashCode(), backChess2.hashCode(), whiteChess.hashCode(), whiteChess2.hashCode()));               System.out.println("生成的持久化对象个数->"+chessList.size());    }}

结果:

图片

        通过对比两个对象的hashCode,我们发现确实是同一个对象,下了四个棋子,只new了两个对象哈!

 再来一个应用场景:

二、缓存一个连接池

        换一个应用场景,我们要用享元模式来搞一个缓存池,我们要怎么来实现呢?

1.连接对象抽象一个接口,方便以后增加连接类型

package com.xing.design.flyweight.pool;
/** *  线程对象公共操作 * @author xing * @createTime */public interface Connect {/**   * 连接   */public void connect();}

2.两个实现:

mysql

package com.xing.design.flyweight.pool;
/** *  Oracle连接对象 * @author xing */public class ConnectOracle implements Connect{/**连接地址 */private String url;/**用户名 */private String userName;/**密码 */private String passWord;public String getUrl() {return url;  }public void setUrl(String url) {this.url = url;  }public String getUserName() {return userName;  }public void setUserName(String userName) {this.userName = userName;  }public String getPassWord() {return passWord;  }public void setPassWord(String passWord) {this.passWord = passWord;  }public ConnectOracle() {    System.out.println("new了一个Oracle连接对象");  }  @Overridepublic void connect() {    System.out.println("Oracle连接成功,连接信息:【地址:"+url+"|用户名:"+userName+"|密码:"+passWord+"】");  }}

3.搞个工厂,核心代码

package com.xing.design.flyweight.pool;
import java.util.HashMap;import java.util.Map;
/** *  工厂生成连接对象 * @author xing */public class ConnectFacatory {
/**   * 连接池   */private static final Map<String, Connect> chessMap = new HashMap<>();
public static Connect getConnect(String type) {    Connect connect = chessMap.get(type);if(connect != null) {      System.out.println("池中已有"+type+"对象,直接使用");return connect;    }switch (type) {case "mysql":        connect = new ConnectMysql();        chessMap.put(type, connect);break;case "oracle":        connect = new ConnectOracle();        chessMap.put(type, connect);break;default:        System.out.println("暂只支持mysql、oracle连接"+type+"是不可用的连接类型");        connect = null;    }return connect;  }}

4.demo使用验证

package com.xing.design.flyweight.pool;
public class ConnectDemo {
public static void main(String[] args) {    System.out.println("--------------------------------mysql--------------------------------");    ConnectMysql mysql1 = (ConnectMysql) ConnectFacatory.getConnect("mysql");    mysql1.setUrl("http//127.0.0.1/xing");    mysql1.connect();    ConnectMysql mysql2 = (ConnectMysql) ConnectFacatory.getConnect("mysql");    mysql2.setUrl("http//127.0.0.2/hua");    mysql2.connect();if(mysql1.hashCode() == mysql2.hashCode()) {      System.out.println("mysql1->hash码【"+mysql1.hashCode()+"】和mysql2->hash码【"+mysql1.hashCode()+"】是同一个对象");    }    System.out.println("--------------------------------oracle--------------------------------");    ConnectOracle oracle1 = (ConnectOracle) ConnectFacatory.getConnect("oracle");    oracle1.setUrl("http//127.0.0.1/ora");    oracle1.setUserName("root");    oracle1.setPassWord("123");    oracle1.connect();    ConnectOracle oracle2 = (ConnectOracle) ConnectFacatory.getConnect("oracle");    oracle2.setUrl("http//127.0.0.1/ora");    oracle2.setUserName("root");    oracle2.setPassWord("root");    oracle2.connect();    ConnectOracle oracle3 = (ConnectOracle) ConnectFacatory.getConnect("oracle");    oracle3.setUrl("http//127.0.0.1/jing");    oracle3.setUserName("cang");    oracle3.setPassWord("123");    oracle3.connect();    System.out.println("oracle1的hashCode:"+oracle1.hashCode());    System.out.println("oracle2的hashCode:"+oracle2.hashCode());    System.out.println("oracle3的hashCode:"+oracle3.hashCode());    System.out.println("--------------------------------mongoDB--------------------------------");    Connect mongoDB = ConnectFacatory.getConnect("mongoDB");if(mongoDB == null) {      System.out.println("呐,mongoDB不支持");    }  }  }

结果:

图片

OK!

总结:

        享元模式就是将用过的对象用HashMap作为缓存池存储,下次用的时候用唯一的外部状态(type)去缓存池取,取不到再重新new,用这样的模式来大大减少相同对象的初始化,减少内存开支,缺点是提高了系统复杂度,核心代码是必须要有一个工厂类来控制对象的创建。

END

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第九天,点击查看活动详情

PS:已经更文多少天,N就写几。一定要写对文案,否则文章不计入在内;模板句子需要带超链接。