结构型模式之享元模式(Flyweight)

享元模式的作用在于节省内存开销,对于系统内存在的大量相似的对象,通过享元的设计方式,可以提取出可共享的部分,将其只保留一份进而节省大量的内存开销。

不是所有的对象都适合进行享元设计,它要求对象具有可共享的特征,这些可共享的特征可以做享元设计,对象的可共享特征比例越大,进行享元设计后节省的内存越多。

注意,对象的不可共享特征不能计入享元设计,所以需要仔细判断区分对象的可共享特征与不可共享特征。


外部特征,不可共享特征,保证调用过程不会影响下次调用。

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

游戏开发中的享元设计

一次炮弹的发射过程有这些特征:炮弹外观图片、飞行轨迹、爆炸效果、伤害值等,炮弹是被频繁发射的,如果每发射一个炮弹都创建一个对象,那么喜欢散弹枪的玩家会创建数不清的炮弹对象,这样就会给系统造成很大内存压力。

其实仔细分析一下,炮弹虽然发射了很多,但其实他们都可以共享一个对象,就是炮弹对象本身(包括外观图片、特效图片、爆炸效果等,这些都是可共享特征),不同的是炮弹的运行轨迹(不可共享特征)而已,轨迹是个很小的对象或方法的参数而已不会对系统造成负担,这样的享元设计就可以大量节省内存。

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


场景:一家饮品小厅,可以提供咖啡、茶、水3种饮料,客人会选择坐几号桌子并点一杯饮品,之后饮品为客人服务(即被慢慢地喝掉)。

分析:在现实生活中,每个客人会选择一杯饮品,每个人的饮品肯定不是同一杯,但是在程序运行过程中,完全可以使用同一个对象来实现,饮品本身存在的可共享特征(如成分、毫升、冷热等)都可计入到享元设计中,饮品要服务的对象(如几号桌哪个人)属于不可共享特征不能计入享元设计。


设计:

示例代码:

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

class ServiceContext {
    private int tableIndex;
    private String customerName;
    public ServiceContext(int tableIndex, String customerName) {
        this.tableIndex = tableIndex;
        this.customerName = customerName;
    }
    public int getTableIndex() {
        return tableIndex;
    }
    public String getCustomerName() {
        return customerName;
    }
}
interface Drink {
    void provideService(ServiceContext serviceContext);
}
class Coffee implements Drink {
    public Coffee() {
        System.out.println("Coffee is created.");
    }
    @Override
    public void provideService(ServiceContext serviceContext) {
        System.out.println("Coffee is serving for table " + serviceContext.getTableIndex() + " customer " + serviceContext.getCustomerName());
    }
}
class Tea implements Drink {
    public Tea() {
        System.out.println("Drink is created.");
    }
    @Override
    public void provideService(ServiceContext serviceContext) {
        System.out.println("Drink is serving for table " + serviceContext.getTableIndex() + " customer " + serviceContext.getCustomerName());
    }
}
class Water implements Drink {
    public Water() {
        System.out.println("Water is created.");
    }
    @Override
    public void provideService(ServiceContext serviceContext) {
        System.out.println("Water is serving for table " + serviceContext.getTableIndex() + " customer " + serviceContext.getCustomerName());
    }
}
class DrinkFactory {
    private Map<String, Drink> drinks = new HashMap<>();
    public Drink createDrink(String type) {
        Drink drink = drinks.get(type);
        if (drink == null) {
            // 以下可以考虑抽象工厂模式实现以符合开闭原则,也可以使用反射
            if (type.equals("Water")) {
                drink = new Water();
            } else if (type.equals("Tea")) {
                drink = new Tea();
            } else if (type.equals("Coffee")) {
                drink = new Coffee();
            }
            drinks.put(type, drink);
        }
        return drink;
    }
}
public class WaiterTest {
    public static void main(String[] args) {
        String[] types = {"Water", "Tea", "Coffee"};
        DrinkFactory drinkFactory = new DrinkFactory();
        for (int i = 1; i <= 9; i++) {
            Drink drink = drinkFactory.createDrink(types[i % 3]);// Drink 可共享特征 在 DrinkFactory 内部实现享元
            ServiceContext serviceContext = new ServiceContext(i, "Sir" + i);// 服务细节为不可共享特征,不能享元
            drink.provideService(serviceContext);//外部特征,不可共享特征,保证调用过程不会影响下次调用。
        }
    }
}
输出:

Drink is created.
Drink is serving for table 1 customer Sir1
Coffee is created.
Coffee is serving for table 2 customer Sir2
Water is created.
Water is serving for table 3 customer Sir3
Drink is serving for table 4 customer Sir4
Coffee is serving for table 5 customer Sir5
Water is serving for table 6 customer Sir6
Drink is serving for table 7 customer Sir7
Coffee is serving for table 8 customer Sir8
Water is serving for table 9 customer Sir9
可以看出:在9次的服务过程中,饮品只创建了三次,一种饮品都仅仅创建了一次,减小了很多内存开销,服务可以很好的进行。
这就是享元模式的精髓,但务必注意区分出可共享与不可共享部分,保证不可共享部分在使用之后不会影响下次使用,即不会改变可共享部分。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值