设计模式-享元模式

一、享元模式(结构型模式)
1.定义

享元模式(Flyweight Pattern)就是通过共享技术实现大量细粒度对象的复用。享元模式是通过细粒度对象的共享,所以享元模式是一种轻量级模式。
2.模式解释
享元模式有两种状态,可以共享的的内容就做内部状态(Intrinsic State),内部状态是不可变的,需要外部环境设置的不能共享的内容称为外部状态(Extrinisic State)。享元模式需要创建一个享元工厂负责维护享元池(Flyweight Pool),享元池用于存储具有相同内部状态的享元对象,享元模式中共享的仅仅是享元对象,外部状态是需要通过环境类设置的,在实际使用中,能共享的内部状态不是很多的,所以设计享元对象是比较小的,也就是细粒度对象,所以说享元模式就是通过共享技术实现大量细粒度对象的复用。
3.享元模式角色
**(1). 抽象享元角色(Flyweight):**是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
**(2).具体享元(Concrete Flyweight)角色:**实现抽象享元角色中所规定的接口。
**(3).非享元(Unsharable Flyweight)角色:**是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
**(4).享元工厂(Flyweight Factory)角色:**负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
4.代码实现方式
(1).创建一个抽象享元角色,通常是接口,定义自己的功能方法和设置外部状态的方法。
(2).创建具体享元角色,实现抽象享元角色,重写方法。
(3).创建享元工厂角色,通常用一个Map来做享元池,将创建的享元对象放在享元池中,key的话根据实际情况定义,然后根据key去享元池(Map)中找享元对象,如果存在则直接用,然后设置外部状态,不存在就创建一个,然后放入享元池,然后设置外部状态。
5.代码实现
(1).享元模式分为单纯享元模式和复合享元模式
(1-1).单纯享元模式:这种享元模式中的所有的具体享元类都是可以共享的,不存在非共享的具体享元类。
(1-2).复合享元模式:这种享元模式中的有些享元对象是由一些单纯享元对象组合而成的,它们就是复合享元对象。虽然复合享元对象本身不能共享,但它们可以分解成单纯享元对象再被共享。
(2).场景
比如我们去定火车票,火车票的起点和终点不会变的吧(内部状态),但是车票价格会根据座位的不同,车型的不同改变的(外部状态)。
(3).具体代码
(3-1).单纯享元模式

package com.tw.designPattern.flyweight.ticket;

/**
 * 抽象享元角色
 */
public interface Ticket {

    /**
     * 车票信息
     */
    void info();


    /**
     * 设置座位,
     * 座位(seat) 就是非享元角色
     * @param seat
     */
    void setSeat(String seat);
}

package com.tw.designPattern.flyweight.ticket;

public class ConcreteTicket implements Ticket {

    private String from;

    private String to;

    private String seat;


    public ConcreteTicket(String from, String to) {
        this.from = from;
        this.to = to;
    }

    @Override
    public void info() {
        System.out.println(this.from + "-->" + this.to + this.seat + "价格:" + this.getPrice(this.seat));
    }

    public Integer getPrice(String seat) {
        Integer price;
        switch (seat) {
            case "硬座":
                price = 100;
                break;
            case "商务座":
                price = 150;
                break;
            default:
                price = 50;
        }
        return price;
    }

    @Override
    public void setSeat(String seat) {
        this.seat = seat;
    }
}

package com.tw.designPattern.flyweight.ticket;

import java.util.HashMap;
import java.util.Map;

/**
 * 享元工厂
 */
public class TicketFactory {

    public static Map<String,Ticket> ticketMap = new HashMap<>();

    public static Ticket getTicket(String from,String to){
        String key = from + "-->" + to;
        if (ticketMap.containsKey(key)){
            System.out.println("从享元池中获取对象");
            return ticketMap.get(key);
        }
        System.out.println("没有从享元池中获取对象");
        Ticket ticket = new ConcreteTicket(from, to);
        ticketMap.put(key,ticket);
        return ticket;
    }
}

package com.tw.designPattern.flyweight.ticket;

public class TicketTest {

    public static void main(String[] args) {
        Ticket ticket = TicketFactory.getTicket("苏州", "上海");
        ticket.setSeat("硬座");
        ticket.info();
        Ticket ticket1 = TicketFactory.getTicket("苏州", "杭州");
        ticket1.setSeat("商务座");
        ticket1.info();
        Ticket ticket2 = TicketFactory.getTicket("苏州", "上海");
        ticket2.setSeat("商务座");
        ticket2.info();
    }
}

测试结果

没有从享元池中获取对象
苏州-->上海硬座价格:100
没有从享元池中获取对象
苏州-->杭州商务座价格:150
从享元池中获取对象
苏州-->上海商务座价格:150

(3-2).复合享元模式

package com.tw.designPattern.flyweight.compositeFlyweight;

/**
 * 抽象享元角色
 */
public interface Flyweight {

    /**
     * 一个示意性方法,参数state是外蕴状态,由外部传入
     * @param state
     */
    void operation(String state);
}

package com.tw.designPattern.flyweight.compositeFlyweight;

/**
 * 单纯享元对象(具体享元角色)
 */
public class ConcreteFlyweight implements Flyweight{

    private Character intrinsicState;

    public ConcreteFlyweight(Character intrinsicState){
        this.intrinsicState = intrinsicState;
    }


    /**
     * 外蕴状态作为参数传入方法中,改变方法行为
     * 但是并不改变方法的内蕴状态
     * @param state
     */
    @Override
    public void operation(String state) {
        System.out.println("内部状态 Intrinsic State = " + this.intrinsicState);
        System.out.println("外部状态 Extrinsic State = " + state);
    }
}

package com.tw.designPattern.flyweight.compositeFlyweight;

import java.util.HashMap;
import java.util.Map;

/**
 * 复合享元对象
 * 复合享元对象是由单纯享元对象通过复合而成的,因此它提供了add()这样的聚集管理方法。
 * 由于一个复合享元对象具有不同的聚集元素,这些聚集元素在复合享元对象被创建之后加入,
 * 这本身就意味着复合享元对象的状态是会改变的,因此复合享元对象是不能共享的。
 *
 * 复合享元角色实现了抽象享元角色所规定的接口,也就是operation()方法,这个方法有一个参数,
 * 代表复合享元对象的外蕴状态。一个复合享元对象的所有单纯享元对象元素的外蕴状态都是与复合享元对象的外蕴状态相等的;
 * 而一个复合享元对象所包含的所有的单纯享元对象的内蕴状态一般是不相等的,不然就没有了使用的价值。
 */
public class ConcreteCompositeFlyweight implements Flyweight{

    private Map<Character,Flyweight> flyweightMap = new HashMap<>();

    /**
     * 添加一个新的单纯享元对象到集合中
     * @param key
     * @param flyweight
     */
    public void add(Character key,Flyweight flyweight){
        flyweightMap.put(key,flyweight);
    }


    /**
     * 外蕴状态作为参数传递到方法中
     * @param state
     */
    @Override
    public void operation(String state) {
        Flyweight flyweight;
        for (Character key : flyweightMap.keySet()){
            flyweight = flyweightMap.get(key);
            flyweight.operation(state);
            System.out.println(flyweight);
        }
    }
}

package com.tw.designPattern.flyweight.compositeFlyweight;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 复合享元工厂
 *
 * 复合享元对象
 * 定义:
 *  这种享元模式中的有些享元对象是由一些单纯享元对象组合而成的,它们就是复合享元对象。虽然复合享元对象本身不能共享,但它们可以分解成单纯享元对象再被共享。
 * 结构:
 *  抽象享元角色(Flyweight):给出一个抽象接口,以规定出所有具体享元角色所需要实现的方法。
 *  具体享元角色(ConcreteFlyweight):实现抽象享元角色所规定的借口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。
 *  复合享元角色(ConcreteCompositeFlyweight):复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元对象又称作不可共享的享元对象。
 *  享元工厂角色(FlyweightFactory):本角色需要负责创建和管理角色。本角色必须保证享元对象可以被系统适当的共享。当一个客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有一个符合要求的享元对象。
 *  如果已经存在,则享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,则享元工厂角色就应当创建一个合适的享元对象。
 */
public class FlyweightFactory {

    private Map<Character, Flyweight> flyweightMap = new HashMap<>();

    /**
     * 复合享元工厂方法
     * @param compositeStateList
     * @return
     */
    public Flyweight factory(List<Character> compositeStateList){
        ConcreteCompositeFlyweight concreteCompositeFlyweight = new ConcreteCompositeFlyweight();
        for (Character state : compositeStateList){
            concreteCompositeFlyweight.add(state,this.factory(state));
        }
        return concreteCompositeFlyweight;
    }

    /**
     * 单纯享元工厂方法
     * @param state
     * @return
     */
    public Flyweight factory(Character state){
        // 先从已有缓存中查询对象是否存在
        Flyweight flyweight = flyweightMap.get(state);
        if (flyweight == null){
            // 如果对象不存在,则重新创建一个新的Flyweight对象
            flyweight = new ConcreteFlyweight(state);
            // 将新生成的对象放入到缓存中
            flyweightMap.put(state,flyweight);
        }
        return flyweight;
    }
}

package com.tw.designPattern.flyweight.compositeFlyweight;

import java.util.ArrayList;
import java.util.List;

public class CompositeFlyweightTest {

    /**
     * 运行结果解释:
     *  一个复合享元对象的所有单纯享元对象元素的外蕴状态都是与复合享元对象的外蕴状态相等,也就是上面例子中的Composite1 Call。
     *  一个复合享元对象所含有的单纯享元对象的内蕴状态一般是不想等的,也就是A、B、C。
     *  复合享元对象是不能共享的。也就是说,使用相同的对象compositeState通过享元工厂角色分别两次创建出的对象不是同一个对象。
     *  单纯享元对象是可以共享的。也就是相同的对象state通过享元工厂角色分别多次创建出的对象是同一个对象。
     * @param args
     */
    public static void main(String[] args) {
        List<Character> compositeStateList = new ArrayList<>();
        compositeStateList.add('A');
        compositeStateList.add('B');
        compositeStateList.add('C');
        compositeStateList.add('B');
        compositeStateList.add('A');

        FlyweightFactory flyweightFactory = new FlyweightFactory();
        Flyweight compositeFlyweight1 = flyweightFactory.factory(compositeStateList);
        Flyweight compositeFlyweight2 = flyweightFactory.factory(compositeStateList);
        compositeFlyweight1.operation("compositeFlyweight1 Call");
        compositeFlyweight1.operation("compositeFlyweight2 Call");
        System.out.println("------------------------------------------");
        System.out.println("复合享元对象是否可以共享对象:" + (compositeFlyweight1 == compositeFlyweight2));
        System.out.println(compositeFlyweight1);
        System.out.println(compositeFlyweight2);

        Character state = 'A';
        Flyweight flyweight1 = flyweightFactory.factory(state);
        Flyweight flyweight2 = flyweightFactory.factory(state);
        System.out.println("单纯享元模式是否可以共享对象:" + (flyweight1 == flyweight2));
    }
}

测试结果

内部状态 Intrinsic State = A
外部状态 Extrinsic State = compositeFlyweight1 Call
com.tw.designPattern.flyweight.compositeFlyweight.ConcreteFlyweight@6a6824be
内部状态 Intrinsic State = B
外部状态 Extrinsic State = compositeFlyweight1 Call
com.tw.designPattern.flyweight.compositeFlyweight.ConcreteFlyweight@5c8da962
内部状态 Intrinsic State = C
外部状态 Extrinsic State = compositeFlyweight1 Call
com.tw.designPattern.flyweight.compositeFlyweight.ConcreteFlyweight@512ddf17
内部状态 Intrinsic State = A
外部状态 Extrinsic State = compositeFlyweight2 Call
com.tw.designPattern.flyweight.compositeFlyweight.ConcreteFlyweight@6a6824be
内部状态 Intrinsic State = B
外部状态 Extrinsic State = compositeFlyweight2 Call
com.tw.designPattern.flyweight.compositeFlyweight.ConcreteFlyweight@5c8da962
内部状态 Intrinsic State = C
外部状态 Extrinsic State = compositeFlyweight2 Call
com.tw.designPattern.flyweight.compositeFlyweight.ConcreteFlyweight@512ddf17
------------------------------------------
复合享元对象是否可以共享对象:false
com.tw.designPattern.flyweight.compositeFlyweight.ConcreteCompositeFlyweight@2c13da15
com.tw.designPattern.flyweight.compositeFlyweight.ConcreteCompositeFlyweight@77556fd
单纯享元模式是否可以共享对象:true

6.优缺点
(1).优点

(1-1).减少对象的创建,降低了系统中对象的数量,所以可以减低系统的使用内存,提高效率。
(2).缺点
(2-1).提高了系统的复杂度,需要注意分离出来的外部状态和内部状态。
(2-2).线程安全问题,我这里举的例子都比较简单,实际开发中业务肯定比这复杂多了,到时候要考虑好线程并发带来的安全问题。
7.应用场景
(1).系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源。
(2).大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。
(3). 由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。
8.Jdk中的享元模式
(1).String类(这个一句话解释不清,反正是从常量池里面拿的,挺多东西的,感兴趣可以看看)
(2).Integer类(在-128~127之间的值是从缓存拿的)

package com.tw.designPattern.flyweight.ticket;

public class TicketTest {

    public static void main(String[] args) {
        // 在-128~127之间的会从缓存中拿
        Integer a = Integer.valueOf("12");
        Integer b = 12;
        System.out.println(a == b);
        // 不在-128~128之间的创建新对象
        Integer c = Integer.valueOf("128");
        Integer d = 128;
        System.out.println(c == d);
    }
}

测试结果

true
false
**注:内容参考网络上各种资料,还有一些本人的理解和思想,仅为了学习记录和分享一下自己所学之处,如有不足的地方麻烦大牛指出,如有侵权的地方,请联系删除,谢谢。**
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值