享元模式(结构型)

思考问题:设计一个围棋游戏,模拟一个下棋动作,如何设计?

解答:很直接的,我们会设计一个棋盘类Chessboard,一个棋子类Chesspiece,每下一枚棋子时就new一个棋子对象(传入颜色、位置),然后将这些棋子装入到一个容器中。

这种简单粗暴的方式确实是解决了问题,但你会发现,棋子永远只有黑白色,棋子对象的函数都是一样的, 主要的变化只是(x,y)位置而已,那能不能单独把(x,y)独立出来让调用者管理(而非记录到棋子内部),让棋子对象变成一个可重用的对象呢?这样就可以避免产生大量棋子对象了,这就是享元模式(把棋子享元的无法共享的状态交给调用者管理,模式只管理(重用)棋子享元)。

享元接口角色:该角色对享元类进行抽象,需要外部状态的操作可以通过参数的形式传入享元对象。

具体享元对象角色:该角色实现享元接口定义的业务,注意享元对象的内部状态与环境无关,从而使得享元对象可以在系统内共享。

享元工厂角色:该角色就是构造一个池容器(pool),负责创建和管理享元角色,并提供从池容器中获得对象的方法,保证享元角色会检查系统中是否已经有一个符合要求的享元对象,如果已经存在,享元工厂则提供这个已有的享元对象;否则创建一个合适的享元对象。

客户端角色:该角色需要自行存储所有享元对象的外部状态。



两个概念:

  1. 内部状态:是存储在享元对象内部的、可以共享的信息(如棋子颜色和大小属性),并且不会随环境改变而改变(比如棋子颜色大小不会随着坐标变化而变化);
  2. 外部状态:是随着环境改变而改变且不可以共享的状态(如坐标),享元对象的外部状态必须由客户端保存(如棋子设为享元模式后所有黑色棋子都引用同一个对象了,就不能保存自己的位置了),并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部做操作。


享元模式使用场景:

1. 系统中有大量的相似对象(如棋子),这些对象耗费大量内存。

2. 细粒度的对象都具备较接近的外部状态(如棋子的坐标就是外部状态),而且内部状态(如棋子颜色和大小)与环境无关,即对象没有特定身份。

3. 需要缓冲池的场景。



棋子的类图:

实例代码:

棋子享元的工厂:

package com.shusheng.flyweight;
import java.util.concurrent.ConcurrentHashMap;
/**棋子享元的工厂*/
public class ChesspieceFactory {

    private static ConcurrentHashMap<String,ChesspieceFlyweight> conHashMap = new ConcurrentHashMap<>();//这是线程安全且效率高的容器 

    /**工厂方法用于获取棋子享元对象*/
    public static ChesspieceFlyweight getChesspieceFlyweight(String color){
        ChesspieceFlyweight flyweight = conHashMap.get(color);
        if (flyweight==null) {
            flyweight = new ChesspieceFlyweight(color);
            conHashMap.put(color,flyweight);
        }
        System.out.println("容器的容量"+conHashMap.size());
        return flyweight;
    }

}

棋子享元的抽象接口:

package com.shusheng.flyweight;

/**抽象棋子应该有的动作*/
public interface ChesspieceI {

    /**下棋动作*/
    public void put(int x,int y);
}

棋子享元的具体实现:

package com.shusheng.flyweight;

/**棋子*/
public class ChesspieceFlyweight implements ChesspieceI{

    private String color;//棋子颜色

    ChesspieceFlyweight(String color) {//享元对象构造方法不对外公开,避免外部使用者直接new该对象。
        this.color = color;
    }

    /**下棋动作*/
    @Override
    public void put(int x, int y) {//这里简单处理,实际系统,应该要在界面画出那个棋子。
        System.out.println("我是"+color+"色棋子,我被下的位置是X="+x+",Y="+y+";\n");
    }
}

Client用于模拟享元模式的外部调用:

package com.shusheng.flyweight;

/**客户端角色,需要自己保存享元对象的所有外部状态*/
public class Client {

    public static void main(String[] args) {
        ChesspieceFlyweight chesspiece1 = ChesspieceFactory.getChesspieceFlyweight("黑");
        chesspiece1.put(10,10);//黑棋下到(10,10)

        ChesspieceFlyweight chesspiece3 = ChesspieceFactory.getChesspieceFlyweight("黑");
        chesspiece3.put(20,10);//黑棋下到(20,10)

        ChesspieceFlyweight chesspiece2 = ChesspieceFactory.getChesspieceFlyweight("白");
        chesspiece2.put(10,20);//白棋下到(10,20)

        ChesspieceFlyweight chesspiece4 = ChesspieceFactory.getChesspieceFlyweight("白");
        chesspiece4.put(20,20);//白棋下到(20,20)

    }
}

从运行结果可以看出,无论下多少个棋子,最后产生的棋子对象都只是两个,是不是比一开始的设计少了很多的对象、省了很多内存呢?



享元模式的优点:

1. 大幅度减少内存中对象的数量,降低程序的内存占用,提高性能。

缺点:

1. 享元模式增加了系统的复杂性(从上面的设计看得出来,好好的一个new的过程弄的挺复杂的);

2. 需要分外部状态和内部状态,而且内部状态具有固化特性,不应随外部状态改变而改变(比如棋子颜色大小等不随位置变化而变化),这使得程序的逻辑复杂化。

3. 享元模式将享元对象的状态外部化(指外部状态),而读取外部状态使得程序运行时间很长(比如选中一个棋子对象,就再也不能通过该对象知道它下的位置了)。

时间: 2024-12-16 23:33:45

享元模式(结构型)的相关文章

设计模式(十二): Flyweight享元模式 -- 结构型模式

说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释放.我们只是为了学习而简单做了介绍. 1. 概述 面向对象技术可以很好地解决系统一些灵活性或可扩展性或抽象性的问题,但在很多情况下需要在系统中增加类和对象的个数.当对象数量太多时,将导致运行代价过高,带来性能下降等问题.比如:例子1:图形应用中的图元等对象.字处理应用中的字符对象等. 2.解决方案: 享元模式(Flyweight):对象结构型模式运用

设计模式之享元模式(结构型)

模式定义 享元模式(Flyweight Pattern)就是通过共享技术实现大量细粒度对象的复用.享元模式是通过细粒度对象的共享,所以也可以说享元模式是一种轻量级模式.按照Gof模式分类,享元模式属于对象结构型模式. 模式解释 可以共享的内容称为内部状态(Intrinsic State),需要外部环境设置的不能共享的内容称为外部状态(Extrinsic State).享元模式需要创建一个享元工厂负责维护享元池(Flyweight Pool),享元池用于存储具有相同内部状态的享元对象. 享元模式中

Flyweight享元模式(结构型模式)

1.面向对象的缺点 虽然OOP能很好的解决系统抽象的问题,并且在大多数的情况下,也不会损失系统的性能.但是在某些特殊的业务下,由于对象的数量太多,采用面向对象会给系统带来难以承受的内存开销.示例代码如下: /// <summary> /// Word文字的Font样式 /// </summary> public class Font //8+8(继承object的虚表指针4个字节.垃圾收集同步占4个字节)=16个字节 { public Font(string fontName, i

Flyweight 享元(结构型)

一:描述:(该模式实际应用较少) Flyweight 享元模式是对大量细粒度的元素进行共享和重用.减少对象的创建减轻内存: 注和单例模式不同的是:享元模式的各个对象佣有各自的行为并可实例化,单例模式的各个对象佣有一样的行为并不可直接实例化. 二:模式图: 三:实现代码简单例子: 1.创建抽像的享元类 2.创建共享的享元对象(可以有很多这种对像) 3.创建非共享的享元对象 4.创建享元的工厂 5.客端的使用和效果;

NET设计模式 第二部分 结构性模式(12):享元模式(Flyweight Pattern)

享元模式(Flyweight Pattern) ——.NET设计模式系列之十三 Terrylee,2006年3月 摘要:面向对象的思想很好地解决了抽象性的问题,一般也不会出现性能上的问题.但是在某些情况下,对象的数量可能会太多,从而导致了运行时的代价.那么我们如何去避免大量细粒度的对象,同时又不影响客户程序使用面向对象的方式进行操作? 本文试图通过一个简单的字符处理的例子,运用重构的手段,一步步带你走进Flyweight模式,在这个过程中我们一同思考.探索.权衡,通过比较而得出好的实现方式,而不

.NET设计模式(13):享元模式(Flyweight Pattern)(转)

摘要:面向对象的思想很好地解决了抽象性的问题,一般也不会出现性能上的问题.但是在某些情况下,对象的数量可能会太多,从而导致了运行时的代价.那么我们如何去避免大量细粒度的对象,同时又不影响客户程序使用面向对象的方式进行操作? 本文试图通过一个简单的字符处理的例子,运用重构的手段,一步步带你走进Flyweight模式,在这个过程中我们一同思考.探索.权衡,通过比较而得出好的实现方式,而不是给你最终的一个完美解决方案. 主要内容: 1.  Flyweight模式解说 2..NET中的Flyweight

&quot;围观&quot;设计模式(17)--结构型之享元模式(Flyweight Pattern)

享元模式(英语:Flyweight Pattern)是一种软件设计模式.它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件:它适合用于当大量物件只是重复因而导致无法令人接受的使用大量内存.通常物件中的部分状态是可以分享.常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元.----WIKIPEDIA 个人理解 共享,内存消耗大的时候应考虑对象的共享,共享对象可以减少对象的生成数量,这样可以减少内存的消耗,当一个对象和其他的对象存在共性且内容一致的时候,可以将共有

设计模式-12 享元模式(结构型模式)

一 享元模式 享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用. 主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建. 关键代码:存储相似的对象 使用场景: 1.系统有大量相似对象. 2.需要缓冲池的场景. 类图 : 二 实现代码 Java里面的JDBC连接池,适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接 池来说,url.driv

设计模式(十)享元模式Flyweight(结构型)

说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释放.我们只是为了学习而简单做了介绍. 1. 概述 面向对象技术可以很好地解决系统一些灵活性或可扩展性或抽象性的问题,但在很多情况下需要在系统中增加类和对象的个数.当对象数量太多时,将导致运行代价过高,带来性能下降等问题.比如:例子1:图形应用中的图元等对象.字处理应用中的字符对象等. 2.解决方案: 享元模式(Flyweight):对象结构型模式运用

第13章 结构型模式—享元模式

1. 享元模式(Flyweight Pattern)的定义 (1)运用共享技术高效地支持大量细粒度的对象 ①对象内部状态:数据不变且重复出现,这部分不会随环境变化而改变,是可以共享的. ②对象外部状态:数据是变化的,会随环境变化而改变,是不可以共享的. ③所谓的享元,就是把内部状态的数据分离出来共享,通过共享享元对象,可以减少对内存的占用.把外部状态分离出来,放到外部,让应用程序在使用的时候进行维护,并在需要的时候传递给享元对象使用. ④享元模式真正缓存和共享的是享元的内部状态,而外部状态是不被