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

  • 相关阅读:
    Java Spring Boot VS .NetCore (十) Java Interceptor vs .NetCore Interceptor
    Java Spring Boot VS .NetCore (九) Spring Security vs .NetCore Security
    IdentityServer4 And AspNetCore.Identity Get AccessToken 问题
    Java Spring Boot VS .NetCore (八) Java 注解 vs .NetCore Attribute
    Java Spring Boot VS .NetCore (七) 配置文件
    Java Spring Boot VS .NetCore (六) UI thymeleaf vs cshtml
    Java Spring Boot VS .NetCore (五)MyBatis vs EFCore
    Java Spring Boot VS .NetCore (四)数据库操作 Spring Data JPA vs EFCore
    Java Spring Boot VS .NetCore (三)Ioc容器处理
    Java Spring Boot VS .NetCore (二)实现一个过滤器Filter
  • 原文地址:https://www.cnblogs.com/Dyleaf/p/8507045.html
Copyright © 2011-2022 走看看