• 设计模式之享元模式的实现


    (一)、享元模式的简单介绍

    享元模式:类似于池技术,实现对象的复用。

    好处:

    1)、减少内存的使用 ,避免出现大量重复的创建销毁对象的场景

    2)、外部状态相对独立,而且不会影响其内部状态 ,元对象可以在不同的环境中 被共享

    享元模式的实现:把一个对象的状态分成内部状态和外部状态 ,内部状态即是不变的 ,外部状态是变化的 ,通过共享不变的部分 ,达到减少对象数量并节约内存 的目的。

    享元模式应用:

    当系统中多处需要同一组信息时 ,把这些信息封装到一个对象中 ,然后对该对象进行缓存 。

    享元模式 同样要求创建一个或一组对象,通过工厂方法生成对象的 。

    享元模式的三种角色

    1)、抽象享元模式(flyweight)

    享元对象抽象基类或者接口,  同时定义出对象的外部状态和内部状态的接口或
    
    实现。
    

    2)、具体享元模式(concreteflyweight)

    实现抽象角色定义的业务 ,角色的内部状态处理应该与环境无关 
    
        不能出现会有一个操作改变内部状态,同时修改了外部状态。 
    

    3)、享元工厂(flyweightfctory)

    负责管理享元对象池和创建享元对象  
    

    (二)、实现享元模式的步骤

    1、享元抽象类

    2、享元具体类

    3、享元工厂类

    4、使用类

    (三)、利用享元模式模拟围棋游戏

    /**
    围棋游戏
     * --: 围棋分白子和黑子,用户在玩围棋时会用到多个黑子和白子
     * 模拟下棋操作
     *  --:此时,出现了一个问题:
     *      每当用户出一个棋子就会重行创建一个棋对象,如果很多用户在下棋,那么,服务器就会有很对棋对象,占用服务器的内存
     *
     *  --:发现规律,用户操作棋子,棋子的颜色(黑色和白色)不会变化,变棋子的位置发生化。
     *      解决问题:
     *      所有的用户使用同一个棋子对象
     */
    

    享元抽象类:

    /**
     * 围棋
     *  --: 棋子的属性
     *       颜色:黑色/白色,是不变的。
     *       坐标:x,y ,变化的。
     */
    public abstract class Chess {
        //棋子的颜色为内部状态
        private String color;
        /**
         * 享元模式,将可变的东西转为外部状态。
         */
        //private int x;
        //private int y;
    
        public abstract String getColor();
    
        //Coordinates coord 为外部状态,通过外部注入的方式生成
        public void display(Coordinates coord){
            System.out.println("棋子的颜色:"+ this.getColor() +"-->棋子所在的位置"+coord.getX()+","+coord.getY());
        }
    }
    

    享元具体类:

    public class BlackChess extends Chess{
    
        @Override
        public String getColor() {
            return "黑色";
        }
    }
    
    public class WhiteChess extends Chess{
        @Override
        public String getColor() {
            return "白色";
        }
    }
    

    外部状态类:

    /**
     * 围棋类的外部状态类
     */
    public class Coordinates {
        private int x;
        private int y;
    
        public int getX() {
            return x;
        }
    
        public void setX(int x) {
            this.x = x;
        }
    
        public int getY() {
            return y;
        }
    
        public void setY(int y) {
            this.y = y;
        }
    
        public Coordinates(int x, int y) {
            this.x = x;
            this.y = y;
        }
    
        public Coordinates() {
        }
    }
    

    享元工厂类:

    /**
     * 创建棋子的工厂,工厂生产的产品不重复
     *   ---:1. 通过工厂,可以创建一批同类对象。
     *        2. 通过过池(集合),可以复用对象。
     */
    public class ChessFactory {
        /**
         * 将棋子存放在一个集合中,保证生产的产品不重复
         */
        private static Map<String, Chess> pool = new HashMap<>();
    
        /**
         *  接单时先根据产品标识,判断产品是否重复
         */
        public static Chess getChess(String color){
            Chess chess = null;
            if(pool.containsKey(color)){
                return pool.get(color);
            }
            //如果池中没有所要对象则重新创建对象,并放入池中
            if(color == "b"){
                chess = new BlackChess();
                pool.put(color, chess);
                return chess;
            }
            if(color == "w"){
                chess = new WhiteChess();
                pool.put(color, chess);
                return chess;
            }
            return chess;
        }
    }
    

    使用类:

    /**
     * 通过工厂来创建棋子
     *
     */
    public class Client2 {
        public static void main(String[] args) {
            //通过工厂创建一个黑棋对象
            Chess blackChess1 = ChessFactory.getChess("b");
            blackChess1.display(new Coordinates(1,3));
            //从池中取出黑棋对象
            Chess blackChess2 = ChessFactory.getChess("b");
            blackChess2.display(new Coordinates(3,6));
            System.out.println(blackChess1 == blackChess2);
    
            Chess whiteChess1 = ChessFactory.getChess("w");
            whiteChess1.display(new Coordinates(1,1));
            Chess whiteChess2 = ChessFactory.getChess("w");
            whiteChess2.display(new Coordinates(2, 2));
            System.out.println(whiteChess1 == whiteChess2);
    
        }
    }
    

    结果:

    棋子的颜色:黑色-->棋子所在的位置1,3
    棋子的颜色:黑色-->棋子所在的位置3,6
    true
    棋子的颜色:白色-->棋子所在的位置1,1
    棋子的颜色:白色-->棋子所在的位置2,2
    true
    

    (四)、利用享元模式模拟人事管理系统查询

    需求:
    *  --: 一个SAAS(software AS A Service)软件应用模式的人事管理系   统,该系统提供给甲、乙、丙三家公司使用
    *       1.甲、乙、丙各有100名员工,员工可以登录系统,查询自己的工资报表。
    *       2.甲、乙、丙公司各有自己对应的数据库,可以为甲、乙、丙提供对应的查询接口。
    		3.创建各公司对应的查询接口实例,公司的员工查询工资时使用同一个实例。
    

    享元抽象类:

    /**
     *  抽象享元类
     *
     *  需求:
     *  --: 一个SAAS(software AS A Service)软件应用模式的人事管理系统,该系统提供给甲、乙、丙三家公司使用
     *       1.甲、乙、丙各有100名员工,员工可以登录系统,查询自己的工资报表。
     *       2.甲、乙、丙公司各有自己对应的数据库,可以为甲、乙、丙提供对应的查询接口。
     *
     */
    
    public interface IReportManager {
        /**
         * 该接口的作用是创建报表
         */
        public String  createReport(String employeeName);
    }
    

    享元具体类:

    /**
     * 创建员工工资报表
     */
    public class FiniancialReportManager implements IReportManager{
    
        /**
         *  tenantId : 享元对象的内部状态, 使用该系统的公司名称
         */
        private String tenantId = null;
    
        FiniancialReportManager(String tenantId){
            this.tenantId = tenantId;
        }
    
        /**
         * employeeName: 享元模式的外部状态
         */
        @Override
        public String createReport(String employeeName) {
            return "create FiniancialReport:"+employeeName;
        }
    }
    
    /**
     * 享元具体类
     */
    public class EemployeeReportManager implements IReportManager{
        private String tenantId = null;
        EemployeeReportManager(String tenantId){
            this.tenantId = tenantId;
        }
    
        @Override
        public String createReport(String employeeName) {
            return "create EemployeeReport:" + employeeName;
        }
    }
    

    享元工厂类:

    public class ReportManangerFactory {
        //存放查询工资报表的集合
        Map<String, IReportManager> financialMap = new HashMap<>();
        //存放查询员工信息报表的集合
        Map<String, IReportManager> employeeMap = new HashMap<>();
    
        /**
         * 获取工资报表接口对象
         * @return
         */
        public IReportManager getFinancialReport(String tenantId){
            if(financialMap.containsKey(tenantId)){
                return financialMap.get(tenantId);
            }else{
                FiniancialReportManager finiancialReportManager = new FiniancialReportManager(tenantId);
                financialMap.put(tenantId, finiancialReportManager);
                return finiancialReportManager;
            }
        }
    
        /**
         * 获取员工信息列表对象
         * @param tenantId
         * @return
         */
        public IReportManager getEmployeeReport(String tenantId){
            if(employeeMap.containsKey(tenantId)){
                return employeeMap.get(tenantId);
            }else{
                EemployeeReportManager employeeReportManager = new EemployeeReportManager(tenantId);
                employeeMap.put(tenantId, employeeReportManager);
                return employeeReportManager;
            }
        }
    }
    

    使用类:

    public class Client {
        public static void main(String[] args) {
            //创建享元对象工厂
            ReportManangerFactory reportManangerFactory = new ReportManangerFactory();
            //A公司员工甲查询工资,创建A公司的报表查询接口
            IReportManager reportManagerA = reportManangerFactory.getFinancialReport("A");
            //使用查询报表功能
            String result = reportManagerA.createReport("李雷");
            System.out.println(result);
    
            //A公司的员工乙查询工资,也创建A公司的报表查询接口
            IReportManager reportManagerReA = reportManangerFactory.getFinancialReport("A");
            //判断两个查询接口是否是同一个对象
            System.out.println(reportManagerA == reportManagerReA);
            String result1 = reportManagerReA.createReport("migng");
            System.out.println(result1);
        }
    }
    

    结果:

    create FiniancialReport:李雷
    true
    create FiniancialReport:migng
    金麟岂能忍一世平凡 飞上了青天 天下还依然
  • 相关阅读:
    MySQL锁(阻塞)
    MySQL锁类型(一致性是非锁定读、自增和外键)
    MySQL锁算法(行锁的三种算法以及解决幻读问题)
    MySQL锁概述
    MySQL锁问题(脏读、不可重复读、幻读)
    MySQL默认隔离级别对应解决的三种问题
    简单动态字符串
    限流
    # SpringBoot自定义线程池
    & 生产环境mysql问题记录
  • 原文地址:https://www.cnblogs.com/Auge/p/11580221.html
Copyright © 2020-2023  润新知