设计模式---享元模式

一、概念

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

上面的概念乍一听好像单例模式其实不是,单例模式只保存一个对象,但是这里可以有很多个不同对象,但是每个对象只有一个实例而已。也就是说享元模式使用了单例模式。

例子解释

张三去借书,然后阅读完了还回去了,过一段时间发现还是不懂,又去借了同样的书,但是这本书其实和上一次借的书是同一本。李四也去借书,发现书架上没有,就去图书管理员那边拿出来了一本全新的书。这就是享元模式。

享元模式的主要角色由享元工厂、抽象享元、具体享元类几部分组成。

我们使用这个例子以类图的角度来观察一下:

从上面这个例子我们可以看到,这里其实有三个角色:

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

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

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

在这里享元工厂是享元模式的核心,它需要确保系统可以共享相同的对象。它会维护一个对象列表,当我们想要获取享元类时,如果请求的享元类已经被创建,则直接返回已有的享元类:若没有,则创建一个新的享元对象,并将它加入到维护队列中。

二、代码实现

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

public abstract class Book {

    //外部状态
    public String name;

    public Book(String name){
        this.name = name;
    }
    /**
     * 借书动作
     */
    public abstract void borrow();
}

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

public class ConcreteBook extends Book {


    //被借出的书名
    public ConcreteBook(String name){
        super(name);
    }

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

第三步:享元工厂(Llibrary)

public class Library {

    //图书馆维护一个图书列表
    private Map<String,Book> bookPools = new HashMap<>();

    private static Library factory = new Library();
    //图书馆只有一个
    public static Library getInstance(){
        return factory;
    }

    //图书馆外借图书
    public Book libToBorrow(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 getAllBookSize(){
        return bookPools.size();
    }
}

第四步:模拟学生去借书

public class Student {

    //图书馆书架上的书
    private static List<Book> books = new ArrayList<Book>();
    private static Library library;

    public static void main(String[] args) {

        library = Library.getInstance();
        studentBorrow("java编程思想");
        studentBorrow("java核心卷一");
        studentBorrow("java从入门到精通");
        //把每一本书借出去
        for (Book book : books) {
            book.borrow();
        }
        System.out.println("后两本没学会,又借了一次 ");
        studentBorrow("java核心卷一");
        studentBorrow("java从入门到精通");
        Book java核心卷一 = library.libToBorrow("java核心卷一");
        java核心卷一.borrow();
        Book java从入门到精通 = library.libToBorrow("java从入门到精通");
        java从入门到精通.borrow();

        //输出一些学生一共借多少本书
        System.out.println("学生一共借了 " + books.size() + " 本书! ");
        //输出一下图书馆一共借出多少本书
        System.out.println("图书馆实际借出" + library.getAllBookSize() + " 本书");
    }


    private static void studentBorrow(String bookName){
        books.add(library.libToBorrow(bookName));
    }

}

在上面其实学生一共借了5次书,但是有两本是重复的,所以对于图书馆来说,其实是借出去了三本书。

输出结果:

借了一本书名为:java编程思想

借了一本书名为:java核心卷一

借了一本书名为:java从入门到精通

后两本没学会,又借了一次 

借了一本书名为:java核心卷一

借了一本书名为:java从入门到精通

学生一共借了 5 本书! 

图书馆实际借出3 本书

三、享元模式在java中的体现

1.String中的intern()方法

public static void main(String[] args) {
        String str1 = "和谐";
        String str2 = "社会";
        String str3 = "和谐社会";
        String str4;
        str4 = str1 + str2;
        System.out.println(str3 == str4);
//        如果是String的对象池中有该类型的值,则直接返回对象池中的对象
        str4 = (str1 + str2).intern();
        System.out.println(str3 == str4);
}

输出结果:false true

2.Integer中的vauleOf()方法

在范围[-128, 127]内在Integer缓存中直接取,范围外通过new Integer(i) 生成

            //Integer中的缓存数组 cache
			cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

			public static Integer valueOf(int i) {
                if (i >= IntegerCache.low && i <= IntegerCache.high)
                    return IntegerCache.cache[i + (-IntegerCache.low)];
                return new Integer(i);
       	 }
        Integer i1 = Integer.valueOf(99);
        Integer i2 = Integer.valueOf(99);
        System.out.println(i1==i2);
        Integer i3 = Integer.valueOf(128);
        Integer i4 = Integer.valueOf(128);
        System.out.println(i3==i4);

输出结果:true false

四、享元模式特点

1、优点

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

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

2、缺点

提高了系统复杂性,需要分离出外部状态(k)和内部状态(v),而且外部状态具有固化特性,不应该随内部状态改变而改变,否则导致系统的逻辑混乱,为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。

● 内部状态

内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变,它们可以作为一个对象的动态附加信息,不必直接储存在具体某个对象中,属于可以共享的部分。

● 外部状态

外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态,它是一批对象的统一标识,是唯一的一个索引值。

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

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

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

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java设计模式是一组经过实践验证的面向对象设计原则和模式,可以帮助开发人员解决常见的软件设计问题。下面是常见的23种设计模式: 1. 创建型模式(Creational Patterns): - 工厂方法模式(Factory Method Pattern) - 抽象工厂模式(Abstract Factory Pattern) - 单例模式(Singleton Pattern) - 原型模式(Prototype Pattern) - 建造者模式(Builder Pattern) 2. 结构型模式(Structural Patterns): - 适配器模式(Adapter Pattern) - 桥接模式(Bridge Pattern) - 组合模式(Composite Pattern) - 装饰器模式(Decorator Pattern) - 外观模式(Facade Pattern) - 享元模式(Flyweight Pattern) - 代理模式(Proxy Pattern) 3. 行为型模式(Behavioral Patterns): - 责任链模式(Chain of Responsibility Pattern) - 命令模式(Command Pattern) - 解释器模式(Interpreter Pattern) - 迭代器模式(Iterator Pattern) - 中介者模式(Mediator Pattern) - 备忘录模式(Memento Pattern) - 观察者模式(Observer Pattern) - 状态模式(State Pattern) - 策略模式(Strategy Pattern) - 模板方法模式(Template Method Pattern) - 访问者模式(Visitor Pattern) 4. 并发型模式(Concurrency Patterns): - 保护性暂停模式(Guarded Suspension Pattern) - 生产者-消费者模式(Producer-Consumer Pattern) - 读写锁模式(Read-Write Lock Pattern) - 信号量模式(Semaphore Pattern) - 线程池模式(Thread Pool Pattern) 这些设计模式可以根据问题的特点和需求来选择使用,它们提供了一些可复用的解决方案,有助于开发高质量、可维护且易于扩展的软件系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值