一、故事解说:小明如何选择出行方式
假设小明要去上班,有多种出行方式:
-
天气晴朗:选择骑自行车(省钱、环保);
-
下雨:选择打车(快,不淋雨);
-
时间充裕:选择坐地铁(便宜,但慢)。
策略模式核心:定义一组算法(如出行方式),将每个算法封装起来(如自行车类、出租车类),使它们可以相互替换。客户端(小明)可以在运行时动态选择不同的算法。
二、策略模式核心结构(出行方式案例)
java
// 1. 策略接口:出行方式
interface TravelStrategy {
void travel(); // 出行方法
}
// 2. 具体策略:自行车
class BikeStrategy implements TravelStrategy {
@Override
public void travel() {
System.out.println("骑自行车上班,省钱环保");
}
}
// 3. 具体策略:出租车
class TaxiStrategy implements TravelStrategy {
@Override
public void travel() {
System.out.println("打出租车上班,快速舒适");
}
}
// 4. 具体策略:地铁
class SubwayStrategy implements TravelStrategy {
@Override
public void travel() {
System.out.println("坐地铁上班,便宜但拥挤");
}
}
// 5. 上下文:小明
class XiaoMing {
private TravelStrategy strategy;
public void setStrategy(TravelStrategy strategy) {
this.strategy = strategy;
}
public void goToWork() {
if (strategy == null) {
System.out.println("请先选择出行方式");
return;
}
strategy.travel();
}
}
// 6. 客户端:动态选择策略
public class Client {
public static void main(String[] args) {
XiaoMing xiaoming = new XiaoMing();
// 晴天:骑自行车
xiaoming.setStrategy(new BikeStrategy());
xiaoming.goToWork(); // 输出:骑自行车上班,省钱环保
// 下雨:打出租车
xiaoming.setStrategy(new TaxiStrategy());
xiaoming.goToWork(); // 输出:打出租车上班,快速舒适
}
}
三、Android 常用策略模式案例与实现
案例 1:图片加载策略(Glide/Picasso 选择)
java
// 1. 策略接口:图片加载器
interface ImageLoader {
void loadImage(String url, ImageView imageView);
}
// 2. 具体策略:Glide加载器
class GlideLoader implements ImageLoader {
@Override
public void loadImage(String url, ImageView imageView) {
Glide.with(imageView.getContext())
.load(url)
.into(imageView);
System.out.println("使用Glide加载图片:" + url);
}
}
// 3. 具体策略:Picasso加载器
class PicassoLoader implements ImageLoader {
@Override
public void loadImage(String url, ImageView imageView) {
Picasso.get()
.load(url)
.into(imageView);
System.out.println("使用Picasso加载图片:" + url);
}
}
// 4. 上下文:图片加载管理器
class ImageLoaderManager {
private ImageLoader loader;
public ImageLoaderManager(ImageLoader loader) {
this.loader = loader;
}
public void setLoader(ImageLoader loader) {
this.loader = loader;
}
public void load(String url, ImageView imageView) {
if (loader == null) {
throw new IllegalStateException("请先设置图片加载器");
}
loader.loadImage(url, imageView);
}
}
// 5. 在Activity中使用
public class MainActivity extends AppCompatActivity {
private ImageView imageView;
private ImageLoaderManager manager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.imageView);
// 初始化管理器,默认使用Glide
manager = new ImageLoaderManager(new GlideLoader());
// 加载图片
manager.load("https://example.com/image.jpg", imageView);
// 动态切换到Picasso
manager.setLoader(new PicassoLoader());
manager.load("https://example.com/another_image.jpg", imageView);
}
}
优点:
-
可替换性:可在运行时动态切换图片加载库(如从 Glide 切换到 Picasso);
-
解耦实现:图片加载逻辑与 Activity 分离,代码更清晰;
-
扩展性好:新增图片加载库(如 Fresco)只需实现 ImageLoader 接口,无需修改现有代码。
缺点:
- 类数量增加:每个策略需要一个类,若策略过多会导致类膨胀;
- 客户端需了解策略:Activity 需要知道有哪些策略(Glide/Picasso),违反迪米特法则。
案例 2:加密策略(MD5/SHA-256 选择)
java
// 1. 策略接口:加密算法
interface EncryptionStrategy {
String encrypt(String data);
}
// 2. 具体策略:MD5加密
class MD5Strategy implements EncryptionStrategy {
@Override
public String encrypt(String data) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(data.getBytes());
return bytesToHex(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return data;
}
}
private String bytesToHex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b));
}
return result.toString();
}
}
// 3. 具体策略:SHA-256加密
class SHA256Strategy implements EncryptionStrategy {
@Override
public String encrypt(String data) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(data.getBytes());
return bytesToHex(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return data;
}
}
private String bytesToHex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b));
}
return result.toString();
}
}
// 4. 上下文:加密管理器
class EncryptionManager {
private EncryptionStrategy strategy;
public EncryptionManager(EncryptionStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(EncryptionStrategy strategy) {
this.strategy = strategy;
}
public String encryptData(String data) {
return strategy.encrypt(data);
}
}
// 5. 在Activity中使用
public class LoginActivity extends AppCompatActivity {
private EditText passwordEditText;
private EncryptionManager manager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
passwordEditText = findViewById(R.id.password);
// 默认使用SHA-256加密
manager = new EncryptionManager(new SHA256Strategy());
}
public void onLoginClick(View view) {
String password = passwordEditText.getText().toString();
// 加密密码
String encryptedPassword = manager.encryptData(password);
// 发送到服务器
sendToServer(encryptedPassword);
}
// 切换加密策略(如用户选择更安全的加密方式)
public void switchToMD5(View view) {
manager.setStrategy(new MD5Strategy());
Toast.makeText(this, "已切换到MD5加密", Toast.LENGTH_SHORT).show();
}
}
优点:
-
算法独立:不同加密算法(MD5/SHA-256)相互独立,便于维护和测试;
-
运行时切换:可根据用户需求或安全级别动态切换加密算法;
-
符合开闭原则:新增加密算法(如 AES)只需实现 EncryptionStrategy 接口,无需修改现有代码。
缺点:
- 策略选择逻辑复杂:若加密算法选择逻辑复杂(如根据数据大小、类型选择),会增加客户端代码复杂度;
- 策略创建成本:每次创建新策略对象(如 new SHA256Strategy ())可能有性能开销。
案例 3:缓存策略(内存缓存 / 磁盘缓存)
java
// 1. 策略接口:缓存
interface CacheStrategy {
void put(String key, Object value);
Object get(String key);
void clear();
}
// 2. 具体策略:内存缓存(使用LruCache)
class MemoryCacheStrategy implements CacheStrategy {
private LruCache<String, Object> cache;
public MemoryCacheStrategy() {
// 获取最大可用内存的1/8作为缓存大小
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
cache = new LruCache<String, Object>(cacheSize) {
@Override
protected int sizeOf(String key, Object value) {
return 1; // 简单实现,实际应计算对象大小
}
};
}
@Override
public void put(String key, Object value) {
cache.put(key, value);
System.out.println("内存缓存:存入" + key);
}
@Override
public Object get(String key) {
Object value = cache.get(key);
System.out.println("内存缓存:获取" + key + (value != null ? "成功" : "失败"));
return value;
}
@Override
public void clear() {
cache.evictAll();
System.out.println("内存缓存:已清空");
}
}
// 3. 具体策略:磁盘缓存(简化示例)
class DiskCacheStrategy implements CacheStrategy {
private Context context;
public DiskCacheStrategy(Context context) {
this.context = context;
}
@Override
public void put(String key, Object value) {
// 实际中应将数据写入文件
System.out.println("磁盘缓存:存入" + key);
}
@Override
public Object get(String key) {
// 实际中应从文件读取数据
System.out.println("磁盘缓存:获取" + key);
return null; // 简化示例
}
@Override
public void clear() {
// 实际中应删除所有缓存文件
System.out.println("磁盘缓存:已清空");
}
}
// 4. 上下文:缓存管理器
class CacheManager {
private CacheStrategy strategy;
public CacheManager(CacheStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(CacheStrategy strategy) {
this.strategy = strategy;
}
public void cacheData(String key, Object value) {
strategy.put(key, value);
}
public Object retrieveData(String key) {
return strategy.get(key);
}
public void clearCache() {
strategy.clear();
}
}
// 5. 在Activity中使用
public class MainActivity extends AppCompatActivity {
private CacheManager manager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 默认使用内存缓存
manager = new CacheManager(new MemoryCacheStrategy());
// 缓存数据
manager.cacheData("user_info", new User("张三", 25));
// 读取数据
User user = (User) manager.retrieveData("user_info");
// 切换到磁盘缓存
manager.setStrategy(new DiskCacheStrategy(this));
manager.cacheData("config", new Config());
}
}
优点:
-
按需选择缓存:根据数据重要性和使用频率选择内存缓存或磁盘缓存;
-
解耦缓存逻辑:Activity 无需关心缓存实现细节,只通过 CacheManager 操作;
-
扩展性好:新增缓存策略(如双缓存、网络缓存)只需实现 CacheStrategy 接口。
缺点:
- 缓存同步问题:切换缓存策略后,可能需要处理数据同步问题(如内存缓存和磁盘缓存不一致);
- 策略配置复杂:若有多种缓存策略组合(如先查内存再查磁盘),会增加配置复杂度。
四、策略模式的适用场景与总结
适用场景:
- 多种算法可选:如图片加载、加密、缓存、排序等场景,有多种实现方式;
- 运行时动态切换:需要根据条件(如用户偏好、网络状态)动态选择算法;
- 避免多重条件判断:替代大量
if-else或switch语句,使代码更清晰; - 算法独立变化:算法的实现细节经常变化,使用策略模式可独立维护。
核心优点:
- 算法解耦:将不同算法封装到独立策略类中,提高可维护性;
- 可替换性:可在运行时动态切换算法,灵活性高;
- 符合开闭原则:新增策略无需修改现有代码,只需实现接口;
- 避免条件语句:替代复杂的条件判断,使代码更简洁。
核心缺点:
- 类数量增加:每个策略需一个类,导致类膨胀;
- 策略暴露:客户端需要知道有哪些策略,增加使用难度;
- 策略创建成本:频繁创建策略对象可能影响性能(可通过策略工厂 + 享元模式优化)。
Android 中的最佳实践:
-
利用系统策略:如 Android 的动画插值器(AccelerateInterpolator、DecelerateInterpolator)就是策略模式的应用;
-
自定义策略时:
-
使用枚举或工厂类管理策略,避免客户端直接依赖具体策略类;
-
对策略进行分组(如按功能、性能),减少客户端选择复杂度;
-
结合单例模式或享元模式复用策略对象,降低创建成本。
-
策略模式是 Android 开发中最常用的设计模式之一,理解它有助于写出更灵活、可维护的代码。