设计模式——享元模式


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:云都小生

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值