设计模式--享元模式


Cyan

首先学习设计模式的宗旨:将一类不断重复发生、相似的问题以及这类问题的解决方案总结出来,将它们的共同点抽象成一定的模式,以便遇到这类问题,我们可以直接采用这种模式去解决问题。

使用设计模式的误区:不要为了模式而模式,能够在项目中选择最佳设计和实现方案才是王道。设计模式就是一种解决问题的思维。

举个生活中的例子

家里一到晚上就开始抢电视,大人们喜欢看连续剧,小孩喜欢看少儿节目。每天晚上你争我抢的,最后还是输给了小孩,最近看少儿节目,都看的快弱智了!肿么办,摆两台电视吧,一个给小孩看,一个给大人看。一个人用一台电视显得有点浪费,而且家里不一定放得下。按需求来分开两拨,花最少的钱得到所需,更明智些!

官方解释:

它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。

Java中最常见的享元模式

1
2
3
4
5
6
7
public class Flyweight{
        public static void main(String[] args){
               String n = "I Love Java" ;
               String m = "I Love Java" ;            
               System.out.println(n==m);
        }
}

这段代码会告诉你n==mtrue,这就说明了在JVMnm两个引用了同一个String对象。String类型的设计避免了在创建N多的String对象时产生的不必要的资源损耗,可以说是享元模式应用的范例。

结构

设计模式:

1)抽象享元角色:为具体享元角色规定了必须实现的方法,而外蕴状态就是以参数的形式通过此方法传入。在Java中可以由抽象类、接口来担当。

2)具体享元角色:实现抽象角色规定的方法。如果存在内蕴状态,就负责为内蕴状态提供存储空间。

3)享元工厂角色:负责创建和管理享元角色。要想达到共享的目的,这个角色的实现是关键!

4)客户端角色:维护对所有享元对象的引用,而且还需要存储对应的外蕴状态。


代码案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
Order:
import java.util.*;
public abstract class Order {
// 执行卖出动作
public abstract void sell();
}
FlavorOrder:
public class FlavorOrder extends Order {
public String flavor;
// 获取咖啡口味
public FlavorOrder(String flavor) {
    this .flavor = flavor;
}
@Override
public void sell() {
    // TODO Auto-generated method stub
    System.out.println( "卖出一份" + flavor + "的咖啡。" );
}
}
FlavorFactory:
public class FlavorFactory {
private Map<String, Order> flavorPool = new HashMap<String, Order>();
// 静态工厂,负责生成订单对象
private static FlavorFactory flavorFactory = new FlavorFactory();
private FlavorFactory() {
}
public static FlavorFactory getInstance() {
    return flavorFactory;
}
public Order getOrder(String flavor) {
    Order order = null ;
  if (flavorPool.containsKey(flavor)) { // 如果此映射包含指定键的映射关系,则返                //回 true
     order = flavorPool.get(flavor);
    } else {
     order = new FlavorOrder(flavor);
     flavorPool.put(flavor, order);
    }
    return order;
}
public int getTotalFlavorsMade() {
    return flavorPool.size();
}
}
Client
public class Client {
// 客户下的订单
private static List<Order> orders = new ArrayList<Order>();
// 订单对象生成工厂
private static FlavorFactory flavorFactory;
// 增加订单
private static void takeOrders(String flavor) {
    orders.add(flavorFactory.getOrder(flavor));
}
public static void main(String[] args) {
    // 订单生成工厂
    flavorFactory = FlavorFactory.getInstance();
    // 增加订单
    takeOrders( "摩卡" );
    takeOrders( "卡布奇诺" );
    takeOrders( "香草星冰乐" );
    takeOrders( "香草星冰乐" );
    takeOrders( "拿铁" );
    takeOrders( "卡布奇诺" );
    takeOrders( "拿铁" );
    takeOrders( "卡布奇诺" );
    takeOrders( "摩卡" );
    takeOrders( "香草星冰乐" );
    takeOrders( "卡布奇诺" );
    takeOrders( "摩卡" );
    takeOrders( "香草星冰乐" );
    // 卖咖啡
    for (Order order : orders) {
     order.sell();
    }
    // 打印生成的订单java对象数量
    System.out.println( "\n客户一共买了 " + orders.size() + " 杯咖啡! " );
    // 打印生成的订单java对象数量
    System.out.println( "共生成了 " + flavorFactory.getTotalFlavorsMade()
      + " 个 FlavorOrder java对象! " );
}
}
Output:
卖出一份摩卡的咖啡。
卖出一份卡布奇诺的咖啡。
卖出一份香草星冰乐的咖啡。
卖出一份香草星冰乐的咖啡。
卖出一份拿铁的咖啡。
卖出一份卡布奇诺的咖啡。
卖出一份拿铁的咖啡。
卖出一份卡布奇诺的咖啡。
卖出一份摩卡的咖啡。
卖出一份香草星冰乐的咖啡。
卖出一份卡布奇诺的咖啡。
卖出一份摩卡的咖啡。
卖出一份香草星冰乐的咖啡。
客户一共买了 13 杯咖啡!
共生成了 4 个 FlavorOrder java对象!

核心总结,可以共享的对象,也就是说返回的同一类型的对象其实是同一实例,当客户端要求生成一个对象时,工厂会检测是否存在此对象的实例,如果存在那么直接返回此对象实例,如果不存在就创建一个并保存起来,这点有些单例模式的意思。通常工厂类会有一个集合类型的成员变量来用以保存对象,如hashtable等。在java中,数据库连接池,线程池等即是用享元模式的应用。

优缺点:

优点:减少对象数量,节省内存空间。

缺点:

1)享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。

2)享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值