Java设计模式学习记录-享元模式 Java设计模式学习记录-GoF设计模式概述

前言

享元模式也是一种结构型模式,这篇是介绍结构型模式的最后一篇了(由于代理模式很早以前就已经写过了)。享元模式采用一个共享来避免大量拥有相同内容对象的开销。这种开销最多见、最直观的就是内存损耗。html

享元模式

定义

享元模式是指运用共享技术有效的支持大量细粒度对象的复用。系统只使用少许的对象,而这些对象都很类似,状态变化很小,能够实现对象的屡次复用。因为享元模式要求可以共享的对象必须是细粒度对象,所以它又称为轻量级模式,它是一种对象结构型模式。java

举例

咖啡问题,在一家咖啡店里有几种口味的咖啡,例如:拿铁、摩卡、卡布奇诺等等。最近这家店在搞促销活动,一中午能卖出几百杯咖啡,那么咖啡的口味就是一种共享的元素。下面用代码来实现一下这个例子。面试

定义订单接口设计模式

/**
 * 订单接口
 */
public interface Order {

    //卖出咖啡
    void sell();

}

具体的订单实现ide

public class CoffeeOrder implements Order {

    //咖啡口味
    public String flavor;

    public CoffeeOrder(String flavor){
        this.flavor = flavor;
    }

    @Override
    public void sell() {
        System.out.println("卖出了一份"+flavor+"的咖啡。");
    }
}

订单工厂类post

import com.google.common.collect.Maps;
import java.util.Map;
import java.util.Objects;

/**
 * 订单工厂类
 */
public class CoffeeOrderFactory {

    private static Map<String,Order> cof = Maps.newHashMap();

    /**
     * 得到订单
     * @param flavor 口味
     * @return
     */
    public static Order getOrder(String flavor){

        Order order = cof.get(flavor);

        if(Objects.isNull(order)){
            order = new CoffeeOrder(flavor);
            cof.put(flavor,order);
        }
        return order;
    }

    /**
     * 获取最终建立的对象个数
     * @return
     */
    public static int getSize(){

        return cof.size();
    }

}

测试类性能

import org.assertj.core.util.Lists;
import java.util.List;

/**
 * 测试买咖啡
 */
public class MyTest {

    public static void main(String[] args) {


        buyCoffee("拿铁");
        buyCoffee("卡布奇诺");
        buyCoffee("摩卡");

        buyCoffee("拿铁");
        buyCoffee("拿铁");
        buyCoffee("拿铁");

        buyCoffee("卡布奇诺");
        buyCoffee("卡布奇诺");
        buyCoffee("卡布奇诺");

        buyCoffee("摩卡");
        buyCoffee("摩卡");
        buyCoffee("摩卡");

        //打印出卖出的咖啡
        coffeeOrderList.stream().forEach(Order::sell);


        System.out.println("一共卖出去"+coffeeOrderList.size()+"杯咖啡!");
        System.out.println("一共生成了"+CoffeeOrderFactory.getSize()+"个Java对象!");


    }
    //订单列表
    public static List<Order> coffeeOrderList = Lists.newArrayList();

    /**
     * 买咖啡
     * @param flavor 口味
     */
    public static void buyCoffee(String flavor){
        coffeeOrderList.add(CoffeeOrderFactory.getOrder(flavor));
    }

}

运行结果学习

卖出了一份拿铁的咖啡。
卖出了一份卡布奇诺的咖啡。
卖出了一份摩卡的咖啡。
卖出了一份拿铁的咖啡。
卖出了一份拿铁的咖啡。
卖出了一份拿铁的咖啡。
卖出了一份卡布奇诺的咖啡。
卖出了一份卡布奇诺的咖啡。
卖出了一份卡布奇诺的咖啡。
卖出了一份摩卡的咖啡。
卖出了一份摩卡的咖啡。
卖出了一份摩卡的咖啡。
一共卖出去12杯咖啡!
一共生成了3个Java对象!

从上面的运行结果能够看出来,虽然卖出去了12杯咖啡,可是最终的口味对象只有3个,由于咖啡口味只有在第一次使用的时候建立,后面就直接使用不会再建立了。测试

享元模式的分析

下面仍是来分析一下享元模式的结构吧,结构图以下:this

 

 

享元模式涉及到的角色有抽象享元角色、具体享元角色、复合享元角色、享元工厂角色,以及客户端角色。具体说明以下:

  • 抽象享元角色(FlyWeight):此角色是全部具体享元类的父级,为这些类规定出须要实现的公共接口或抽象类。上面例子中的Order接口就是表明的这个角色。
  • 具体享元角色(ConcreteFlyweight):实现抽象享元角色所规定的接口。有时候具体享元角色有称为单纯具体享元角色,由于复合享元角色是由单纯具体享元角色经过复合而成的。上面例子中的CoffeeOrder就是表明的这个角色。
  • 复合享元角色(UnsharableFlyweight):复合享元角色所表明的对象是不能够共享的,可是一个复合享元对象能够分解成为多个自己是单纯享元对象的组合。复合对象又称为不可共享对象。这个角色通常不多用。 
  • 享元工厂角色(FlyweightFactory):负责建立和管理享元角色。此角色必须保证享元对象能够被系统适当地共享。当客户端对象请求一个享元对象时,享元工厂角色须要检查系统中是否已经有一个符合要求的享元对象,若是已经有了享元工厂角色就应当提供这个已有的享元对象;若是系统中没有一个适当的享元对象的话,享元工厂角色就应当建立一个新的合适的享元对象。上面例子中的CoffeeOrderFactory就是表明的这个角色。
  • 客户端角色(client):此角色调用享元工厂角色来使用具体享元角色。

享元模式总结

单纯享元模式和复合享元模式

标准的享元模式中既包含享元对象又包含非享元对象,可是在实际使用过程当中咱们会用到具体两种特殊形式的享元模式:单纯享元模式和复合享元模式。

单纯享元模式是指,全部的具体享元对象都是能够共享的,不包括非享元对象。

复合享元模式是指,将一些单纯享元对象使用组合模式加以组合,还能够造成组合享元对象,这样的复合享元对象不能共享,可是它能够分解成单纯享元对象,分解后就能够共享了。

享元模式的优势

一、能够极大的减小内存中对象的数量,使得相同或类似的对象在内存中只保存一份,从而能够节约系统资源,提升系统性能。

二、享元模式的外部状态相对独立,并且不会影响其内部状态,从而使得享元对象能够在不一样的环境中被共享。

享元模式的缺点

一、享元模式使得系统变得复杂,须要分离出内部状态和外部状态,这使得程序的逻辑复杂化。

二、为了使对象能够共享,享元模式须要将享元对象的部分状态外部化,而读取外部状态将使得运行时间变长。

适用场景

一个系统有大量相同或者类似的对象,形成内存的大量耗费。

对象的大部分状态均可之外部化,能够将这些外部状态传入对象中。

在使用享元模式时须要维护一个存储享元对象的享元池,而这须要耗费必定的系统资源,所以,应当在须要屡次重复使用享元对象时才值得使用享元模式。

 延伸

在JDK中就有使用享元模式的例子,最多见的就是咱们使用的String类,你们都知道String类是被final修饰的,因此不会被继承,每次变动都会生成一个新的字符串,这样就有点占内存了。因此若是直接写出了一个字符串,当后面又写出了一个一样的字符串时会自动去堆中(JDK7以上)查看是否已经存在这个字符串了,若是已经存在则直接使用,若是不存在这个时候才在堆中再给开辟一块空间存储字符串。

例以下面的例子:

        String testStr = "享元模式";
        String testString = "享元模式";

        System.out.println(testStr == testString);
        System.out.println(testStr.equals(testString));    

运行结果:

true
true

 

 


想了解更多的设计模式请查看Java设计模式学习记录-GoF设计模式概述

 

 

 

 

 

 

 

 

 

面试面到怀疑人生,继续加油吧!

相关文章
相关标签/搜索