享元模式详解
目录
享元模式简介
定义
享元模式(Flyweight Pattern)是一种结构型设计模式,它运用共享技术有效地支持大量细粒度的对象。享元模式通过共享相同的内在状态来减少内存使用,提高系统性能。
核心思想
- 共享内在状态:将对象的内在状态(不变部分)提取出来共享
- 分离内外状态:内在状态存储在享元对象中,外在状态由客户端维护
- 减少对象数量:通过共享减少系统中对象的数量
- 提高性能:减少内存占用,提高系统性能
模式结构
- Flyweight(抽象享元类):声明一个接口,通过它可以接受并作用于外部状态
- ConcreteFlyweight(具体享元类):实现抽象享元接口,并为内部状态增加存储空间
- UnsharedConcreteFlyweight(非共享具体享元类):不需要共享的享元类
- FlyweightFactory(享元工厂类):创建并管理享元对象,确保合理地共享享元对象
- Client(客户端):维护对享元对象的引用,计算或存储享元对象的外部状态
核心流程
享元模式流程图
基本实现流程
1. 定义抽象享元类
// 抽象享元类
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. 实现具体享元类
// 具体享元类
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. 实现享元工厂
// 享元工厂类
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. 客户端使用
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:内在状态和外在状态的分离
问题描述
如何正确识别和分离对象的内在状态和外在状态。
解决方案
// 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:享元对象池的管理
问题描述
如何高效地管理享元对象池,避免内存泄漏和性能问题。
解决方案
// 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:线程安全问题
问题描述
在多线程环境下,享元工厂的线程安全问题。
解决方案
// 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:享元模式的性能优化
问题描述
如何优化享元模式的性能,避免频繁的对象创建和查找。
解决方案
// 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的享元模式应用
// 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的享元模式
// 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的享元模式
// 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的享元模式
// 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. 文本编辑器
// 字符享元对象
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. 游戏中的图形对象
// 图形享元对象
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. 数据库连接池
// 连接享元对象
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. 缓存系统
// 缓存项享元对象
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();
}
}面试高频点
面试知识点思维导图
1. 享元模式的基本概念
问题:什么是享元模式?
答案要点:
- 运用共享技术有效地支持大量细粒度的对象
- 通过共享相同的内在状态来减少内存使用
- 属于结构型设计模式
- 提高系统性能,减少内存占用
问题:享元模式有哪些角色?
答案要点:
- Flyweight(抽象享元类):声明接口,接受外部状态
- ConcreteFlyweight(具体享元类):实现抽象享元接口,存储内部状态
- UnsharedConcreteFlyweight(非共享具体享元类):不需要共享的享元类
- FlyweightFactory(享元工厂类):创建并管理享元对象
- Client(客户端):维护享元对象的引用,存储外部状态
2. 实现方式相关
问题:如何实现享元模式?
答案要点:
// 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. 重难点问题
问题:如何区分内在状态和外在状态?
答案要点:
- 内在状态:存储在享元对象内部,可以被多个对象共享,不会随环境变化
- 外在状态:存储在客户端,随环境变化,不能被共享
- 识别原则:如果状态在多个对象间相同且不变,则为内在状态;否则为外在状态
- 示例:字符的字体、大小、颜色是内在状态;位置坐标是外在状态
问题:享元模式的线程安全问题如何解决?
答案要点:
// 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中如何使用享元模式?
答案要点:
// 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. 与其他模式的对比
问题:享元模式与单例模式的区别?
答案要点:
- 目的:享元模式是共享对象,单例模式是唯一实例
- 数量:享元模式可以有多个实例,单例模式只有一个实例
- 状态:享元模式有内在状态和外在状态,单例模式只有内在状态
- 使用场景:享元模式用于大量相似对象,单例模式用于全局唯一对象
问题:享元模式与对象池模式的区别?
答案要点:
- 目的:享元模式是共享状态,对象池模式是重用对象
- 生命周期:享元模式对象长期存在,对象池模式对象短期存在
- 状态:享元模式区分内外状态,对象池模式不区分
- 使用场景:享元模式用于状态共享,对象池模式用于资源管理
总结
享元模式是一种重要的结构型设计模式,它通过共享相同的内在状态来减少内存使用,提高系统性能。
核心优势
- 内存优化:减少对象数量,降低内存占用
- 性能提升:避免重复创建相似对象
- 状态分离:清晰分离内在状态和外在状态
- 扩展性:易于添加新的享元类型
注意事项
- 复杂度增加:增加了系统的复杂度
- 状态管理:需要仔细管理内外状态
- 线程安全:多线程环境下需要注意线程安全
- 适用场景:只适用于有大量相似对象的场景
在实际开发中,享元模式特别适用于需要创建大量相似对象、内存占用较大的场景。通过合理使用享元模式,可以大大提高系统的性能和内存利用率。
 
                    
                     
                    
                 
                    
                 
 
         
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号