设计模式之享元模式

享元模式

举例分析1:

展示网站项目需求
小型的外包项目,给客户A做一个产品展示网站,客户A的朋友感觉效果不错,也希
望做这样的产品展示网站,但是要求都有些不同:

  1. 有客户要求以新闻的形式发布
  2. 有客户人要求以博客的形式发布
  3. 有客户希望以微信公众号的形式发布
举例分析2:
在大学时代,估计每个人都去图书馆借过书。借书的流程很简单,如果书架上有这本书直接拿走,到借阅机上借阅就好了,如果没有,可以到图书管理处去拿一本新书。对于整个图书馆来说,书其实就是共享的,但是我们会发现其实每次借的书都是那些破旧一点的书,而不是新书,这是因为学生太多了,如果我们每一次借书都拿出来一本新书,那整个图书馆估计会放不下,对于我们借书的流程和图书共享的方式就是享元模式。

============================================================

传统方案解决网站展现项目

  1. 直接复制粘贴一份,然后根据客户不同要求,进行定制修改
  2. 给每个网站租用一个空间
  3. 方案设计示意图

在这里插入图片描述

传统方案解决网站展现项目-问题分析
  1. 需要的网站结构相似度很高,而且都不是高访问量网站,如果分成多个虚拟空间来
    处理,相当于一个相同网站的实例对象很多,造成服务器的资源浪费
  2. 解决思路:整合到一个网站中,共享其相关的代码和数据,对于硬盘、内存、CPU、
    数据库空间等服务器资源都可以达成共享,减少服务器资源
  3. 对于代码来说,由于是一份实例,维护和扩展都更加容易
  4. 上面的解决思路就可以使用 享元模式 来解决
基本介绍

如果在一个系统中存在多个相同的对象,那么只需要共享一份对象的拷贝,而不必为每一次使用都创建新的对象。目的是提高系统性能。

  1. 享元模式(Flyweight Pattern) 也叫 蝇量模式: 运
    用共享技术有效地支持大量细粒度的对象
  2. 常用于系统底层开发,解决系统的性能问题。像
    数据库连接池,里面都是创建好的连接对象,在
    这些连接对象中有我们需要的则直接拿来用,避
    免重新创建,如果没有我们需要的,则创建一个
    3) 享元模式能够解决重复对象的内存浪费的问题
    当系统中有大量相似对象,需要缓冲池时。不需
    总是创建新对象,可以从缓冲池里拿。这样可以
    降低系统内存,同时提高效率
  3. 享元模式经典的应用场景就是池技术了,String常
    量池、数据库连接池、缓冲池
    等等都是享元模式
    的应用,享元模式是池技术的重要实现方式
享元模式的原理类图

在这里插入图片描述

1)享元工厂(Llibrary):用于创建具体享元类,维护相同的享元对象。当请求对象已经存在时,直接返回对象,不存在时,在创建对象。在例子中的解释就是图书馆,保存了所有的书,当学生借书时,有就拿走,没有买一本新书。这里面其实是使用了单例模式的。
2) FlyWeight 抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态和内部状态(后面介绍) 的接口或实现
3) ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
4) UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂

代码编写:

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

/**
* 定义抽象享元类(Book)
*
* 抽象享元(Book):定义需要共享的对象业务接口。享元类被创建出来总是为了实现某些特定的业务逻辑.
*/
public interface Book {
    public void borrow();
}

ConcreteFlyWeight 实现抽象享元类的接口,完成某一具体逻辑。

/**
 * @author 孙一鸣 on 2020/2/13
 * 具体享元(ConcreteBook):实现抽象享元类的接口,完成某一具体逻辑。在这里表示可以被借出。
 *
 */
public class ConcreteBook  implements Book{
    //被借出的书名
    public  String name;


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

    @Override
    public void borrow() {
        System.out.println("图书馆借出一本书,书名为:"+this.name);
    }
}

享元工厂(Llibrary)图书馆,保存了所有的书,当学生借书时,有就拿走,没有买一本新书。


/**
 * 享元工厂
 *
 * @author 孙一鸣 on 2020/2/13
 */
public class Library {
//图书馆维护一个图书列表
    private Map<String,Book> bookPools = new HashMap<String, Book>();
    private static  Library libraryFactory = new Library();

    //图书馆只有一个
    public static Library getInstance(){
        return libraryFactory;
    }

    //图书馆外借图书
    public Book libTo(String bookname){
        Book order = null;
        //如果书架 有,取出这本书
        if(bookPools.containsKey(bookname)){
            order = bookPools.get(bookname);
        }
        else {
            order = new ConcreteBook(bookname);
            bookPools.put(bookname,order);
        }
        return order;
    }

    //图书馆书架上书的数量
    public int getAllBooks(){
        return bookPools.size();
    }
}


学生类:


/**
 * @author 孙一鸣 on 2020/2/13
 */
public class Student {
    private  static List<Book> books =new ArrayList<>();
    private static  Library library;

    private static void studentBorrow(String name){
        books.add(library.libTo(name));
    }

    public static void main(String[] args) {
        library = Library.getInstance();
        studentBorrow("JAVA编程思想");
        studentBorrow("JAVA核心卷一");
        studentBorrow("JAVA从入门到精通");
        System.out.println("后两本没学会,又借一次");
        studentBorrow("JAVA核心卷一");
        studentBorrow("JAVA从入门到精通");

        //把每一本书借出去
        for (Book book: books){
            book.borrow();
        }
        System.out.println("学生一共借了"+books.size());
        System.out.println("图书馆实际借出"+library.getAllBooks());
    }
}
结果截图:

在这里插入图片描述

内部状态和外部状态

比如围棋、五子棋、跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色多一
点,所以棋子颜色就是棋子的内部状态;而各个棋子之间的差别就是位置的不同,当我们落子后,
落子颜色是定的,但位置是变化的,所以棋子座标就是棋子的外部状态

  1. 享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态
    了,即将对象的信息分为两个部分:内部状态和外部状态
  2. 内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
  3. 外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。
  4. 举个例子:围棋理论上有361个空位可以放棋子,每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,这样就很好的解决了对象的开销问题
享元模式的注意事项和细节
  1. 在享元模式这样理解,“享”就表示共享,“元”表示对象
  2. 系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
  3. 唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用
    HashMap/HashTable存储
  4. 享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
  5. 享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方.
  6. 使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。
  7. 享元模式经典的应用场景是需要缓冲池的场景,比如 String常量池、数据库连接池

享元模式的优点

(1)节省内存空间,对于可重复的对象只会被创建一次,对象比较多时,就会极大的节省空间。

(2)提高效率,由于创建对象的数量减少,所以对系统内存的需求也减小,使得速度更快,效率更高。

享元模式的缺点

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

享元模式与单例模式的区别

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章