zoukankan      html  css  js  c++  java
  • 设计模式之享元模式

    概述

    享元模式(FlyWeight):运用共享技术有效的支持大量细粒度对象的重用。

    享元对象能做到共享的关键就是区分了内部状态外部状态

    内部状态:可以共享,不会随环境变化而变化

    外部状态:不可以共享,会随环境变化而变化

    使用场景

    • 系统中存在大量的相似对象
    • 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份.
    • 需要缓冲池的场景比如线程池,数据库连接池,这些都利用享元模式共享了部分属性,在池中操作。
    • String类的设计也是享元模式

    UML

    享元模式分为单纯享元模式和复合享元模式

    单纯享元模式

    在单纯的享元模式中,所有的享元对象都是可以共享的.抽象享元模式UML如下

    其中涉及到的角色有:

    • Flyweight (抽象享元角色): 享元对象抽象基类或者接口,规定出所有具有享元角色需要实现的方法.
    • ConcreteFlyweight (具体享元角色) : 具体的享元对象,实现了Flyweight接口
    • FlyweightFactory (享元工厂角色) : 享元工厂,负责管理享元对象池和创建享元对象.本角色必须保证享元对象可以被系统适当地共享.
    复合享元模式

    复合享元模式将一些单纯享元使用合成模式加以复合.形成复合享元对象,复合享元对象本身不能共享,但可以分解为单纯享元对象实现共享,复合享元对象的UML如下

    其中涉及到的角色

    • Flyweight (抽象享元角色) : 享元对象的抽象接口.
    • ConcreteFlyweight (具体享元角色) : 实现了Flyweight接口的享元角色.
    • ConcreteCompositeFlyweight (复合享元角色) : 单纯享元对象的组合,不可共享的享元对象.
    • FlyweightFactory (享元工厂角色) : 创建和管理享元角色.

    实例

    单纯享元

    如下以火车票售票系统为例模拟,当数以万计的人查询票价信息时,假设 出发地-终点地,上铺,下铺,坐票看做是享元对象进行缓存,减少服务器压力,如下是一个单纯享元模式示例

    • 首先创建Flyweight
    package com.dyleaf.structure.FlyweightPattern.Simple;
    
    /**
     * 抽象享元类
     */
    public interface Ticket {
        public void showTicketInfo(String bunk);  //外部状态传入
    }
    
    • 创建ConcreteFlyweight对象,这里是火车票
    package com.dyleaf.structure.FlyweightPattern.Simple;
    
    import Struct.FlyweightPattern.Simple.Ticket;
    
    import java.util.Random;
    
    /**
     * 具体享元角色类
     */
    public class TrainTicket implements Ticket {
    
        public String from;
        public String to;
        public String bunk;
        public int price;
    
        //负责为内部状态提供存储空间
        public TrainTicket(String from, String to) {
            this.from = from;
            this.to = to;
        }
    
        @Override
        public void showTicketInfo(String bunk) {
            price = new Random().nextInt(300);
            System.out.println("购买 从" + from + " 到" + to + "的" + bunk + "火车票" + "," + "价格" + price);
        }
    }
    
    
    • 创建FlyweightFactory享元工厂
    package com.dyleaf.structure.FlyweightPattern.Simple;
    
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    
    /**
     * 享元工厂
     */
    public class TicketFactory {
        public static Map<String, Ticket> stringTicketMap = new ConcurrentHashMap<>(); //内部状态
    
        public static Ticket getTicket(String from, String to) {
            String key = from + "-" + to;
            if (stringTicketMap.containsKey(key)) {
                System.out.println("使用缓存-->" + key);
                return stringTicketMap.get(key);
            } else {
                System.out.println("创建对象-->" + key);
                Ticket ticket = new TrainTicket(from, to);
                stringTicketMap.put(key,ticket);
                return new TrainTicket(from, to);
            }
        }
    }
    
    
    • Client调用代码如下
    package com.dyleaf.structure.FlyweightPattern.Simple;
    
    public class Test {
        public static void main(String[] args) {
            Ticket ticket1 = TicketFactory.getTicket("北京", "青岛");
            ticket1.showTicketInfo("上铺");
            Ticket ticket2 = TicketFactory.getTicket("北京", "青岛");
            ticket2.showTicketInfo("下铺");
            Ticket ticket3 = TicketFactory.getTicket("北京", "青岛");
            ticket3.showTicketInfo("坐票");
        }
    }
    
    

    输出

    创建对象-->北京-青岛
    购买 从北京 到青岛的上铺火车票,价格161
    使用缓存-->北京-青岛
    购买 从北京 到青岛的下铺火车票,价格259
    使用缓存-->北京-青岛
    购买 从北京 到青岛的坐票火车票,价格162

    复杂享元

    • 首先创建Flyweight
    package com.dyleaf.structure.FlyweightPattern.Complex;
    
    /**
     * 抽象享元类
     */
    public interface Ticket {
        public void showTicketInfo(String bunk);  //外部状态传入
    }
    
    • 创建ConcreteFlyweight对象,这里是火车票
    package com.dyleaf.structure.FlyweightPattern.Complex;
    
    import Struct.FlyweightPattern.Simple.Ticket;
    
    import java.util.Random;
    
    /**
     * 具体享元角色类
     */
    public class TrainTicket implements Ticket {
    
        public String from;
        public String to;
        public String bunk;
        public int price;
    
        //负责为内部状态提供存储空间
        public TrainTicket(String from, String to) {
            this.from = from;
            this.to = to;
        }
    
        @Override
        public void showTicketInfo(String bunk) {
            price = new Random().nextInt(300);
            System.out.println("购买 从" + from + " 到" + to + "的" + bunk + "火车票" + "," + "价格" + price);
        }
    }
    
    
    • 创建ConcreteCompositeFlyweight对象,这里是指定位置的火车票
    package com.dyleaf.structure.FlyweightPattern.Complex;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class CompositeTrainTicket implements Ticket {
    
        private Map<String, Ticket> map = new HashMap<>();
    
        /**
         * 增加一个新的单纯享元对象到聚集中
         */
        public void add(String key, Ticket trainTicket) {
            map.put(key, trainTicket);
        }
    
        /**
         * 外蕴状态作为参数传入到方法中
         */
        @Override
        public void showTicketInfo(String bunk) {
            Ticket ticket = null;
            for (Object o:map.keySet()) {
                ticket = map.get(o);
                ticket.showTicketInfo(bunk);
            }
        }
    }
    
    
    • 创建FlyweightFactory享元工厂
    package com.dyleaf.structure.FlyweightPattern.Complex;
    
    import jdk.internal.util.xml.impl.Pair;
    
    import java.security.KeyPair;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    
    /**
     * 享元工厂
     */
    public class TicketFactory {
        public static Map<String, Ticket> stringTicketMap = new ConcurrentHashMap<>(); //内部状态
    
    
        public Ticket getTicket(List<Map.Entry<String,String>> fromToPair){
            CompositeTrainTicket ticket = new CompositeTrainTicket();
            for (Map.Entry p:fromToPair) {
                ticket.add(p.getKey()+"-"+p.getValue(), this.getTicket((String) p.getKey(), (String) p.getValue()));
            }
            return ticket;
        }
    
        public static Ticket getTicket(String from, String to) {
            String key = from + "-" + to;
            if (stringTicketMap.containsKey(key)) {
                System.out.println("使用缓存-->" + key);
                return stringTicketMap.get(key);
            } else {
                System.out.println("创建对象-->" + key);
                Ticket ticket = new TrainTicket(from, to);
                stringTicketMap.put(key,ticket);
                return new TrainTicket(from, to);
            }
        }
    }
    
    
    • Client调用代码如下
    package com.dyleaf.structure.FlyweightPattern.Complex;
    
    import java.util.AbstractMap;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    public class Test {
        public static void main(String[] args) {
            List<Map.Entry<String, String>> list = new ArrayList<>();
            list.add(new AbstractMap.SimpleEntry<String, String>("北京", "青岛"));
            list.add(new AbstractMap.SimpleEntry<String, String>("北京", "杭州"));
            list.add(new AbstractMap.SimpleEntry<String, String>("北京", "福州"));
    
            TicketFactory ticketFactory = new TicketFactory();
            CompositeTrainTicket ticket1 = (CompositeTrainTicket) ticketFactory.getTicket(list);
            CompositeTrainTicket ticket2 = (CompositeTrainTicket) ticketFactory.getTicket(list);
            ticket1.showTicketInfo("下铺");
            ticket2.showTicketInfo("上铺");
    
        }
    }
    

    输出

    创建对象-->北京-青岛
    创建对象-->北京-杭州
    创建对象-->北京-福州
    使用缓存-->北京-青岛
    使用缓存-->北京-杭州
    使用缓存-->北京-福州
    购买 从北京 到福州的下铺火车票,价格83
    购买 从北京 到青岛的下铺火车票,价格249
    购买 从北京 到杭州的下铺火车票,价格245
    购买 从北京 到福州的上铺火车票,价格85
    购买 从北京 到青岛的上铺火车票,价格36
    购买 从北京 到杭州的上铺火车票,价格141

    优缺点

    优点:

    1.极大的减少内存中对象的数量

    2.相同或相似对象内存中只存在一份,极大的节约资源,提高系统性能

    3.外部状态相对独立,不影响内部状态

    缺点:

    1.模式较复杂,使程序逻辑复杂化

    2.为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态使运行时间变长。

    see source code

  • 相关阅读:
    linux nohup
    并发编程-多进程模块介绍
    并发编程-多进程
    网络编程-粘包现象
    Typora快捷键Mac
    网络编程
    异常处理
    面向对象-元类介绍
    面向对象-内置方法
    面向对象-反射
  • 原文地址:https://www.cnblogs.com/Dyleaf/p/8507045.html
Copyright © 2011-2022 走看看