首先学习设计模式的宗旨:将一类不断重复发生、相似的问题以及这类问题的解决方案总结出来,将它们的共同点抽象成一定的模式,以便遇到这类问题,我们可以直接采用这种模式去解决问题。
使用设计模式的误区:不要为了模式而模式,能够在项目中选择最佳设计和实现方案才是王道。设计模式就是一种解决问题的思维。
举个生活中的例子
家里一到晚上就开始抢电视,大人们喜欢看连续剧,小孩喜欢看少儿节目。每天晚上你争我抢的,最后还是输给了小孩,最近看少儿节目,都看的快弱智了!肿么办,摆两台电视吧,一个给小孩看,一个给大人看。一个人用一台电视显得有点浪费,而且家里不一定放得下。按需求来分开两拨,花最少的钱得到所需,更明智些!
官方解释:
它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。
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==m是true,这就说明了在JVM中n和m两个引用了同一个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对象! "
);
}
}
|
卖出一份摩卡的咖啡。
卖出一份卡布奇诺的咖啡。
卖出一份香草星冰乐的咖啡。
卖出一份香草星冰乐的咖啡。
卖出一份拿铁的咖啡。
卖出一份卡布奇诺的咖啡。
卖出一份拿铁的咖啡。
卖出一份卡布奇诺的咖啡。
卖出一份摩卡的咖啡。
卖出一份香草星冰乐的咖啡。
卖出一份卡布奇诺的咖啡。
卖出一份摩卡的咖啡。
卖出一份香草星冰乐的咖啡。
客户一共买了 13 杯咖啡!
共生成了 4 个 FlavorOrder java对象!
核心总结,可以共享的对象,也就是说返回的同一类型的对象其实是同一实例,当客户端要求生成一个对象时,工厂会检测是否存在此对象的实例,如果存在那么直接返回此对象实例,如果不存在就创建一个并保存起来,这点有些单例模式的意思。通常工厂类会有一个集合类型的成员变量来用以保存对象,如hashtable等。在java中,数据库连接池,线程池等即是用享元模式的应用。
优缺点:
优点:减少对象数量,节省内存空间。
缺点:
1)享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。
2)享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。