设计模式结构型——享元模式

目录

什么是享元模式

享元模式的实现

享元模式角色

享元模式类图

享元模式代码实现

享元模式的特点

优点

缺点

使用场景

注意事项


什么是享元模式

        享元模式(Flyweight Pattern)是一种结构型设计模式,享元模式中的“享元”指被共享的单元,享元模式通过复用对象,以达到节省内存的目的。要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式。其主要解决的问题是创建大量相似对象时的内存开销过大。

        其意图运用共享技术有效地支持大量细粒度的对象。解决在有大量对象时,有可能会造成内存溢出问题,把其中共同的部分抽象出来,查找时直接返回在内存中已有的对象,没有才重新创建,避免重新创建已有对象。减少创建对象的数量,以减少内存占用和提高性能,避免大量相似对象的开销,从而提高系统资源的利用率,尽可能复用现有的同类对象。

        “享元”,被共享的单元,即复用对象,节省内存,注意前提是享元对象是不可变对象。

        当一个系统中存在大量重复不可变对象,就能利用享元模式将对象设计成享元,在内存中只保留一份实例,供引用。这就减少内存中对象的数量,最终节省内存。当然,不仅相同对象可设计成享元,相似对象,也能提取对象中的相同部分(字段)设计成享元。

        “不可变对象”:一旦通过构造器初始化完成后,其状态(对象的成员变量或属性)就不会再被修改。所以,不可变对象不能暴露任何set()等修改内部状态的方法。之所以要求享元是不可变对象,是因为它会被多处代码共享使用,避免一处代码对享元进行了修改,影响到其他使用它的代码。

享元模式的实现

享元(Flyweight )模式中存在以下两种状态:

1. 内部状态,即不会随着环境的改变而改变的可共享部分。

2. 外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。

享元模式角色

  1. 抽象享元角色(Flyweight):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公 共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。
  2. 具体享元角色(Concrete Flyweight):实现了抽象享元类,称为享元对象;在具体享元 类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
  3. 非享元角色(Unsharable Flyweight) :并非所有的抽象享元类的子类都需要被共享,不 能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
  4. 享元工厂角色(Flyweight Factory):负责创建和管理享元角色。当客户对象请求一个享元 对象时,享元工厂检査系统中是否存在符合要求的享元对象,存在则提供给客户,不存在新创建提供给客户。

享元模式类图

享元模式代码实现

抽象享元角色

package com.common.demo.pattern.flyweight;

/**
 * @author Evan Walker 昂焱数据: https://www.ayshuju.com
 * @version 1.0
 * @desc 抽象享元角色 篮球
 * @date 2023/07/21 15:03:57
 */
public interface BasketBall {

    void play();
}

具体享元角色和非享元角色

package com.common.demo.pattern.flyweight;

/**
 * @author Evan Walker 昂焱数据: https://www.ayshuju.com
 * @version 1.0
 * @desc 具体享元角色 体育馆
 * @date 2023/07/21 15:05:37
 */
public class Gymnasium implements BasketBall{

    private String gymnasiumName;

    private String sport = "篮球";

    double price = 50;

    public Gymnasium() {
    }

    public Gymnasium(String gymnasiumName) {
        this.gymnasiumName = gymnasiumName;
    }

    public String getGymnasiumName() {
        return gymnasiumName;
    }

    public void setGymnasiumName(String gymnasiumName) {
        this.gymnasiumName = gymnasiumName;
    }

    public String getSport() {
        return sport;
    }

    public void setSport(String sport) {
        this.sport = sport;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public void play() {
        System.out.println("Gymnasium{" + "gymnasiumName='" + gymnasiumName + '\'' +
                ", sport='" + sport + '\'' + ", price=" + price + '}'
                +", this Object='" + this + '\'' );
    }

}

享元工厂角色

package com.common.demo.pattern.flyweight;

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

/**
 * @author Evan Walker 昂焱数据: https://www.ayshuju.com
 * @version 1.0
 * @desc 享元工厂 篮球工厂
 * @date 2023/07/21 15:11:37
 */
public class BasketBallFactory {

    private static Map<String,Gymnasium> MAP = new HashMap<>();

    public static Gymnasium getBasketBall(String gymnasiumName){
        Gymnasium gymnasium = MAP.get(gymnasiumName);
        if(Objects.isNull(gymnasium)){
            gymnasium = new Gymnasium(gymnasiumName);
            MAP.put(gymnasiumName,gymnasium);
        }
        return gymnasium;
    }
}

测试类

package com.common.demo.pattern.flyweight;

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

/**
 * @author Evan Walker 昂焱数据: https://www.ayshuju.com
 * @version 1.0
 * @desc
 * @date 2023/07/21 15:16:00
 */
public class Test {

    private static final List<String> nameList = new ArrayList<>();

    public static void main(String[] args) {
        nameList.add("斯塔普斯中心球馆");
        nameList.add("大通中心球馆");
        nameList.add("麦迪逊花园球馆");
        nameList.add("联合中心球馆");
        nameList.add("速贷中心球馆");
        nameList.add("斯塔普斯中心球馆");
        nameList.add("速贷中心球馆");
        nameList.add("美航球馆");
        nameList.add("丹佛百事中心球馆");

        for (String name : nameList){
            BasketBallFactory.getBasketBall(name).play();
        }
    }
}

测试截图

享元模式的特点

优点

  1. 大大减少对象的创建,对象复用,降低系统的内存,使效率提高。

缺点

  1. 提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成线程安全问题。
  2. 需要牺牲一定的时间和空间,来实现对象共享和控制机制。当对象之间没有复用性时,使用享元模式可能会导致额外的开销。

使用场景

  1. 系统有大量相似对象。
  2. 当对象需要被共享时,如需要缓冲池的场景。
  3. 当系统的内存资源相对有限时可以考虑使用享元模式,以减少内存的使用。
  4. 当需要减少对象的创建次数、降低系统开销时。

注意事项

  1. 注意划分外部状态和内部状态,否则可能会引起线程安全问题。
  2. 这些类必须有一个工厂对象加以控制。

更多消息资讯,请访问昂焱数据(https://www.ayshuju.com)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值