设计模式-享元模式详解

享元模式详解

目录

  1. 享元模式简介
  2. 核心流程
  3. 重难点分析
  4. Spring中的源码分析
  5. 具体使用场景
  6. 面试高频点

享元模式简介

定义

享元模式(Flyweight Pattern)是一种结构型设计模式,它运用共享技术有效地支持大量细粒度的对象。享元模式通过共享相同的内在状态来减少内存使用,提高系统性能。

核心思想

  • 共享内在状态:将对象的内在状态(不变部分)提取出来共享
  • 分离内外状态:内在状态存储在享元对象中,外在状态由客户端维护
  • 减少对象数量:通过共享减少系统中对象的数量
  • 提高性能:减少内存占用,提高系统性能

模式结构

  • Flyweight(抽象享元类):声明一个接口,通过它可以接受并作用于外部状态
  • ConcreteFlyweight(具体享元类):实现抽象享元接口,并为内部状态增加存储空间
  • UnsharedConcreteFlyweight(非共享具体享元类):不需要共享的享元类
  • FlyweightFactory(享元工厂类):创建并管理享元对象,确保合理地共享享元对象
  • Client(客户端):维护对享元对象的引用,计算或存储享元对象的外部状态

核心流程

享元模式流程图

状态分离 享元对象池 是 否 内在状态共享 外在状态客户端维护 享元对象A 享元对象B 享元对象C 客户端请求对象 享元工厂 享元对象是否存在? 返回现有享元对象 创建新享元对象 缓存享元对象 返回新享元对象 设置外部状态 执行操作 返回结果

基本实现流程

1. 定义抽象享元类
java 复制代码
// 抽象享元类
public abstract class Flyweight {
    // 内在状态(共享)
    protected String intrinsicState;
  
    public Flyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }
  
    // 操作外在状态的方法
    public abstract void operation(String extrinsicState);
  
    public String getIntrinsicState() {
        return intrinsicState;
    }
}
2. 实现具体享元类
java 复制代码
// 具体享元类
public class ConcreteFlyweight extends Flyweight {
    public ConcreteFlyweight(String intrinsicState) {
        super(intrinsicState);
    }
  
    @Override
    public void operation(String extrinsicState) {
        System.out.println("内在状态: " + intrinsicState + ", 外在状态: " + extrinsicState);
    }
}

// 非共享具体享元类
public class UnsharedConcreteFlyweight extends Flyweight {
    private String allState;
  
    public UnsharedConcreteFlyweight(String allState) {
        super(allState);
        this.allState = allState;
    }
  
    @Override
    public void operation(String extrinsicState) {
        System.out.println("非共享享元: " + allState + ", 外在状态: " + extrinsicState);
    }
}
3. 实现享元工厂
java 复制代码
// 享元工厂类
public class FlyweightFactory {
    private Map<String, Flyweight> flyweights = new HashMap<>();
  
    public Flyweight getFlyweight(String key) {
        Flyweight flyweight = flyweights.get(key);
        if (flyweight == null) {
            flyweight = new ConcreteFlyweight(key);
            flyweights.put(key, flyweight);
            System.out.println("创建新的享元对象: " + key);
        } else {
            System.out.println("复用现有享元对象: " + key);
        }
        return flyweight;
    }
  
    public int getFlyweightCount() {
        return flyweights.size();
    }
  
    public void clear() {
        flyweights.clear();
    }
}
4. 客户端使用
java 复制代码
public class Client {
    public static void main(String[] args) {
        FlyweightFactory factory = new FlyweightFactory();
      
        // 获取享元对象
        Flyweight flyweight1 = factory.getFlyweight("A");
        Flyweight flyweight2 = factory.getFlyweight("B");
        Flyweight flyweight3 = factory.getFlyweight("A"); // 复用
      
        // 使用享元对象
        flyweight1.operation("状态1");
        flyweight2.operation("状态2");
        flyweight3.operation("状态3");
      
        System.out.println("享元对象总数: " + factory.getFlyweightCount());
    }
}

重难点分析

重难点1:内在状态和外在状态的分离

问题描述

如何正确识别和分离对象的内在状态和外在状态。

解决方案
java 复制代码
// 1. 明确状态分类
public class Character {
    // 内在状态(不变,可共享)
    private final char symbol;
    private final String fontFamily;
    private final int fontSize;
    private final String color;
  
    // 外在状态(变化,不可共享)
    private int x, y; // 位置坐标
    private boolean visible; // 是否可见
  
    public Character(char symbol, String fontFamily, int fontSize, String color) {
        this.symbol = symbol;
        this.fontFamily = fontFamily;
        this.fontSize = fontSize;
        this.color = color;
    }
  
    public void render(int x, int y, boolean visible) {
        this.x = x;
        this.y = y;
        this.visible = visible;
      
        if (visible) {
            System.out.println("渲染字符: " + symbol + " at (" + x + "," + y + ") " +
                             "字体: " + fontFamily + " 大小: " + fontSize + " 颜色: " + color);
        }
    }
  
    // 内在状态getter
    public char getSymbol() { return symbol; }
    public String getFontFamily() { return fontFamily; }
    public int getFontSize() { return fontSize; }
    public String getColor() { return color; }
}

// 2. 享元对象只包含内在状态
public class CharacterFlyweight {
    private final char symbol;
    private final String fontFamily;
    private final int fontSize;
    private final String color;
  
    public CharacterFlyweight(char symbol, String fontFamily, int fontSize, String color) {
        this.symbol = symbol;
        this.fontFamily = fontFamily;
        this.fontSize = fontSize;
        this.color = color;
    }
  
    public void render(int x, int y, boolean visible) {
        if (visible) {
            System.out.println("渲染字符: " + symbol + " at (" + x + "," + y + ") " +
                             "字体: " + fontFamily + " 大小: " + fontSize + " 颜色: " + color);
        }
    }
  
    // 内在状态getter
    public char getSymbol() { return symbol; }
    public String getFontFamily() { return fontFamily; }
    public int getFontSize() { return fontSize; }
    public String getColor() { return color; }
}

// 3. 外在状态由客户端维护
public class TextEditor {
    private FlyweightFactory factory = new FlyweightFactory();
    private List<TextPosition> positions = new ArrayList<>();
  
    public void addCharacter(char symbol, int x, int y, boolean visible) {
        CharacterFlyweight character = factory.getCharacter(symbol);
        positions.add(new TextPosition(character, x, y, visible));
    }
  
    public void render() {
        for (TextPosition position : positions) {
            position.render();
        }
    }
  
    private static class TextPosition {
        private final CharacterFlyweight character;
        private final int x, y;
        private final boolean visible;
      
        public TextPosition(CharacterFlyweight character, int x, int y, boolean visible) {
            this.character = character;
            this.x = x;
            this.y = y;
            this.visible = visible;
        }
      
        public void render() {
            character.render(x, y, visible);
        }
    }
}

重难点2:享元对象池的管理

问题描述

如何高效地管理享元对象池,避免内存泄漏和性能问题。

解决方案
java 复制代码
// 1. 使用弱引用避免内存泄漏
public class WeakFlyweightFactory {
    private final Map<String, WeakReference<Flyweight>> flyweights = new ConcurrentHashMap<>();
    private final ScheduledExecutorService cleanupExecutor = Executors.newScheduledThreadPool(1);
  
    public WeakFlyweightFactory() {
        // 定期清理失效的弱引用
        cleanupExecutor.scheduleAtFixedRate(this::cleanup, 1, 1, TimeUnit.MINUTES);
    }
  
    public Flyweight getFlyweight(String key) {
        WeakReference<Flyweight> ref = flyweights.get(key);
        Flyweight flyweight = ref != null ? ref.get() : null;
      
        if (flyweight == null) {
            flyweight = new ConcreteFlyweight(key);
            flyweights.put(key, new WeakReference<>(flyweight));
        }
      
        return flyweight;
    }
  
    private void cleanup() {
        flyweights.entrySet().removeIf(entry -> entry.getValue().get() == null);
    }
  
    public void shutdown() {
        cleanupExecutor.shutdown();
    }
}

// 2. 使用LRU缓存管理享元对象
public class LRUFlyweightFactory {
    private final int maxSize;
    private final LinkedHashMap<String, Flyweight> cache;
  
    public LRUFlyweightFactory(int maxSize) {
        this.maxSize = maxSize;
        this.cache = new LinkedHashMap<String, Flyweight>(16, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<String, Flyweight> eldest) {
                return size() > maxSize;
            }
        };
    }
  
    public synchronized Flyweight getFlyweight(String key) {
        Flyweight flyweight = cache.get(key);
        if (flyweight == null) {
            flyweight = new ConcreteFlyweight(key);
            cache.put(key, flyweight);
        }
        return flyweight;
    }
  
    public synchronized int size() {
        return cache.size();
    }
}

// 3. 使用对象池模式
public class FlyweightPool {
    private final Queue<Flyweight> pool = new ConcurrentLinkedQueue<>();
    private final int maxSize;
    private final AtomicInteger currentSize = new AtomicInteger(0);
  
    public FlyweightPool(int maxSize) {
        this.maxSize = maxSize;
    }
  
    public Flyweight acquire(String key) {
        Flyweight flyweight = pool.poll();
        if (flyweight == null) {
            flyweight = new ConcreteFlyweight(key);
        }
        return flyweight;
    }
  
    public void release(Flyweight flyweight) {
        if (currentSize.get() < maxSize) {
            // 重置状态
            flyweight.reset();
            pool.offer(flyweight);
            currentSize.incrementAndGet();
        }
    }
}

重难点3:线程安全问题

问题描述

在多线程环境下,享元工厂的线程安全问题。

解决方案
java 复制代码
// 1. 使用ConcurrentHashMap
public class ThreadSafeFlyweightFactory {
    private final ConcurrentHashMap<String, Flyweight> flyweights = new ConcurrentHashMap<>();
  
    public Flyweight getFlyweight(String key) {
        return flyweights.computeIfAbsent(key, k -> new ConcreteFlyweight(k));
    }
}

// 2. 使用双重检查锁定
public class DoubleCheckedFlyweightFactory {
    private final Map<String, Flyweight> flyweights = new HashMap<>();
    private final Object lock = new Object();
  
    public Flyweight getFlyweight(String key) {
        Flyweight flyweight = flyweights.get(key);
        if (flyweight == null) {
            synchronized (lock) {
                flyweight = flyweights.get(key);
                if (flyweight == null) {
                    flyweight = new ConcreteFlyweight(key);
                    flyweights.put(key, flyweight);
                }
            }
        }
        return flyweight;
    }
}

// 3. 使用ReadWriteLock
public class ReadWriteLockFlyweightFactory {
    private final Map<String, Flyweight> flyweights = new HashMap<>();
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
  
    public Flyweight getFlyweight(String key) {
        lock.readLock().lock();
        try {
            Flyweight flyweight = flyweights.get(key);
            if (flyweight != null) {
                return flyweight;
            }
        } finally {
            lock.readLock().unlock();
        }
      
        lock.writeLock().lock();
        try {
            Flyweight flyweight = flyweights.get(key);
            if (flyweight == null) {
                flyweight = new ConcreteFlyweight(key);
                flyweights.put(key, flyweight);
            }
            return flyweight;
        } finally {
            lock.writeLock().unlock();
        }
    }
}

重难点4:享元模式的性能优化

问题描述

如何优化享元模式的性能,避免频繁的对象创建和查找。

解决方案
java 复制代码
// 1. 使用缓存预热
public class PreloadedFlyweightFactory {
    private final Map<String, Flyweight> flyweights = new ConcurrentHashMap<>();
  
    public PreloadedFlyweightFactory() {
        // 预加载常用享元对象
        preloadCommonFlyweights();
    }
  
    private void preloadCommonFlyweights() {
        String[] commonKeys = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"};
        for (String key : commonKeys) {
            flyweights.put(key, new ConcreteFlyweight(key));
        }
    }
  
    public Flyweight getFlyweight(String key) {
        return flyweights.computeIfAbsent(key, k -> new ConcreteFlyweight(k));
    }
}

// 2. 使用批量操作
public class BatchFlyweightFactory {
    private final Map<String, Flyweight> flyweights = new ConcurrentHashMap<>();
  
    public Map<String, Flyweight> getFlyweights(Collection<String> keys) {
        Map<String, Flyweight> result = new HashMap<>();
        List<String> missingKeys = new ArrayList<>();
      
        // 批量获取现有对象
        for (String key : keys) {
            Flyweight flyweight = flyweights.get(key);
            if (flyweight != null) {
                result.put(key, flyweight);
            } else {
                missingKeys.add(key);
            }
        }
      
        // 批量创建缺失对象
        if (!missingKeys.isEmpty()) {
            synchronized (this) {
                for (String key : missingKeys) {
                    if (!flyweights.containsKey(key)) {
                        flyweights.put(key, new ConcreteFlyweight(key));
                    }
                    result.put(key, flyweights.get(key));
                }
            }
        }
      
        return result;
    }
}

// 3. 使用异步加载
public class AsyncFlyweightFactory {
    private final Map<String, CompletableFuture<Flyweight>> flyweights = new ConcurrentHashMap<>();
  
    public CompletableFuture<Flyweight> getFlyweightAsync(String key) {
        return flyweights.computeIfAbsent(key, k -> 
            CompletableFuture.supplyAsync(() -> new ConcreteFlyweight(k))
        );
    }
  
    public Flyweight getFlyweight(String key) {
        try {
            return getFlyweightAsync(key).get();
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException("Failed to get flyweight", e);
        }
    }
}

Spring中的源码分析

Spring Bean的享元模式应用

java 复制代码
// BeanDefinition作为享元对象
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
  
    void setBeanClassName(@Nullable String beanClassName);
    @Nullable
    String getBeanClassName();
  
    void setScope(@Nullable String scope);
    @Nullable
    String getScope();
  
    void setLazyInit(boolean lazyInit);
    boolean isLazyInit();
  
    // 其他方法...
}

// 具体实现
public class GenericBeanDefinition extends AbstractBeanDefinition {
    @Nullable
    private String beanClassName;
  
    public GenericBeanDefinition() {
        super();
    }
  
    public GenericBeanDefinition(BeanDefinition original) {
        super(original);
    }
  
    @Override
    public void setBeanClassName(@Nullable String beanClassName) {
        this.beanClassName = beanClassName;
    }
  
    @Override
    @Nullable
    public String getBeanClassName() {
        return this.beanClassName;
    }
  
    @Override
    public AbstractBeanDefinition cloneBeanDefinition() {
        return new GenericBeanDefinition(this);
    }
}

Spring Security的享元模式

java 复制代码
// ConfigAttribute作为享元对象
public interface ConfigAttribute extends Serializable {
    String getAttribute();
}

// 具体实现
public class SecurityConfig implements ConfigAttribute {
    private final String config;
  
    public SecurityConfig(String config) {
        Assert.hasText(config, "You must provide a configuration attribute");
        this.config = config;
    }
  
    @Override
    public String getAttribute() {
        return this.config;
    }
  
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof SecurityConfig) {
            SecurityConfig that = (SecurityConfig) obj;
            return this.config.equals(that.config);
        }
        return false;
    }
  
    @Override
    public int hashCode() {
        return this.config.hashCode();
    }
  
    @Override
    public String toString() {
        return this.config;
    }
}

// 享元工厂
public class SecurityConfigFactory {
    private static final Map<String, SecurityConfig> configs = new ConcurrentHashMap<>();
  
    public static SecurityConfig createConfig(String config) {
        return configs.computeIfAbsent(config, SecurityConfig::new);
    }
  
    public static int getConfigCount() {
        return configs.size();
    }
}

Spring MVC的享元模式

java 复制代码
// HandlerMapping作为享元对象
public interface HandlerMapping {
    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

// 具体实现
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {
  
    private final Map<RequestMappingInfo, HandlerMethod> handlerMethods = new LinkedHashMap<>();
    private final Map<HandlerMethod, RequestMappingInfo> methodMappings = new LinkedHashMap<>();
  
    @Override
    @Nullable
    public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        HandlerMethod handlerMethod = getHandlerInternal(request);
        if (handlerMethod == null) {
            handlerMethod = getDefaultHandler();
        }
        if (handlerMethod == null) {
            return null;
        }
      
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handlerMethod, request);
      
        if (logger.isTraceEnabled()) {
            logger.trace("Mapped to " + handlerMethod);
        } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.REQUEST)) {
            logger.debug("Mapped to " + handlerMethod);
        }
      
        return executionChain;
    }
  
    @Nullable
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        this.mappingRegistry.acquireReadLock();
        try {
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        } finally {
            this.mappingRegistry.releaseReadLock();
        }
    }
}

Spring AOP的享元模式

java 复制代码
// Pointcut作为享元对象
public interface Pointcut {
    ClassFilter getClassFilter();
    MethodMatcher getMethodMatcher();
  
    Pointcut TRUE = TruePointcut.INSTANCE;
}

// 具体实现
public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher, Serializable {
    private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();
  
    static {
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
        SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
    }
  
    private String expression;
    private PointcutExpression pointcutExpression;
    private ClassFilter classFilter;
    private MethodMatcher methodMatcher;
  
    public void setExpression(@Nullable String expression) {
        this.expression = expression;
    }
  
    @Override
    public ClassFilter getClassFilter() {
        return this.classFilter;
    }
  
    @Override
    public MethodMatcher getMethodMatcher() {
        return this.methodMatcher;
    }
  
    @Override
    public boolean matches(Class<?> clazz) {
        return this.classFilter.matches(clazz);
    }
  
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return this.methodMatcher.matches(method, targetClass);
    }
}

具体使用场景

1. 文本编辑器

java 复制代码
// 字符享元对象
public class CharacterFlyweight {
    private final char character;
    private final String fontFamily;
    private final int fontSize;
    private final String color;
  
    public CharacterFlyweight(char character, String fontFamily, int fontSize, String color) {
        this.character = character;
        this.fontFamily = fontFamily;
        this.fontSize = fontSize;
        this.color = color;
    }
  
    public void render(int x, int y, boolean visible) {
        if (visible) {
            System.out.println("渲染字符: " + character + " at (" + x + "," + y + ") " +
                             "字体: " + fontFamily + " 大小: " + fontSize + " 颜色: " + color);
        }
    }
  
    public char getCharacter() { return character; }
    public String getFontFamily() { return fontFamily; }
    public int getFontSize() { return fontSize; }
    public String getColor() { return color; }
}

// 字符工厂
public class CharacterFactory {
    private static final Map<String, CharacterFlyweight> characters = new ConcurrentHashMap<>();
  
    public static CharacterFlyweight getCharacter(char character, String fontFamily, int fontSize, String color) {
        String key = character + "|" + fontFamily + "|" + fontSize + "|" + color;
        return characters.computeIfAbsent(key, k -> 
            new CharacterFlyweight(character, fontFamily, fontSize, color)
        );
    }
  
    public static int getCharacterCount() {
        return characters.size();
    }
}

// 文本位置
public class TextPosition {
    private final CharacterFlyweight character;
    private final int x, y;
    private final boolean visible;
  
    public TextPosition(CharacterFlyweight character, int x, int y, boolean visible) {
        this.character = character;
        this.x = x;
        this.y = y;
        this.visible = visible;
    }
  
    public void render() {
        character.render(x, y, visible);
    }
}

// 文本编辑器
public class TextEditor {
    private List<TextPosition> positions = new ArrayList<>();
  
    public void addCharacter(char character, int x, int y, boolean visible) {
        CharacterFlyweight flyweight = CharacterFactory.getCharacter(character, "Arial", 12, "black");
        positions.add(new TextPosition(flyweight, x, y, visible));
    }
  
    public void render() {
        for (TextPosition position : positions) {
            position.render();
        }
    }
  
    public int getCharacterCount() {
        return positions.size();
    }
  
    public int getUniqueCharacterCount() {
        return CharacterFactory.getCharacterCount();
    }
}

// 使用示例
public class TextEditorDemo {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
      
        // 添加字符
        editor.addCharacter('H', 0, 0, true);
        editor.addCharacter('e', 10, 0, true);
        editor.addCharacter('l', 20, 0, true);
        editor.addCharacter('l', 30, 0, true);
        editor.addCharacter('o', 40, 0, true);
      
        // 渲染文本
        editor.render();
      
        System.out.println("总字符数: " + editor.getCharacterCount());
        System.out.println("唯一字符数: " + editor.getUniqueCharacterCount());
    }
}

2. 游戏中的图形对象

java 复制代码
// 图形享元对象
public class GraphicFlyweight {
    private final String graphicType;
    private final String texture;
    private final int width;
    private final int height;
  
    public GraphicFlyweight(String graphicType, String texture, int width, int height) {
        this.graphicType = graphicType;
        this.texture = texture;
        this.width = width;
        this.height = height;
    }
  
    public void render(int x, int y, int rotation, boolean visible) {
        if (visible) {
            System.out.println("渲染图形: " + graphicType + " at (" + x + "," + y + ") " +
                             "纹理: " + texture + " 大小: " + width + "x" + height + 
                             " 旋转: " + rotation + "度");
        }
    }
  
    public String getGraphicType() { return graphicType; }
    public String getTexture() { return texture; }
    public int getWidth() { return width; }
    public int getHeight() { return height; }
}

// 图形工厂
public class GraphicFactory {
    private static final Map<String, GraphicFlyweight> graphics = new ConcurrentHashMap<>();
  
    public static GraphicFlyweight getGraphic(String graphicType, String texture, int width, int height) {
        String key = graphicType + "|" + texture + "|" + width + "|" + height;
        return graphics.computeIfAbsent(key, k -> 
            new GraphicFlyweight(graphicType, texture, width, height)
        );
    }
  
    public static int getGraphicCount() {
        return graphics.size();
    }
}

// 游戏对象
public class GameObject {
    private final GraphicFlyweight graphic;
    private final int x, y;
    private final int rotation;
    private final boolean visible;
  
    public GameObject(GraphicFlyweight graphic, int x, int y, int rotation, boolean visible) {
        this.graphic = graphic;
        this.x = x;
        this.y = y;
        this.rotation = rotation;
        this.visible = visible;
    }
  
    public void render() {
        graphic.render(x, y, rotation, visible);
    }
}

// 游戏场景
public class GameScene {
    private List<GameObject> objects = new ArrayList<>();
  
    public void addObject(String graphicType, String texture, int width, int height, 
                         int x, int y, int rotation, boolean visible) {
        GraphicFlyweight graphic = GraphicFactory.getGraphic(graphicType, texture, width, height);
        objects.add(new GameObject(graphic, x, y, rotation, visible));
    }
  
    public void render() {
        for (GameObject object : objects) {
            object.render();
        }
    }
  
    public int getObjectCount() {
        return objects.size();
    }
  
    public int getUniqueGraphicCount() {
        return GraphicFactory.getGraphicCount();
    }
}

3. 数据库连接池

java 复制代码
// 连接享元对象
public class ConnectionFlyweight {
    private final String url;
    private final String username;
    private final String password;
    private final String driverClass;
  
    public ConnectionFlyweight(String url, String username, String password, String driverClass) {
        this.url = url;
        this.username = username;
        this.password = password;
        this.driverClass = driverClass;
    }
  
    public Connection createConnection() throws SQLException {
        try {
            Class.forName(driverClass);
            return DriverManager.getConnection(url, username, password);
        } catch (ClassNotFoundException e) {
            throw new SQLException("Driver not found: " + driverClass, e);
        }
    }
  
    public String getUrl() { return url; }
    public String getUsername() { return username; }
    public String getPassword() { return password; }
    public String getDriverClass() { return driverClass; }
}

// 连接工厂
public class ConnectionFactory {
    private static final Map<String, ConnectionFlyweight> connections = new ConcurrentHashMap<>();
  
    public static ConnectionFlyweight getConnection(String url, String username, String password, String driverClass) {
        String key = url + "|" + username + "|" + password + "|" + driverClass;
        return connections.computeIfAbsent(key, k -> 
            new ConnectionFlyweight(url, username, password, driverClass)
        );
    }
  
    public static int getConnectionCount() {
        return connections.size();
    }
}

// 连接池
public class ConnectionPool {
    private final ConnectionFlyweight connectionFlyweight;
    private final Queue<Connection> availableConnections = new ConcurrentLinkedQueue<>();
    private final Set<Connection> usedConnections = ConcurrentHashMap.newKeySet();
    private final int maxSize;
  
    public ConnectionPool(String url, String username, String password, String driverClass, int maxSize) {
        this.connectionFlyweight = ConnectionFactory.getConnection(url, username, password, driverClass);
        this.maxSize = maxSize;
    }
  
    public Connection getConnection() throws SQLException {
        Connection connection = availableConnections.poll();
        if (connection == null) {
            if (usedConnections.size() < maxSize) {
                connection = connectionFlyweight.createConnection();
            } else {
                throw new SQLException("Connection pool exhausted");
            }
        }
        usedConnections.add(connection);
        return connection;
    }
  
    public void releaseConnection(Connection connection) {
        if (usedConnections.remove(connection)) {
            availableConnections.offer(connection);
        }
    }
  
    public void closeAll() throws SQLException {
        for (Connection connection : availableConnections) {
            connection.close();
        }
        for (Connection connection : usedConnections) {
            connection.close();
        }
        availableConnections.clear();
        usedConnections.clear();
    }
}

4. 缓存系统

java 复制代码
// 缓存项享元对象
public class CacheItemFlyweight {
    private final String key;
    private final Class<?> valueType;
    private final long ttl;
    private final boolean compress;
  
    public CacheItemFlyweight(String key, Class<?> valueType, long ttl, boolean compress) {
        this.key = key;
        this.valueType = valueType;
        this.ttl = ttl;
        this.compress = compress;
    }
  
    public void process(Object value) {
        System.out.println("处理缓存项: " + key + " 类型: " + valueType.getSimpleName() + 
                         " TTL: " + ttl + " 压缩: " + compress);
    }
  
    public String getKey() { return key; }
    public Class<?> getValueType() { return valueType; }
    public long getTtl() { return ttl; }
    public boolean isCompress() { return compress; }
}

// 缓存工厂
public class CacheItemFactory {
    private static final Map<String, CacheItemFlyweight> cacheItems = new ConcurrentHashMap<>();
  
    public static CacheItemFlyweight getCacheItem(String key, Class<?> valueType, long ttl, boolean compress) {
        String cacheKey = key + "|" + valueType.getName() + "|" + ttl + "|" + compress;
        return cacheItems.computeIfAbsent(cacheKey, k -> 
            new CacheItemFlyweight(key, valueType, ttl, compress)
        );
    }
  
    public static int getCacheItemCount() {
        return cacheItems.size();
    }
}

// 缓存管理器
public class CacheManager {
    private final Map<String, Object> cache = new ConcurrentHashMap<>();
    private final Map<String, CacheItemFlyweight> cacheItems = new ConcurrentHashMap<>();
  
    public void put(String key, Object value, Class<?> valueType, long ttl, boolean compress) {
        CacheItemFlyweight cacheItem = CacheItemFactory.getCacheItem(key, valueType, ttl, compress);
        cacheItems.put(key, cacheItem);
        cache.put(key, value);
        cacheItem.process(value);
    }
  
    public Object get(String key) {
        CacheItemFlyweight cacheItem = cacheItems.get(key);
        if (cacheItem != null) {
            cacheItem.process(cache.get(key));
        }
        return cache.get(key);
    }
  
    public void remove(String key) {
        cache.remove(key);
        cacheItems.remove(key);
    }
  
    public int getCacheSize() {
        return cache.size();
    }
  
    public int getUniqueCacheItemCount() {
        return CacheItemFactory.getCacheItemCount();
    }
}

面试高频点

面试知识点思维导图

享元模式面试点 基本概念 实现方式 重难点 Spring应用 设计原则 实际应用 共享内在状态 分离内外状态 减少对象数量 提高性能 Flyweight抽象享元类 ConcreteFlyweight具体享元类 FlyweightFactory享元工厂 Client客户端 内外状态分离 享元对象池管理 线程安全问题 性能优化 BeanDefinition享元 SecurityConfig享元 HandlerMapping享元 Pointcut享元 单一职责 开闭原则 依赖倒置 接口隔离 文本编辑器 游戏图形 数据库连接池 缓存系统

1. 享元模式的基本概念

问题:什么是享元模式?

答案要点:

  • 运用共享技术有效地支持大量细粒度的对象
  • 通过共享相同的内在状态来减少内存使用
  • 属于结构型设计模式
  • 提高系统性能,减少内存占用
问题:享元模式有哪些角色?

答案要点:

  • Flyweight(抽象享元类):声明接口,接受外部状态
  • ConcreteFlyweight(具体享元类):实现抽象享元接口,存储内部状态
  • UnsharedConcreteFlyweight(非共享具体享元类):不需要共享的享元类
  • FlyweightFactory(享元工厂类):创建并管理享元对象
  • Client(客户端):维护享元对象的引用,存储外部状态

2. 实现方式相关

问题:如何实现享元模式?

答案要点:

java 复制代码
// 1. 定义抽象享元类
public abstract class Flyweight {
    protected String intrinsicState;
  
    public Flyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }
  
    public abstract void operation(String extrinsicState);
}

// 2. 实现具体享元类
public class ConcreteFlyweight extends Flyweight {
    public ConcreteFlyweight(String intrinsicState) {
        super(intrinsicState);
    }
  
    @Override
    public void operation(String extrinsicState) {
        System.out.println("内在状态: " + intrinsicState + ", 外在状态: " + extrinsicState);
    }
}

// 3. 实现享元工厂
public class FlyweightFactory {
    private Map<String, Flyweight> flyweights = new HashMap<>();
  
    public Flyweight getFlyweight(String key) {
        return flyweights.computeIfAbsent(key, ConcreteFlyweight::new);
    }
}

3. 重难点问题

问题:如何区分内在状态和外在状态?

答案要点:

  • 内在状态:存储在享元对象内部,可以被多个对象共享,不会随环境变化
  • 外在状态:存储在客户端,随环境变化,不能被共享
  • 识别原则:如果状态在多个对象间相同且不变,则为内在状态;否则为外在状态
  • 示例:字符的字体、大小、颜色是内在状态;位置坐标是外在状态
问题:享元模式的线程安全问题如何解决?

答案要点:

java 复制代码
// 1. 使用ConcurrentHashMap
public class ThreadSafeFlyweightFactory {
    private final ConcurrentHashMap<String, Flyweight> flyweights = new ConcurrentHashMap<>();
  
    public Flyweight getFlyweight(String key) {
        return flyweights.computeIfAbsent(key, ConcreteFlyweight::new);
    }
}

// 2. 使用双重检查锁定
public class DoubleCheckedFlyweightFactory {
    private final Map<String, Flyweight> flyweights = new HashMap<>();
    private final Object lock = new Object();
  
    public Flyweight getFlyweight(String key) {
        Flyweight flyweight = flyweights.get(key);
        if (flyweight == null) {
            synchronized (lock) {
                flyweight = flyweights.get(key);
                if (flyweight == null) {
                    flyweight = new ConcreteFlyweight(key);
                    flyweights.put(key, flyweight);
                }
            }
        }
        return flyweight;
    }
}

4. Spring中的应用

问题:Spring中如何使用享元模式?

答案要点:

java 复制代码
// 1. BeanDefinition作为享元对象
public interface BeanDefinition {
    void setBeanClassName(String beanClassName);
    String getBeanClassName();
    // ... 其他方法
}

// 2. SecurityConfig作为享元对象
public class SecurityConfig implements ConfigAttribute {
    private final String config;
  
    public SecurityConfig(String config) {
        this.config = config;
    }
  
    @Override
    public String getAttribute() {
        return this.config;
    }
}

// 3. HandlerMapping作为享元对象
public interface HandlerMapping {
    HandlerExecutionChain getHandler(HttpServletRequest request);
}

5. 设计原则相关

问题:享元模式体现了哪些设计原则?

答案要点:

  • 单一职责:享元对象只负责内在状态的管理
  • 开闭原则:可以添加新的享元类型
  • 依赖倒置:依赖抽象而不是具体实现
  • 接口隔离:客户端只依赖需要的接口

6. 实际应用场景

问题:享元模式适用于哪些场景?

答案要点:

  • 文本编辑器:字符对象的共享
  • 游戏开发:图形对象的共享
  • 数据库连接池:连接配置的共享
  • 缓存系统:缓存项的共享
  • GUI组件:界面元素的共享
  • 网络编程:连接对象的共享

7. 与其他模式的对比

问题:享元模式与单例模式的区别?

答案要点:

  • 目的:享元模式是共享对象,单例模式是唯一实例
  • 数量:享元模式可以有多个实例,单例模式只有一个实例
  • 状态:享元模式有内在状态和外在状态,单例模式只有内在状态
  • 使用场景:享元模式用于大量相似对象,单例模式用于全局唯一对象
问题:享元模式与对象池模式的区别?

答案要点:

  • 目的:享元模式是共享状态,对象池模式是重用对象
  • 生命周期:享元模式对象长期存在,对象池模式对象短期存在
  • 状态:享元模式区分内外状态,对象池模式不区分
  • 使用场景:享元模式用于状态共享,对象池模式用于资源管理

总结

享元模式是一种重要的结构型设计模式,它通过共享相同的内在状态来减少内存使用,提高系统性能。

核心优势

  1. 内存优化:减少对象数量,降低内存占用
  2. 性能提升:避免重复创建相似对象
  3. 状态分离:清晰分离内在状态和外在状态
  4. 扩展性:易于添加新的享元类型

注意事项

  1. 复杂度增加:增加了系统的复杂度
  2. 状态管理:需要仔细管理内外状态
  3. 线程安全:多线程环境下需要注意线程安全
  4. 适用场景:只适用于有大量相似对象的场景

在实际开发中,享元模式特别适用于需要创建大量相似对象、内存占用较大的场景。通过合理使用享元模式,可以大大提高系统的性能和内存利用率。

相关推荐
青草地溪水旁2 小时前
设计模式(C++)详解—享元模式(1)
c++·设计模式·享元模式
郝学胜-神的一滴3 小时前
享元模式(Flyweight Pattern)
开发语言·前端·c++·设计模式·软件工程·享元模式
软件柠檬3 小时前
Java中Integer是如何应用享元模式的?
java·享元模式
charlie1145141913 小时前
精读《C++20设计模式》——创造型设计模式:构建器系列
c++·设计模式·c++20·构造器模式
Taylor不想被展开3 小时前
SpringBoot 项目集成 Flyway
java·spring boot·mysql
大数据003 小时前
Flink消费Datahub到ClickhouseSink
java·前端·flink
观望过往4 小时前
Spring Boot 高级特性:从原理到企业级实战
java·spring boot
喂完待续4 小时前
【序列晋升】38 Spring Data MongoDB 的统一数据访问范式与实践
java·spring·spring cloud·big data·序列晋升
郑洁文4 小时前
上门代管宠物系统的设计与实现
java·spring boot·后端·毕业设计·毕设