1. 项目概述:当策略模式遇上抽象工厂
在UI开发领域,适配不同设备、分辨率和平台的挑战从未停止。最近我在重构一个跨平台项目时,尝试将策略模式与抽象工厂结合,意外收获了架构设计上的突破性进展。这种组合拳不仅解决了传统UI适配方案中条件分支泛滥的问题,还让整个系统的扩展性提升了至少3个量级。
这个方案的核心价值在于:当新增一种设备类型时,你只需要增加对应的工厂类和策略实现,原有代码完全不用修改。这完美符合开闭原则(OCP),特别适合需要支持Android、iOS、Web三端且各端有多个分辨率版本的项目场景。
2. 核心设计思路拆解
2.1 策略模式的精准打击
策略模式在这里扮演着"算法选择器"的角色。我们为不同类型的UI适配逻辑定义统一的策略接口:
public interface LayoutStrategy { void applyLayout(Component component); void adjustFont(TextElement text); void handleImage(ImageElement image); }然后针对不同设备实现具体策略:
// 手机竖屏策略 public class MobilePortraitStrategy implements LayoutStrategy { @Override public void applyLayout(Component c) { // 实现手机竖屏特有的布局逻辑 } } // 平板横屏策略 public class TabletLandscapeStrategy implements LayoutStrategy { // 实现平板横屏适配逻辑 }2.2 抽象工厂的批量生产
抽象工厂负责创建一整套相关的策略对象。关键设计在于工厂接口:
public interface StrategyFactory { LayoutStrategy createLayoutStrategy(); ColorStrategy createColorStrategy(); AnimationStrategy createAnimationStrategy(); }具体工厂实现示例:
// iOS设备工厂 public class IOSStrategyFactory implements StrategyFactory { @Override public LayoutStrategy createLayoutStrategy() { return new IOSLayoutStrategy(); } // 其他策略创建方法... } // 安卓平板工厂 public class AndroidTabletFactory implements StrategyFactory { @Override public LayoutStrategy createLayoutStrategy() { return new TabletLandscapeStrategy(); } // 其他策略创建方法... }3. 架构实现关键细节
3.1 环境检测与工厂选择
设备检测逻辑是整套架构的触发器,推荐使用责任链模式实现:
public class DeviceDetector { private List<DetectionHandler> handlers; public StrategyFactory detect() { for (DetectionHandler handler : handlers) { if (handler.canHandle()) { return handler.createFactory(); } } return new DefaultFactory(); } }3.2 策略上下文管理
策略上下文需要维护当前所有活跃策略:
public class StrategyContext { private LayoutStrategy layoutStrategy; private ColorStrategy colorStrategy; // 其他策略引用... public void applyAllStrategies() { layoutStrategy.applyLayout(); colorStrategy.applyColor(); // 应用所有策略... } }4. 性能优化实践
4.1 策略缓存机制
频繁创建策略对象会影响性能,可以采用享元模式优化:
public class StrategyCache { private static Map<String, LayoutStrategy> layoutCache = new ConcurrentHashMap<>(); public static LayoutStrategy getLayoutStrategy(String key) { return layoutCache.computeIfAbsent(key, k -> { // 根据key创建对应策略 }); } }4.2 懒加载策略
对于不常用的策略,可以实现按需加载:
public class LazyStrategy implements LayoutStrategy { private Supplier<LayoutStrategy> supplier; private LayoutStrategy realStrategy; @Override public void applyLayout(Component c) { if (realStrategy == null) { realStrategy = supplier.get(); } realStrategy.applyLayout(c); } }5. 实战中的经验教训
5.1 策略粒度的把控
经过多次迭代,我发现策略粒度需要平衡:
- 过细:导致策略类爆炸(曾出现200+策略类)
- 过粗:失去策略模式的优势
最佳实践是:
- 将变化频率相同的逻辑放在同一个策略中
- 每个策略类控制在300行代码以内
- 相关策略组成策略组
5.2 工厂与策略的版本兼容
在多版本共存的项目中,必须考虑:
public interface VersionAwareFactory extends StrategyFactory { boolean supportsVersion(int version); }在运行时选择兼容的工厂:
StrategyFactory factory = factories.stream() .filter(f -> ((VersionAwareFactory)f).supportsVersion(version)) .findFirst() .orElseGet(DefaultFactory::new);6. 扩展应用场景
6.1 动态主题切换
结合观察者模式实现实时主题切换:
public class ThemeManager { private List<StrategyFactory> factories; public void changeTheme(String theme) { StrategyFactory factory = getFactoryForTheme(theme); notifyAllComponents(factory); } }6.2 A/B测试支持
轻松实现UI方案的A/B测试:
public class ABTestFactory implements StrategyFactory { // 随机返回A或B版本的策略 public LayoutStrategy createLayoutStrategy() { return random.nextBoolean() ? new VersionAStrategy() : new VersionBStrategy(); } }7. 测试策略建议
7.1 工厂测试要点
重点验证:
- 工厂创建的对象不为null
- 创建的对象类型正确
- 不同工厂创建的策略是否兼容
示例测试用例:
@Test public void testMobileFactory() { StrategyFactory factory = new MobileStrategyFactory(); assertNotNull(factory.createLayoutStrategy()); assertTrue(factory.createColorStrategy() instanceof MobileColorStrategy); }7.2 策略组合测试
验证策略组合效果:
@Test public void testTabletLayoutWithDarkTheme() { StrategyContext context = new StrategyContext(); context.setLayoutStrategy(new TabletLayoutStrategy()); context.setColorStrategy(new DarkColorStrategy()); TestComponent component = new TestComponent(); context.applyAllStrategies(); assertEquals(TabletLayoutStrategy.EXPECTED_WIDTH, component.getWidth()); assertEquals(DarkColorStrategy.PRIMARY_COLOR, component.getBgColor()); }8. 典型问题排查指南
8.1 策略未生效常见原因
- 工厂选择错误:检查设备检测逻辑
- 策略应用顺序问题:某些策略需要先执行
- 策略冲突:多个策略修改同一属性
排查步骤:
graph TD A[策略未生效] --> B{工厂是否正确} B -->|是| C[检查策略应用顺序] B -->|否| D[修正设备检测逻辑] C --> E[检查策略依赖]8.2 内存泄漏预防
策略对象可能持有UI引用,需要:
- 在UI销毁时清除策略引用
- 避免策略中保存UI状态
- 使用弱引用持有UI组件
关键代码:
public class SafeLayoutStrategy implements LayoutStrategy { private WeakReference<Component> componentRef; @Override public void applyLayout() { Component c = componentRef.get(); if (c != null) { // 安全操作 } } }9. 性能监控方案
9.1 策略执行耗时统计
使用装饰器模式添加监控:
public class MonitoredStrategy implements LayoutStrategy { private LayoutStrategy realStrategy; @Override public void applyLayout(Component c) { long start = System.nanoTime(); realStrategy.applyLayout(c); long end = System.nanoTime(); Metrics.record("layout", end - start); } }9.2 工厂创建性能优化
对于初始化耗时的工厂:
public class CachedFactory implements StrategyFactory { private LayoutStrategy layoutStrategy; @Override public synchronized LayoutStrategy createLayoutStrategy() { if (layoutStrategy == null) { layoutStrategy = new HeavyLayoutStrategy(); } return layoutStrategy; } }10. 架构演进建议
当策略数量超过50个时,建议:
- 按功能域划分策略包
- 引入策略元数据配置
- 实现动态策略加载
配置化示例:
{ "strategyMappings": { "mobile-portrait": { "layout": "com.xxx.MobilePortraitLayout", "color": "com.xxx.LightColorScheme" } } }加载逻辑:
public LayoutStrategy createFromConfig(String configKey) { String className = config.getStrategyClass(configKey); return (LayoutStrategy) Class.forName(className).newInstance(); }在大型项目中,这套架构已经验证可以支持:
- 20+种设备类型
- 6种主题方案
- 3种交互模式 的灵活组合,而代码复杂度仅线性增长。