Flyweight design pattern coding

我们来coding享元模式,享元模式的重点就是共享,网站需要各个管理者上传报告,如果这些报告已经生成过了,

我们就没有必要再去new一个了,我们来通过这场景来体现一下享元模式,这里面我们也会结合工厂模式,首先我们

创建一个包,结构型,这里创建一个享元的包,flyweight,flyweight英文的愿意呢,是指轻量级的,如果用英英字典来解析的话,

最轻量级的类,那在中文翻译成享元模式,还是非常贴切的,那我们首先创建一个员工的接口

首先看这三个类,下面的应用层先不看,这个关系还是非常简单的,重点在这个工厂中,EMPLOYEE_MAP,

然后他两是组合关系,EmployeeFactory的getManager的返回值,是Employee,然后他来创建对应的Manager,

Manager又实现Employee,那整体来说,享元模式理解起来也非常容易,那我们也强调了,在使用享元模式的时候,

我们一定要关注线程安全问题,例如说现在这个Test,那在getManager的时候,这里面使用的就是HashMap,并不是线程

安全的,而且我们这里面也没有加同步锁,那对于作报告这种简单的场景,如果我们多线程过来的时候,其实并没有必要

关注他的线程安全问题,我们说的线程安全问题,其实也是要享元模式一定要保证他的线程安全,这个还是要根据实际的应用

场景进行取舍,那现在我们来思考一下,对于这个部门经理,department这个属性,现在是声明在外部的,Manager的department

属性,它是在外部来声明的,并且Manager的department的状态依赖于外部传入的一个参数,所以我们可以认为他是外部状态,

因为我传入不同的department,所获得的这个Manager,他里面的department的属性是不一样的,对于Manager这个实体,

我们就可以认为这个状态department这个状态,是外部状态,那怎么理解内部状态,很简单,我们写一个private String title,

直接赋值部门经理,这个时候我们外部怎么传department,或者reportContent,Manager的title是不变的,他不随外部的department

的状态变化而变化,他的title永远是部门经理,非常容易理解,之所以把title放到这里面讲,而不是之前讲呢,是因为前面我们也有说,

大家在coding的时候呢,就开始思考,然后在这里面我们再强调一下,并且产生对比,这样你们对外部状态和内部状态肯定会理解的,

那这种例子非常多,假设我们现在要用程序,画圆形,而这个圆形的半径,如果我们是固定好的,那他就是内部状态,如果这个圆形的

半径需要我们在应用层,客户端代码来传入的话,那么他就是外部状态,那我们再思考一个问题,如果这个圆形还要有颜色呢,

希望你们能继续思考一下,非常简单,那享元模式就讲到这里,我们看一下享元模式在一些源码的应用
package com.learn.design.pattern.structural.flyweight;

/**
 * 这个接口我们有一个方法report
 * 部门的Manager当然也是员工
 * 他实现员工这个接口之后呢
 * 也就要实现report接口
 * 我们再创建一个类
 * Manager
 * 
 * @author Leon.Sun
 *
 */
public interface Employee {
	/**
	 * 做报告
	 * 
	 * 
	 */
    void report();
}
package com.learn.design.pattern.structural.flyweight;

/**
 * 实现Employee
 * 然后实现他的接口
 * 既然是部分的Manager
 * 他的属性就是所在的部门
 * 
 * 
 * @author Leon.Sun
 *
 */
public class Manager implements Employee {
	/**
	 * 那在report的时候就比较简单了
	 * 因为报告的内容我已经拿到了
	 * 所以我们直接输出
	 * reportContent
	 * 开始做报告
	 * 那这两个映射类OK了
	 * 我们还要创建一个类呢
	 * 是工厂
	 * 通过Employee的工厂呢
	 * 来获取对应的Manager
	 * 对应的Employee
	 * 那我们这里只关注Manager
	 * 因为现在只让Manager做报告
	 * 我们Employee不需要做报告
	 * 
	 * 
	 */
    @Override
    public void report() {
        System.out.println(reportContent);
    }
    private String title = "部门经理";
    /**
     * 部门
     */
    private String department;
    /**
     * 那在做报告的时候
     * 需要有内容的
     * 所以他还持有一个报告的内容
     * reportContent
     * 这么两个属性
     * 那这个Manager在new的时候
     * 我肯定是应该知道
     * 他在哪个部门的
     * 
     *    
     */
    private String reportContent;

    /**
     * 那对于reportContent
     * 我们可以在外部重置reportContent
     * 所以我们加一个reportContent的set方法
     * 
     * 
     * @param reportContent
     */
    public void setReportContent(String reportContent) {
        this.reportContent = reportContent;
    }

    /**
     * 这里加一个构造器
     * 这个构造器只传一个department就可以了
     * 
     * 
     * @param department
     */
    public Manager(String department) {
        this.department = department;
    }
}
package com.learn.design.pattern.structural.flyweight;

import java.util.HashMap;
import java.util.Map;

/**
 * EmployeeFactory这个类是工厂类
 * 你们肯定是相当熟悉了
 * 之前有系统的学习过
 * 工厂相关的模式
 * 而且在单例模式中
 * 也学过容器单例
 * 所以这个类如何实现
 * 相信大部分是心里有数的
 * 那我们这一节就当一个复习
 * 非常简单
 * 轻松加愉快的来学习享元模式
 * 
 * 
 * 
 * @author Leon.Sun
 *
 */
public class EmployeeFactory {
	/**
	 * 首先我们来创建一个容器
	 * final的Map
	 * key是String
	 * value是Employee
	 * 这个就叫EMPLOYEE_MAP
	 * 因为我们使用static和final
	 * IDEA会自动提示使用大写的命名
	 * 直接new一个HashMap
	 * 然后泛型写上
	 * 我们都是用原生的JDK来做
	 * 现在把Mape的包导入一下
	 * 
	 * 
	 */
    private static final Map<String,Employee> EMPLOYEE_MAP = new HashMap<String,Employee>();

    /**
     * 我们再写一个方法
     * 返回者肯定是这个类getManager
     * 就是需要从EMPLOYEE_MAP中获取对应的Manager
     * Manager也是Employee
     * 接下来想要哪个经理来做报告
     * 这个部门我们就要交给外部来传
     * 以后我们在使用的时候
     * 你们就要回想一下
     * 我们之前有讲过内部状态和外部状态
     * 这个时候大家就可以跟着思考一下
     * 这个时候我们传入一个入参
     * department
     * 
     * 
     * 
     * @param department
     * @return
     */
    public static Employee getManager(String department){
    	/**
    	 * 然后获取一个Manager
    	 * 直接从这里面获得一个Manager
    	 * 赋值成什么呢
    	 * 肯定是从Map里面直接取
    	 * get的key肯定就是部门
    	 * 这里面需要我们强转
    	 * 
    	 * 首先从Map里面获取一个Manager
    	 * 
    	 * 
    	 */
        Manager manager = (Manager) EMPLOYEE_MAP.get(department);

        /**
         * 这个时候做一些空判断
         * 如果manager是空的话
         * 
         * 如果没有获取到
         * 就new一个放进去
         * 如果我们还想让同一部门的经理来做报告的话
         * 那我们直接从对象池中取出来
         * 就不需要new了
         * 接下来我们来写一下主函数
         * Test测试类
         * 
         * 
         */
        if(manager == null){
        	/**
        	 * 那我们就new一个manager出来
        	 * 把department传进去
        	 * 
        	 * manager现在有部门了
        	 * 那还需要让他来创建报告
        	 * 
        	 * 
        	 */
            manager = new Manager(department);
            System.out.print("创建部门经理:"+department);
            /**
             * 然后把报告内容统一一下
             * 
             * 
             */
            String reportContent = department+"部门汇报:此次报告的主要内容是......";
            /**
             * 所以我们直接在这里面manager.setReportContent
             * 首先是部门名称加上部门汇报
             * 谁来汇报
             * 此处的报告内容是
             * 开始做报告了
             * 那我们把这个顺序改一下
             * 设置完报告呢
             * 就输出一个创建报告
             * 然后reportContent放到这里边
             * 这样当这个经理第一次来做报告的时候
             * 首先我们这个系统要创建这个经理
             * 然后再创建这个报告
             * 都创建好之后
             * 放到这个对象池里边
             * 下次做报告直接从池里拿
             * 不再走这个new的过程
             * 我们现在回到Test里边
             * 
             * 
             */
            manager.setReportContent(reportContent);
            System.out.println(" 创建报告:"+reportContent);
            /**
             * 然后往EMPLOYEE_MAP中put
             * 把这个department和manager放进去
             * 
             * 
             */
            EMPLOYEE_MAP.put(department,manager);

        }
        /**
         * 直接return manager
         * 非常简单
         * 那捋一下逻辑
         * 
         * 
         */
        return manager;
    }
}
package com.learn.design.pattern.structural.flyweight;

/**
 * 也就是应用层的代码
 * 
 * 
 * @author Leon.Sun
 *
 */
public class Test {
	/**
	 * 首先我们声明一个部门的常量
	 * final的
	 * 我们直接用一个String的数组就行
	 * departments的一个数组
	 * 这里面我们想象一下
	 * 我们有研发部门
	 * 还有呢QA部门
	 * 还有PM部门
	 * 还有BD部门
	 * 其他的部门我们就不写了
	 * 
	 * 
	 */
    private static final String departments[] = {"RD","QA","PM","BD"};

    /**
     * 我们写一个主函数
     * 
     * 
     * @param args
     */
    public static void main(String[] args) {
    	/**
    	 * 从这个部门随机的取
    	 * 有一个for循环
    	 * 
    	 * 
    	 */
        for(int i=0; i<10; i++){
        	/**
        	 * 首先我们有一个department
        	 * 这里面有一个随机数从departments数组里随机的取
        	 * 也就是说用这个数组的索引取随机数
        	 * 这样这个部门也就随机了
        	 * 打个比方
        	 * 今天副总经理让研发的Manager做报告
        	 * 过了几天呢
        	 * 总经理又来说
        	 * 研发的经理过来
        	 * 大概年底的时间
        	 * 部门的Manager经常要做报告
        	 * 各种报告层出不穷
        	 * 所以这里还真的特别适合响应模式
        	 * 我们接着写
        	 * 从departments里面直接取一个
        	 * 取一个下角标
        	 * Math.random()这个方法
        	 * 让他乘上departments的length
        	 * 就OK了
        	 * 进行类型的转换
        	 * 把它转换成int
        	 * 这样就从里面随机的获取元素
        	 * 来获取这个部门的名称
        	 * 然后Manager
        	 * 
        	 * 
        	 * 
        	 */
            String department = departments[(int)(Math.random() * departments.length)];
            /**
             * 来获取这个Manager
             * 
             * EmployeeFactory.getManager
             * 把department传进去
             * 这里面按照提示强转一下
             * 那在Manager第一次做报告的时候
             * 报告还没有准备好
             * 所以第一次的时候
             * 他需要new一个报告出来
             * 那以后就不需要new了
             * 就直接拿过来
             * 开始做汇报
             * 所以我们来到getManager这里边
             * 
             * 
             * 
             */
            Manager manager = (Manager) EmployeeFactory.getManager(department);
            /**
             * 做报告
             * 
             * manager点report方法
             * 那么现在来run一下
             * 来看一下结果是什么样子的
             * 我们看一下
             * 这里是10次的循环
             * 首先把QA的经理叫过来了
             * 让他做报告
             * 他赶快创建一个报告
             * 主要是......
             * 而第二次的时候
             * 又让他做一次报告
             * 他呢不需要再创建了
             * 我这里有现成的
             * 对于我们这个对象池我们这个经理也在
             * 他的报告也在
             * 那第三次叫BD的经理来做报告了
             * 然后连着让他做两次
             * 又叫QA的部门来做报告了
             * 他还是直接拿过来做报告
             * 然后叫PM的部门来做报告
             * 然后叫RD的部门来做报告
             * 那这些都是一样的
             * 创建过程也是一样的
             * 我们接着回来
             * 因为这里的代码业务逻辑非常简单
             * 就不领着一起来debug了
             * 而且我们在之前学习容器单例的时候
             * 这个知识点都有学过
             * 而且这里面的逻辑清晰而简单
             * 那我们来解析一下他的类图
             * 
             * 
             * 
             */
            manager.report();

        }
//        Integer a = Integer.valueOf(100);
//        Integer b = 100;
//
//        Integer c = Integer.valueOf(1000);
//        Integer d = 1000;
//
//        System.out.println("a==b:"+(a==b));
//
//        System.out.println("c==d:"+(c==d));

    }
}

 

Guess you like

Origin blog.csdn.net/Leon_Jinhai_Sun/article/details/90900252