一、模式解析
Flyweight在拳击比赛中指最轻量级,即“蝇量级”或“雨量级”,这里选择使用“享元模式”的意译,是因为这样更能反映模式的用意。享元模式是对象的结构模式。享元模式以共享的方式高效地支持大量的细粒度对象。
享元模式:主要为了在创建对象时,对共有对象以缓存的方式进行保存,对外部对象进行单独创建
模式要点:
1、享元模式中的对象分为两部分:共性部分和个性化部分,共性部分就是每个对象都一致的或者多个对象可以共享的部分,个性化部分指差异比较大,每个类均不同的部分
2、共性部分的抽象就是此模式介绍的享元对象
3、享元对象一般通过一个享元工厂进行创建和保存
4、享元工厂根据享元对象的多少,实现一个单例或者多例模式,来创建享元对象
5、享元对象可以预先设定好在享元工厂里,也可以创建一个集合,每次创建对象时,查看享元对象是否存在,不存在则进行添加
二、模式代码
1、创建享元接口
package flyweight.patten; //享元接口 public interface FlyWeight { public void opertion(); }
2、创建具体享元类
package flyweight.patten; public class ConcreteFlyWeight implements FlyWeight { public String name; public ConcreteFlyWeight(String name){ this.name=name; } @Override public void opertion() { System.out.println("执行享元类"); } }
3、创建享元工厂
package flyweight.patten; import java.util.HashMap; import java.util.Map; //享元工厂,为客户端提供享元类 public class FlyWeightFactory { //使用内部map,保证享元对象只被创建一次 private Map<String,FlyWeight> map=new HashMap<String,FlyWeight>(); public FlyWeight getFlyWeight(String name){ FlyWeight flyWeight=map.get(name); if(map.get(name)==null||map.get(name).equals("")){ flyWeight=new ConcreteFlyWeight(name); map.put(name, flyWeight); } return flyWeight; } }
5、客户端
package flyweight.patten; public class Client { public static void main(String[] args) { FlyWeightFactory factory=new FlyWeightFactory(); FlyWeight flyWeight1=factory.getFlyWeight("张三"); FlyWeight flyWeight2=factory.getFlyWeight("李四"); FlyWeight flyWeight3=factory.getFlyWeight("张三"); System.out.println(flyWeight1==flyWeight2); System.out.println(flyWeight1==flyWeight3); } }
6、执行结果,可以看到,如果姓名相同,每次引用的享元对象都是一样的,符合多例模式
false true
注:此模式的标准代码为单纯的享元模式,并不完全符合我们上边的模式要点,因为他创建的对象参数全部为享元内容,所以我们在后边的实例中编写一个完全的享元模式代码。
三、应用场景
享元模式的应用比较广泛,类似与数据库的外键关联关系,所以我们以工作中最常见的银行卡举例:
客户账户属性分为:银行卡种类,银行卡名称,银行卡号,余额,客户姓名等因素。在创建卡对象时候,由于卡种类和名称对于每个银行仅有几种,所以创建一个享元对象进行保存。
四、模式代码
1、创建银行卡类,也就是享元对象
package flyweight.example; /** * 卡类型 * @author lenovo * */ public class Card { private String cardType; private String cardName; public String getCardName() { return cardName; } public void setCardName(String cardName) { this.cardName = cardName; } public String getCardType() { return cardType; } public void setCardType(String cardType) { this.cardType = cardType; } public Card(String cardType,String cardName){ this.cardType=cardType; this.cardName=cardName; } @Override public String toString() { return "CardType [cardType=" + cardType + ", cardName=" + cardName + "]"; } }
2、创建账户接口
package flyweight.example; /** * 账户接口 * @author lenovo * */ public interface CountInterface { /** * 显示卡内容 */ public void show() ; }
3、创建账户类
package flyweight.example; import java.math.BigDecimal; /** * 具体账户属性 * @author lenovo * */ public class ConcreteCount implements CountInterface { private String cardNo; private BigDecimal balance; private Card card; /** * 根据参数创建用户对象 * @param cardNo * @param balance * @param cardType */ public ConcreteCount(String cardNo, BigDecimal balance, Card card) { this.cardNo = cardNo; this.balance = balance; this.card = card; } @Override public String toString() { return "ConcreteCount [cardNo=" + cardNo + ", balance=" + balance + ", card=" + card + "]"; } @Override public void show() { System.out.println(this.toString()); } }
5、创建享元工厂,享元工厂返回客户账户实例,在创建客户账户时,首先判断卡属性是否存在,存在就直接从享元对象中获取卡属性,否则创建新的卡
package flyweight.example; import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; /** * 享元工厂,主要用于创建对象 * @author lenovo * */ public class CountFactory { //保存享元对象的map Map<String,Card> cardTypeMap=new HashMap<String, Card>(); public ConcreteCount getConcreteCount(String cardNo, BigDecimal balance,String cardType,String cardName){ Card card=null; //判断享元对象是否已经存在,如果存在则使用,不存在则额外创建 if(cardTypeMap.get(cardType+cardName)!=null){ card=cardTypeMap.get(cardType+cardName); }else { card=new Card(cardType, cardName); cardTypeMap.put(cardType+cardName, card); } return new ConcreteCount(cardNo, balance, card); } }
5、客户端
package flyweight.example; import java.math.BigDecimal; public class CardTest { /** * 测试享元模式 * @param args */ public static void main(String[] args) { CountFactory factory=new CountFactory(); ConcreteCount count=factory.getConcreteCount("001", new BigDecimal(100), "1", "信用卡"); ConcreteCount count2=factory.getConcreteCount("002", new BigDecimal(200), "2", "借记卡"); ConcreteCount count3=factory.getConcreteCount("003", new BigDecimal(300), "1", "信用卡"); ConcreteCount count4=factory.getConcreteCount("004", new BigDecimal(400), "2", "借记卡"); count.show(); count2.show(); count3.show(); count4.show(); } }
6、结果
ConcreteCount [cardNo=001, balance=100, card=CardType [cardType=1, cardName=信用卡]] ConcreteCount [cardNo=002, balance=200, card=CardType [cardType=2, cardName=借记卡]] ConcreteCount [cardNo=003, balance=300, card=CardType [cardType=1, cardName=信用卡]] ConcreteCount [cardNo=004, balance=400, card=CardType [cardType=2, cardName=借记卡]]
五、解析
如同第三部分所说,享元模式大都是以数据库外键形式体现在系统中,所以在系统中使用享元思想的很多,但是使用享元对象标准模式写法的还是比较少,所以设计模式还是要注重思想。