设计模式——享元模式

来源:互联网 发布:景观设计需要哪些软件 编辑:程序博客网 时间:2024/05/16 14:42

Author:云都小生

继承提高代码的复用性,享元模式提高对象的复用性。

推荐!史上最全设计模式导学目录


前述



我不知道大家有没有打过什么枪战游戏,那里面的子弹,都是一样的。若每一个子弹都是相同的对象,只不过每个子弹的位置不一样,那我们是不是得不断的new出一大堆的对象嘞?

换个案例,围棋,黑棋和白棋其实都是一模一样的,只是每一个棋子的位置不一样,那我们是不是得new好多个对象。那样肯定会造成内存浪费,而享元模式,就可以用来解决这种问题。

无论是在C语言还是在Java中,创建一个字符串“云都小生”,如果还创建一个字符串变量也是“云都小生”,那么都会指向同一个对象,这个也是为了避免内存浪费。

这里写图片描述


享元模式概述



当一个软件系统在运行时产生的对象数量过多,又有很多相同具有许多相同的、类似的对象时,我们就可以考虑用享元模式去尝试着优化。

在享元模式中有一个存储共享实力对象的地方,被称为——享元池

在享元模式中还有两个概念非常重要,一个是内部状态,另一个是外部状态

内部状态就是在享元对象内部,并且不会随着环境改变而改变的。你看像围棋中的棋子,除了颜色和位置,其他的就算放到其他地方也不会变。

外部状态就是会随着环境改变而改变的,就像上面所说的位置一样。

这里写图片描述


角色分析


抽象享元类:一个接口或抽象类,声明了具体享元类公共的方法。这些方法向外提供享元对象的内部数据,也提供了方法来设置外部数据。

在围棋游戏中,抽象享元类相当于语义中的“棋子”。

具体享元类:实现了抽象享元类,为内部状态提供了存储空间,每一个具体享元类都有一个唯一的享元对象。

在围棋游戏中,抽象享元类相当于语义中的“黑棋”“白棋”,无论黑棋和白棋有多少个,它们都有共享的特性——例如颜色、大小。

非共享具体享元类:不能被共享的子类可以设计成非共享具体享元类,非共享具体享元类也实现了抽象享元类。

在整个框架中,可能会有一些特殊且没有办法共享的对象,我们可以设计成非共享具体类。例如说飞行器中,确实各方的飞机棋子差不多,有一些特性可以共享。但是!有一个骰子,这个对象是不能共享的。

享元工厂类:享元模式引入了工厂类,享元工厂类用于创建并管理享元对象。它定义了一个集合(通常是一个键值对集合hash)),用来存储享元对象。

享元工厂类,在用户请求一个享元对象的时候,就会在享元池中找到相应的享元对象。如果找不到,就创建一个新的享元对象并返回。

这里写图片描述


代码示范1(内部状态)


//抽象享元类:棋子抽象类abstract class Chessman {    public abstract String getColor();    public void display()    {        System.out.println("棋子颜色:" + this.getColor());    }    public void refsize()    {        System.out.println("棋子大小:2cm x 2cm");    }}//具体享元类:黑色棋子public class BlackChessman extends Chessman{    public String getColor() {        return "黑色";    }}//具体享元类:白色棋子public class WhiteChessman extends Chessman{    public String getColor() {        return "白色";    }}//享元工厂类:棋子创建、存储、管理(包含享元池)import java.util.Hashtable;public class ChessmanFactory {    private static ChessmanFactory factory = new ChessmanFactory();    private static Hashtable ht; //享元池     private ChessmanFactory()    {        ht = new Hashtable();        Chessman black,white;        black = new BlackChessman();        ht.put("black", black);        white = new WhiteChessman();        ht.put("white", white);    }    //返回享元工厂类的唯一实例      public static ChessmanFactory getInstance() {          return factory;      }  //通过key来获取存储在Hashtable中的享元对象      public static Chessman getChessman(String color) {          return (Chessman)ht.get(color);        } }//客户端类public class Client {    public static void main(String[] args) {        Chessman black1,black2;        Chessman white1,white2;        //获取享元工厂对象          ChessmanFactory factory = ChessmanFactory.getInstance();        black1 = factory.getChessman("black");        black2 = factory.getChessman("black");        System.out.println("判断两颗黑子是否相同:" + (black1==black2));        white1 = factory.getChessman("white");        white2 = factory.getChessman("white");        System.out.println("判断两颗黑子是否相同:" + (white1==white2));    }}

其实我重点比较关注享元工厂类,它实现了享元池,对享元对象进行创建、存储、管理。通过客户端类的测试,我们也更清楚了,享元模式确保了对象的唯一性,还实现了对象的共享。在很大程度上节省了内存。

但是在上面这个案例中,我们只有共享的具体享元类,也就是颜色、大小这些特性是可以共享。但是像位置,这些特性是没办法共享的,也就是外部状态,这个怎么做?


代码示范2(外部状态)


//位置类(外部状态)public class Position {    private int x;    private int y;    public Position(int x,int y) {          this.x = x;          this.y = y;      }      public int getX() {          return this.x;      }      public void setX(int x) {          this.x = x;      }      public int getY() {          return this.y;      }      public void setY(int y) {          this.y = y;      }}//棋子抽象类(增加显示位置)abstract class Chessman {    public abstract String getColor();    public void display(Position position)    {        System.out.println("棋子颜色:" + this.getColor());        System.out.println("位置是:" + position.getX() + "," + position.getY());    }    public void refsize()    {        System.out.println("棋子大小:2cm x 2cm");    }}//客户端类public class Client {    public static void main(String[] args) {        Chessman black1,black2;        Chessman white1,white2;        //获取享元工厂对象          ChessmanFactory factory = ChessmanFactory.getInstance();        black1 = factory.getChessman("black");        black2 = factory.getChessman("black");        System.out.println("判断两颗黑子是否相同:" + (black1==black2));        white1 = factory.getChessman("white");        white2 = factory.getChessman("white");        System.out.println("判断两颗黑子是否相同:" + (white1==white2));        black1.display(new Position(1,2));          black2.display(new Position(3,4));          white1.display(new Position(2,5));          white2.display(new Position(2,4));      }}

这样就共享了同一个对象,但是外部状态——显示位置不一样。

2017/12/11 15:37:59 @Author:云都小生

原创粉丝点击