画笔盒的智慧
我们都知道,小孩儿画画都有一个画笔盒,这是一件很普通的事情,但是这其中蕴含着大智慧。
今天我们用一个设计模式来阐述一下这其中的智慧。
一、初识享元模式
享元模式(Flyweight Pattern) 主要的作用是减少创建对象的数量。
大家都晓得,对象比较占内存(跟你对象似的,衣柜全是她占了),享元模式就是通过减少创建对象来减少内存和提高性能的。
享元模式属于结构型模式,他提供了减少对象数量从而改善引用所需对象结构的方式。
说白了,就是想方设法减少对象,其实我理解享元模式的时候就会想到"单例模式",我的理解是能单例的都单例了,实在不能单例了才会享元。
就是一个对象能解决的事情就不要找俩了,你想生个俩小宝宝,你总不能一胎找一个对象,二胎找一个对象吧。你也太不是人了。
我们项目中如果有大量的相似对象 或者 类似缓冲池的场景 的时候 我们可以考虑这种设计模式。
比如Java的String、数据库连接池 都是使用的这种设计模式,可能当时他们不是为了使用这种设计模式才那样设计但是那样设计的结果是符合这种设计模式。
在工作中我们也是一样,没有地方找理论的时候,那就去做,做完了你就得出理论了。勇敢牛牛向前冲。
二、小二上图
我们用画五颜六色的线条来做示例
这里注意了,各种颜色的线条创建都是由一个工厂控制的,这也很容易理解,不然没法统一管理和存储,跟单例一样,总得有一个地方存储。
三、写一写
我们的线条定义了起始坐标和终止坐标。想画某个颜色的线条时,先看有没有这个颜色的线条对象,以的话直接用,没有的话先创建,再用。
Shape
package flyweight;
/**
* @author 木子的昼夜编程
* 图形接口,约束画图行为
*/
public interface Shape {
void draw();
}
Line
package flyweight;
/**
* @author 木子的昼夜编程
* 具体线条类 画五颜六色线条
*/
public class Line implements Shape{
// 起始坐标 终止坐标
int startX,startY,endX,endY;
// 颜色
String color;
public Line(String color){
this.color = color;
}
/**
* 画线
*/
@Override
public void draw() {
String str = "起始坐标:{%s,%s},终止坐标:{%s,%s},颜色:%s";
String res = String.format(str, startX, startY, endX, endY, color);
System.out.println(res);
}
public int getStartX() {
return startX;
}
public void setStartX(int startX) {
this.startX = startX;
}
public int getStartY() {
return startY;
}
public void setStartY(int startY) {
this.startY = startY;
}
public int getEndX() {
return endX;
}
public void setEndX(int endX) {
this.endX = endX;
}
public int getEndY() {
return endY;
}
public void setEndY(int endY) {
this.endY = endY;
}
}
ShapeFactory
````jpackage flyweight;
import java.util.HashMap;
/**
* @author 木子的昼夜编程
* 负责存储各种颜色线条 和 生产线条
*/
public class ShapeFactory {
private static HashMap<String, Shape> lineMap = new HashMap<>();
// 根据不同颜色获取Line
public static Shape getLine(String color){
// 如果不存在就创建颜色对应的line
if (!lineMap.containsKey(color)) {
System.out.println("创建线条:"+color);
lineMap.put(color, new Line(color));
}
// 返回Line
return lineMap.get(color);
}
}
FlyWeightPatternDemo
package flyweight;
/**
* @author 木子的昼夜编程
*/
public class FlyWeightPatternDemo {
public static void main(String[] args) {
// 红色线条画画
Line line01 = (Line) ShapeFactory.getLine("红色");
line01.setStartX(0);
line01.setStartY(0);
line01.setEndX(1);
line01.setEndY(1);
line01.draw();
// 红色线条画画
Line line02 = (Line) ShapeFactory.getLine("红色");
line02.setStartX(2);
line02.setStartY(3);
line02.setEndX(4);
line02.setEndY(5);
line02.draw();
System.out.println(line01 == line02);
// 蓝色线条画画
Line line03 = (Line) ShapeFactory.getLine("蓝色");
line03.setStartX(12);
line03.setStartY(34);
line03.setEndX(45);
line03.setEndY(56);
line03.draw();
}
}
四、唠一唠
通过示例我们可以看到,红色线条第一次是创建,第二次就是直接从缓存获取了,第二次与第一次使用的是一个对象。
问题1: 正是因为我们获取的是一个对象,所以我们使用的时候一定要注意线程安全问题,如果多个线程同时使用红色线条画图肯定是会出问题的,所以理解我们尽可能使用只读的对象,比如上边的起始终止坐标,我们不要设置到Line属性上,我们当参数传给draw方法
问题2:ShapeFactory 创建shape的方法是有问题的,没有保证线程安全,可以参考单例DCL 自行优化
与我们的画笔盒一样,把所有颜色的画笔都存储在画笔盒,我们想画什么颜色图形就找对应颜色的画笔,而不是每次去买一个画笔(创建一个对象),画笔用完了我们就放回画笔盒,而不是扔了(创建对象用完销毁)。
以后遇到学美术的,给他讲讲享元模式吧。