Apex Design Patterns - 策略模式

Posted by Peter Dong on November 28, 2021

策略模式

策略模式(Strategy Pattern)是一种行为设计模式,它允许你定义一系列算法,将每个算法封装到独立的类中,并使它们可以相互替换。

定义

策略模式使得算法可以在不影响客户端的情况下发生变化。其主要目的是通过定义相似的算法,替换 if-else 语句的写法,并且可以在运行时动态切换不同的策略。

优势

  1. 提高代码的可维护性和可扩展性
  2. 将算法的实现与使用算法的代码分离
  3. 避免使用多重条件语句
  4. 运行时切换算法,增加了灵活性

为什么使用策略模式?

在复杂的系统中,如果在同一个类中包含了太多的行为代码,可能会导致以下问题:

  1. 代码变得复杂,难以维护
  2. 新开发人员难以理解和修改代码
  3. 多个开发人员同时在一个复杂的类上工作可能会产生冲突

通过使用策略模式,我们可以创建包含特定类型行为的独立类,而不是将所有行为混合在一起。这样可以在运行时根据需要调用不同的策略,提高代码的灵活性和可维护性。

问题示例

假设您需要提供一种基于地理位置的搜索引擎解决方案,其中执行代码可以在运行时选择不同的搜索提供者。例如:

1
2
geocode('test address')
// => 37.78,-129.39

策略模式解决方案

让我们使用策略模式来实现这个需求:

1. 定义策略接口

首先,我们定义一个通用的策略接口:

1
2
3
public interface GeocodeStrategy {
    List<Double> getLatLong(String address);
}

2. 实现具体策略

然后,我们为不同的地图服务提供商实现具体的策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class GoogleMapsStrategyImpl implements GeocodeStrategy {
    public List<Double> getLatLong(String address) {
        // 调用Google Maps API
        // 这里简化了实现,实际应该调用真实的API
        return new List<Double>{37.78, -122.41};
    }
}

public class BaiduMapsStrategyImpl implements GeocodeStrategy {
    public List<Double> getLatLong(String address) {
        // 调用百度地图API
        // 这里简化了实现,实际应该调用真实的API
        return new List<Double>{39.90, 116.40};
    }
}

3. 创建上下文类

接下来,我们创建一个上下文类来管理策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Geocoder {
    public class GeocoderException extends Exception {}
    
    private static final Map<String, GeocodeStrategy> strategies;
    
    static {
        // 从自定义标签中获取策略名称列表
        List<String> strategyNames = Label.GeocodeStrategies.split(',');
        
        strategies = new Map<String, GeocodeStrategy>();
        for (String name : strategyNames) {
            try {
                strategies.put(name, (GeocodeStrategy)Type.forName(name + 'Impl').newInstance());
            } catch (Exception e) {
                System.debug('Error initializing strategy: ' + name + '. ' + e.getMessage());
            }
        }
    }
    
    private GeocodeStrategy strategy;
    
    public Geocoder(String strategyName) {
        if (!strategies.containsKey(strategyName)) {
            throw new GeocoderException('Invalid strategy name: ' + strategyName);
        }
        strategy = strategies.get(strategyName);
    }
    
    public List<Double> getLatLong(String address) {
        return strategy.getLatLong(address);
    }
}

4. 使用策略

现在,我们可以在代码中使用不同的策略:

1
2
3
4
5
6
7
8
9
try {
    Geocoder googleGeocoder = new Geocoder('GoogleMapsStrategy');
    System.debug('Google Maps result: ' + googleGeocoder.getLatLong('1600 Amphitheatre Parkway, Mountain View, CA'));

    Geocoder baiduGeocoder = new Geocoder('BaiduMapsStrategy');
    System.debug('Baidu Maps result: ' + baiduGeocoder.getLatLong('北京市海淀区上地十街10号'));
} catch (Geocoder.GeocoderException e) {
    System.debug('Error: ' + e.getMessage());
}

策略模式的优势

  1. 灵活性:可以轻松添加新的策略,而不需要修改现有代码
  2. 可维护性:每个策略都在自己的类中,便于维护和测试
  3. 解耦:策略的使用者(Geocoder)和具体策略实现解耦,便于独立开发和测试
  4. 运行时切换:可以在运行时根据需要切换不同的策略
  5. 避免条件语句:减少了复杂的条件语句,提高了代码的可读性

在 Salesforce 中的应用

在 Salesforce Apex 开发中,策略模式可以在以下场景中发挥作用:

  1. 实现不同的定价策略
  2. 处理不同类型的数据导入/导出
  3. 实现多种通知方式(如邮件、短信、应用内通知等)
  4. 根据不同条件应用不同的审批流程
  5. 处理不同的数据同步策略

注意事项

  1. 当策略数量较少且不经常改变时,使用简单的条件语句可能更合适
  2. 确保客户端代码了解不同策略的差异,以便正确选择策略
  3. 策略模式会增加类的数量,在简单场景下可能导致过度设计

结论

策略模式是一种强大的设计模式,特别适合于处理一系列相关但不同的算法或行为。在 Salesforce Apex 开发中,合理使用策略模式可以提高代码的灵活性、可维护性和可扩展性。通过将算法封装在独立的类中,我们可以更容易地添加新的行为,同时保持现有代码的稳定性。

记住,设计模式是工具,不是规则。在应用策略模式时,要根据具体情况和需求来决定,确保它真正为你的应用带来价值,而不是增加不必要的复杂性。


Buy Me a Coffee