JAVA设计模式之享元模式

一、概述

享元模式:“享”就是分享之意,指一物被众人共享,而这也正是该模式的终旨所在。
享元模式有点类似于单例模式,都是只生成一个对象来被共享使用。

享元的目的是为了减少不会要额内存消耗,将多个对同一对象的访问集中起来,不必为每个访问者创建一个单独的对象,以此来降低内存的消耗。
这里有个问题,那就是对共享对象的修改,为了避免出现这种情况,我们将这些对象的公共部分,或者说是不变化的部分抽取出来形成一个对象。这个对象就可以避免到修改的问题。

二、享元模式结构图

在这里插入图片描述

因为享元模式结构比较复杂,一般结合工厂模式一起使用,在它的结构图中包含了一个享元工厂类。

在享元模式结构图中包含如下几个角色:

1、Flyweight(抽象享元类):定义需要共享的对象业务接口。享元类被创建出来总是为了实现某些特定的业务逻辑.

2、ConcreteFlyweight(具体享元类):实现抽象享元类的接口,完成某一具体逻辑。在这里表示可以被借出。

3、FlyweightFactory(享元工厂类):于创建具体享元类,维护相同的享元对象。当请求对象已经存在时,直接返回对象,不存在时,在创建对象。
在例子中的解释就是图书馆,保存了所有的书,当学生借书时,有就拿走,没有买一本新书。这里面其实是使用了单例模式的。

4、UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。

三、享元模式的实现

在享元模式中引入了享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,当用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。

接下来,实现一个图书馆借书的享元模式。

1、模型图
在这里插入图片描述

2、代码实现

第一步:学生用户类

/**
 * 学生
 */
public class Student {
    private String name;

    public Student(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

第二步:定义抽象享元类(Book)

/**
 * 借书 -- 抽象享元类
 */
public interface Book {
    /**
     * 借书--享元类公共方法
     * @param student
     */
    void borrow(Student student);
}

第三步:定义具体享元类(ConcreteBook)

/**
 * 具体享元类
 */
public class ConcreteBook implements Book {
    //书名
    private String bookName;

    public ConcreteBook(String bookName){
        this.bookName = bookName;
    }

    @Override
    public void borrow(Student student) {
        System.out.println(student.getName()+"---- 他借走了这本书是 :"+this.bookName);
    }
}

第四部步:享元工厂(Llibrary)

/**
 * 享元工厂类
 */
public class Library {

    // map充当对象享元池  图书馆维护一个图书列表
    private static Map<String,Book> bookMap = new HashMap<>();

    //图书馆外借书
    public static Book getBook(String bookName){
        // 从享元池中拿  书本
        Book concreteBook = bookMap.get(bookName);
        // 如果享元池中没有此对象 书
        if(concreteBook == null ){
            // 创建对象 书
            concreteBook = new ConcreteBook(bookName);
            // 存到享元池中
            bookMap.put(bookName,concreteBook);
        }
        // 返回对象 书
        return concreteBook;
    }

    public static int getSize(){
        return bookMap.size();
    }
}

第五步:模拟学生去借书

/**
 * 设计模式 结构型模式 之 享元模式
 * 测试类
 */
public class MainTest {
    public static void main(String[] args) {
        Book book1 = Library.getBook("JAVA基础");
        book1.borrow(new Student("小明"));
        Book book3 = Library.getBook("JAVA高并发");
        book3.borrow(new Student("小红"));

        System.out.println("-----书籍还回图书馆后");

        Book book2 = Library.getBook("JAVA基础");
        book2.borrow(new Student("小黑"));

        // 测试是否是同一个对象
        System.out.println("小明和小黑借的书是否为同一本书:" + (book1 == book2));
        // 工厂类中享元池中对象数量
        System.out.println("图书馆时间借出了 :" + Library.getSize()+ " 本书");
    }
}

运行结果:

小明---- 他借走了这本书是 :JAVA基础
小红---- 他借走了这本书是 :JAVA高并发
-----书籍还回图书馆后
小黑---- 他借走了这本书是 :JAVA基础
小明和小黑借的书是否为同一本书:true
图书馆时间借出了 :2 本书

四、总结

从上面代码和运行结果这可以看到,不同学生借书事是 “享” 用同一本书对象。在享元对象池中只有两个对象。

1、享元模式优点
(1)节省内存空间,对于可重复的对象只会被创建一次,对象比较多时,就会极大的节省空间。
(2)提高效率,由于创建对象的数量减少,所以对系统内存的需求也减小,使得速度更快,效率更高。

2、享元模式缺点
外部状态由客户端保存,共享对象读取外部状态的开销可能比较大

享元模式要求将内部状态与外部状态分离,这使得程序的逻辑复杂化,同时也增加了状态维护成本

其实对于享元类有内部状态和外部状态,其区分就是图书馆的书一部分可以外借(外部状态),一部分不可外借(内部状态),两个状态的划分对于书籍管理来说有点复杂化了。

3、享元模式与单例模式的区别
(1)享元设计模式是一个类有很多对象,而单例是一个类仅一个对象。

(2)享元模式是为了节约内存空间,提升程序性能,而单例模式则主要是出于共享状态的目的。

OK,以上就是享元模式,在使用的时候只需要记住享元模式的核心思想,然后根据自己的业务需求来选择,因为大大多情况下都不会使用一种设计模式,而是多种设计模式的组合。如有问题还请批评指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值