结构型设计模式
- 结构型设计模式涉及如何组装类和对象以获得更大的结构。
- 结构型类模式采用继承机制来组合接口或实现。
1、代理模式
简介:
- 代理模式(Proxy Pattern)使用一个类代表另一个类的功能,具有现有对象的对象,以便向外界提供功能接口
- 举例:
- 跟MM在网上聊天,一开头总是“hi,你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这些话,真烦人,写个程序做为我的Proxy吧,凡是接收到这些话都设置好了自己的回答,接收到其他的话时再通知我回答,怎么样,酷吧。
- 代理模式:
- 代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。
- 代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。
- 客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。
- 意图:
- 为其他对象提供一种代理以控制对这个对象的访问
- 主要解决:
- 在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上
- 在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层
适用场景:
- 买火车票不一定在火车站买,也可以去代售点
- 一张支票或银行存单是账户中资金的代理,支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制
- spring aop
- 远程代理
- 虚拟代理
- Copy-on-Write 代理
- 保护(Protect or Access)代理
- Cache代理
- 防火墙(Firewall)代理
- 同步化(Synchronization)代理
- 智能引用(Smart Reference)代理
结构:
- 定义一个 Image 接口和实现了 Image 接口的实体类
- 定义代理类 ProxyImage ,减少 RealImage 对象加载的内存占用
- 定义类 ProxyPatternDemo 使用 ProxyImage 来获取要加载的 Image 对象,并按照需求进行显示
代码实现:
1、 创建一个接口并实现
public interface Image {
void display();
}
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName){
System.out.println("Loading " + fileName);
}
}
public class ProxyImage implements Image{
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName){
this.fileName = fileName;
}
@Override
public void display() {
if(realImage == null){
realImage = new RealImage(fileName);
}
realImage.display();
}
}
2、当被请求时,使用 ProxyImage 来获取 RealImage 类的对象
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");
//图像将从磁盘加载
image.display();
System.out.println("");
//图像将无法从磁盘加载
image.display();
}
}
动态代理
动静态代理的区别,什么场景使用?
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道要代理什么,而动态代理不知道要代理什么。只有在运行时才知道。
动态代理是实现JDK的InvocationHandler接口的invoke方法,但注意代理的是接口。也就是你的业务类必须实现的接口,通过proxy里的newProxyInstance得到代理对象。
还有一种动态代理CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现接口,通过在运行时,动态修改字节码达到修改类的目的。
AOP编程是基于动态代理实现的,比如spring框架。
2、适配器设计模式
简介:
-
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。
-
适配器模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能
-
举例:
- 在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友kent了,他作为我和Sarah之间的Adapter,让我和Sarah可以相互交谈了(也不知道他会不会耍我)
- 读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡
-
适配器模式:
- 把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。
- 适配类可以根据参数返还一个合适的实例给客户端。
-
意图:
将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
-
主要解决:
主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的
-
何时使用:
- 系统需要使用现有的类,而此类的接口不符合系统的需要
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口
- 通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
适用场景:
- 美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V
- JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式
- 在 LINUX 上运行 WINDOWS 程序
- JAVA 中的 jdbc
- 有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
结构:
- MediaPlayer 接口和一个实现了 MediaPlayer 接口的实体类 AudioPlayer 。默认情况下, AudioPlayer 可以播放 mp3 格式的音频文件
- 接口 AdvancedMediaPlayer 和实现了 AdvancedMediaPlayer 接口的实体类。该类可以播放 vlc 和 mp4 格式的文件
- 让 AudioPlayer 播放其他格式的音频文件。为了实现这个功能,我们需要创建一个实现了 MediaPlayer 接口的适配器类 MediaAdapter ,并使用 AdvancedMediaPlayer 对象来播放所需的格式
- AudioPlayer 使用适配器类 MediaAdapter 传递所需的音频类型,不需要知道能播放所需格式音频的实际类
- AdapterPatternDemo 使用 AudioPlayer 类来播放各种格式
代码实现:
1、为媒体播放器和更高级的媒体播放器创建接口
public interface MediaPlayer {
public void play(String audioType, String fileName);
}
public interface AdvancedMediaPlayer {
public void playVlc(String fileName);
public void playMp4(String fileName);
}
2、创建实现了 AdvancedMediaPlayer 接口的实体类
public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName);
}
@Override
public void playMp4(String fileName) {
//什么也不做
}
}
public class Mp4Player implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
//什么也不做
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: "+ fileName);
}
}
3、创建实现了 MediaPlayer 接口的适配器类
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}
4、创建实现了 MediaPlayer 接口的实体类
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
//播放 mp3 音乐文件的内置支持
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file. Name: "+ fileName);
}
//mediaAdapter 提供了播放其他文件格式的支持
else if(audioType.equalsIgnoreCase("vlc")
|| audioType.equalsIgnoreCase("mp4")){
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
}
else{
System.out.println("Invalid media. "+
audioType + " format not supported");
}
}
}
5、使用 AudioPlayer 来播放不同类型的音频格式
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind me.avi");
}
}
适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性文艺。
主要分为三类: 类的适配器模式、对象的适配器模式、接口的适配器模式。
适配器模式,即定义一个包装类,用于包装不兼容接口的对象
类的适配器模式:
public static class Source {
public void method1() {
System.out.println("this is original method!");
}
}
public interface Targetable {
// 与原类中方法相同
public void method1();
// 新类的方法
public void method2();
}
public static class Adapter extends Source implements Targetable {
@Override
public void method2() {
System.out.println("this is Targetable method!");
}
}
public static void main(String[] args) {
Targetable targetable = new Adapter();
targetable.method1();
targetable.method2();
}
对象的适配器模式:
public static class Source {
public void method1() {
System.out.println("this is original method!");
}
}
public interface Targetable {
// 与原类中方法相同
public void method1();
// 新类的方法
public void method2();
}
public static class Wrapper implements Targetable {
private Source source;
public Wrapper(Source source) {
super();
this.source = source;
}
@Override
public void method1() {
source.method1();
}
@Override
public void method2() {
System.out.println("this is Targetable method!");
}
}
public static void main(String[] args) {
Source source = new Source();
Targetable targetable = new Wrapper(source);
targetable.method1();
targetable.method2();
}
接口的适配器模式:
接口的适配器模式是这样的:
有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显就比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,而我们不和原接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。
3、装饰器模式
简介:
-
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。
-
装饰器模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能
-
举例:
- Mary过完轮到Sarly过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,我们都是Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗?
-
装饰模式:
- 装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。
- 动态给一个对象增加功能,这些功能可以再动态的撤消。
- 增加由一些基本功能的排列组合而产生的非常大量的功能。
-
意图:
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活
-
主要解决:
一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀
适用场景:
- 孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能
- 不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体
- 扩展一个类的功能
- 动态增加功能,动态撤销
结构:
- 创建一个 Shape 接口和实现了 Shape 接口的实体类
- 创建一个实现了 Shape 接口的抽象装饰类 ShapeDecorator ,并把 Shape 对象作为它的实例变量
- 创建类 RedShapeDecorator 实现了 ShapeDecorator 实体类
- 创建类 DecoratorPatternDemo 使用 RedShapeDecorator 来装饰 Shape 对象
代码实现:
1、创建一个接口并实现
public interface Shape {
void draw();
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Circle");
}
}
2、创建实现了 Shape 接口的抽象装饰类
public abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}
public void draw(){
decoratedShape.draw();
}
}
3、创建扩展了 ShapeDecorator 类的实体装饰类
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
4、使用 RedShapeDecorator 来装饰 Shape 对象
public class DecoratorPatternDemo {
public static void main(String[] args) {
Shape circle = new Circle();
Shape redCircle = new RedShapeDecorator(new Circle());
Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}
装饰器模式Decorator,就是给一个对象增加一些新的功能,而且是动态的,
要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。
public interface Sourceable {
public void method();
}
public static class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
public static class Decorator implements Sourceable {
private Sourceable source;
public Decorator(Sourceable source) {
super();
this.source = source;
}
@Override
public void method() {
System.out.println("before decorator!");
source.method();
System.out.println("after decorator!");
}
}
public static void main(String[] args) {
Sourceable source = new Source();
Sourceable obj = new Decorator(source);
obj.method();
}
4、外观模式
简介:
- 外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口
- 外观模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用
- 简介:
- 我有一个专业的Nikon相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸好相机有Facade设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整,这样MM也可以用这个相机给我拍张照片了。
- 外观模式:
- 外部与一个子系统的通信必须通过一个统一的门面对象进行。
- 外观模式提供一个高层次的接口,使得子系统更易于使用。
- 每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。
适用场景:
- 去医院看病,可能要去挂号、门诊、划价、取药,让患者或患者家属觉得很复杂,如果有提供接待人员,只让接待人员来处理,就很方便
- JAVA 的三层开发模式
- 为复杂的模块或子系统提供外界访问的模块
- 子系统相对独立
- 预防低水平人员带来的风险
结构:
- 创建一个 Shape 接口和实现了 Shape 接口的实体类
- 定义一个外观类 ShapeMaker
- 定义类 ShapeMaker 使用实体类来代表用户对这些类的调用
- 定义类 FacadePatternDemo 使用 ShapeMaker 类来显示结果
代码实现:
1、创建一个接口并实现
public interface Shape {
void draw();
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Rectangle::draw()");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Square::draw()");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Circle::draw()");
}
}
2、创建一个外观类
public class ShapeMaker {
private Shape circle;
private Shape rectangle;
private Shape square;
public ShapeMaker() {
circle = new Circle();
rectangle = new Rectangle();
square = new Square();
}
public void drawCircle(){
circle.draw();
}
public void drawRectangle(){
rectangle.draw();
}
public void drawSquare(){
square.draw();
}
}
3、使用该外观类画出各种类型的形状
public class FacadePatternDemo {
public static void main(String[] args) {
ShapeMaker shapeMaker = new ShapeMaker();
shapeMaker.drawCircle();
shapeMaker.drawRectangle();
shapeMaker.drawSquare();
}
}
5、桥接模式
简介:
-
桥接模式(Bridge Pattern)是用于把抽象化与实现化解耦,使得二者可以独立变化
-
桥接模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类,这两种类型的类可被结构化改变而互不影响
-
也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。
-
举例:
- 早上碰到MM,要说早上好,晚上碰到MM,要说晚上好;碰到MM穿了件新衣服,要说你的衣服好漂亮哦,碰到MM新做的发型,要说你的头发好漂亮哦。不要问我“早上碰到MM新做了个发型怎么说”这种问题,自己用BRIDGE组合一下不就行了
-
意图:
将抽象部分与实现部分分离,使它们都可以独立的变化
-
主要解决:
在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活
-
何时使用:
实现系统可能有多个角度分类,每一种角度都可能变化
-
如何解决:
把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合
适用场景:
-
猪八戒从天蓬元帅转世投胎到猪,转世投胎的机制将尘世划分为两个等级,即:灵魂和肉体,前者相当于抽象化,后者相当于实现化
生灵通过功能的委派,调用肉体对象的功能,使得生灵可以动态地选择
-
墙上的开关,可以看到的开关是抽象的,不用管里面具体怎么实现的
结构:
- 创建一个作为桥接实现的 DrawAPI 接口和实现了 DrawAPI 接口的实体类 RedCircle 、 GreenCircle
- Shape 是一个抽象类,将使用 DrawAPI 的对象
- BridgePatternDemo 使用 Shape 类来画出不同颜色的圆
代码实现:
1、 创建桥接实现接口和实现类
public interface DrawAPI {
public void drawCircle(int radius, int x, int y);
}
public class RedCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle[ color: red, radius: "
+ radius +", x: " +x+", "+ y +"]");
}
}
public class GreenCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle[ color: green, radius: "
+ radius +", x: " +x+", "+ y +"]");
}
}
2、使用 DrawAPI 接口创建抽象类 Shape
public abstract class Shape {
protected DrawAPI drawAPI;
protected Shape(DrawAPI drawAPI){
this.drawAPI = drawAPI;
}
public abstract void draw();
}
public class Circle extends Shape {
private int x, y, radius;
public Circle(int x, int y, int radius, DrawAPI drawAPI) {
super(drawAPI);
this.x = x;
this.y = y;
this.radius = radius;
}
public void draw() {
drawAPI.drawCircle(radius,x,y);
}
}
3、使用 Shape 和 DrawAPI 类画出不同颜色的圆
public class BridgePatternDemo {
public static void main(String[] args) {
Shape redCircle = new Circle(100,100, 10, new RedCircle());
Shape greenCircle = new Circle(100,100, 10, new GreenCircle());
redCircle.draw();
greenCircle.draw();
}
}
6、组合模式
简介:
-
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象
-
组合模式创建了一个包含自己对象组的类,该类提供了修改相同对象组的方式。
-
举例:
- Mary今天过生日。
- “我过生日,你要送我一件礼物。”
- “嗯,好吧,去商店,你自己挑。”
- “这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”
- “喂,买了三件了呀,我只答应送一件礼物的哦。”
- “什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。”
- “……”,
- MM都会用Composite模式了,你会了没有?
-
合成模式:
- 合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。
- 合成模式就是一个处理对象的树结构的模式。
- 合成模式把部分与整体的关系用树结构表示出来。
- 合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。
-
何时使用:
- 您想表示对象的部分-整体层次结构(树形结构)
- 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象
-
如何解决:
树枝和叶子实现统一接口,树枝内部组合该接口
-
关键代码:
树枝内部组合该接口,并且含有内部属性 List,里面放 Component
适用场景:
- 算术表达式包括操作数、操作符和另一个操作数,其中,另一个操作符也可以是操作树、操作符和另一个操作数
- 在 JAVA AWT 和 SWING 中,对于 Button 和 Checkbox 是树叶,Container 是树枝
- 部分、整体场景,如树形菜单,文件、文件夹的管理
结构:
- 类 Employee ,该类被当作组合模型类
- CompositePatternDemo 类使用 Employee 类来添加部门层次结构,并打印所有员工
代码实现:
1、创建 Employee 类,该类带有 Employee 对象的列表
public class Employee {
private String name;
private String dept;
private int salary;
private List<Employee> subordinates;
//构造函数
public Employee(String name,String dept, int sal) {
this.name = name;
this.dept = dept;
this.salary = sal;
subordinates = new ArrayList<Employee>();
}
public void add(Employee e) {
subordinates.add(e);
}
public void remove(Employee e) {
subordinates.remove(e);
}
public List<Employee> getSubordinates(){
return subordinates;
}
public String toString(){
return ("Employee :[ Name : "+ name
+", dept : "+ dept + ", salary :"
+ salary+" ]");
}
}
2、 使用 Employee 类来创建和打印员工的层次结构
public class CompositePatternDemo {
public static void main(String[] args) {
Employee CEO = new Employee("John","CEO", 30000);
Employee headSales = new Employee("Robert","Head Sales", 20000);
Employee headMarketing = new Employee("Michel","Head Marketing", 20000);
Employee clerk1 = new Employee("Laura","Marketing", 10000);
Employee clerk2 = new Employee("Bob","Marketing", 10000);
Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
Employee salesExecutive2 = new Employee("Rob","Sales", 10000);
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
//打印该组织的所有员工
System.out.println(CEO);
for (Employee headEmployee : CEO.getSubordinates()) {
System.out.println(headEmployee);
for (Employee employee : headEmployee.getSubordinates()) {
System.out.println(employee);
}
}
}
}
7、享元模式
简介:
-
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能
-
享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象
-
举例:
- 每天跟MM发短信,手指都累死了,最近买了个新手机,可以把一些常用的句子存在手机里,要用的时候,直接拿出来,在前面加上MM的名字就可以发送了,再不用一个字一个字敲了。共享的句子就是Flyweight,MM的名字就是提取出来的外部特征,根据上下文情况使用。
-
享元模式:FLYWEIGHT在拳击比赛中指最轻量级。
- 享元模式以共享的方式高效的支持大量的细粒度对象。
- 享元模式能做到共享的关键是区分内蕴状态和外蕴状态。
- 内蕴状态存储在享元内部,不会随环境的改变而有所不同。
- 外蕴状态是随环境的改变而改变的。
- 外蕴状态不能影响内蕴状态,它们是相互独立的。
- 将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。
- 客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。
- 享元模式大幅度的降低内存中对象的数量。
-
意图:
运用共享技术有效地支持大量细粒度的对象
-
主要解决:
在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建
-
何时使用:
- 系统中有大量对象
- 这些对象消耗大量内存
- 这些对象的状态大部分可以外部化
- 这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替
- 系统不依赖于这些对象身份,这些对象是不可分辨的
适用场景:
- JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面
- 数据库的数据池
- 系统有大量相似对象
- 需要缓冲池的场景
结构:
-
定义一个 Shape 接口和实现了 Shape 接口的实体类 Circle
-
定义工厂类 ShapeFactory
ShapeFactory 有一个 Circle 的 HashMap ,其中键名为 Circle 对象的颜色
无论何时接收到请求,都会创建一个特定颜色的圆
ShapeFactory 检查它的 HashMap 中的 circle 对象,如果找到 Circle 对象,则返回该对象,否则将创建一个存储在 hashmap 中以备后续使用的新对象,并把该对象返回到客户端
-
定义类 FlyWeightPatternDemo 使用 ShapeFactory 来获取 Shape 对象
它将向 ShapeFactory 传递信息( red / green / blue/ black / white ),以便获取它所需对象的颜色
代码实现:
1、定义一个接口和实现类
public interface Shape {
void draw();
}
public class Circle implements Shape {
private String color;
private int x;
private int y;
private int radius;
public Circle(String color){
this.color = color;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setRadius(int radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("Circle: Draw() [Color : " + color
+", x : " + x +", y :" + y +", radius :" + radius);
}
}
2、创建一个工厂,生成基于给定信息的实体类的对象
public class ShapeFactory {
private static final HashMap<String, Shape> circleMap = new HashMap();
public static Shape getCircle(String color) {
Circle circle = (Circle)circleMap.get(color);
if(circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("Creating circle of color : " + color);
}
return circle;
}
}
3、使用该工厂,通过传递颜色信息来获取实体类的对象
public class FlyweightPatternDemo {
private static final String colors[] =
{ "Red", "Green", "Blue", "White", "Black" };
public static void main(String[] args) {
for(int i=0; i < 20; ++i) {
Circle circle =
(Circle)ShapeFactory.getCircle(getRandomColor());
circle.setX(getRandomX());
circle.setY(getRandomY());
circle.setRadius(100);
circle.draw();
}
}
private static String getRandomColor() {
return colors[(int)(Math.random()*colors.length)];
}
private static int getRandomX() {
return (int)(Math.random()*100 );
}
private static int getRandomY() {
return (int)(Math.random()*100);
}
}