策略模式入门

基本概念

策略模式定义了算法族,分别封装起来, 让他们可以相互替换,此模式让算法的变化独立于使用算法的客户。

策略模式的重心不是如何来实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活、具有更好的维护性和扩展性。

策略模式的本质:分离算法,选择实现。

策略模式优点:

  1. 算法可以自由切换(高层屏蔽算法,角色自由切换)
  2. 避免使用多重条件判断(如果算法过多就会出现很多种相同的判断,很难维护)
  3. 扩展性好(可自由添加取消算法 而不影响整个功能)

策略模式缺点:

  1. 策略类数量增多(每一个策略类复用性很小,如果需要增加算法,就只能新增类)
  2. 所有的策略类都需要对外暴露(使用的人必须了解使用策略,这个就需要其它模式来补充,比如工厂模式、代理模式)

UML类图

策略模式代码示例

在移动项目开发中,不可避免需要展示图片, 这就需要从server端获取图片路径。 通常来说不同图片类型在server端的存储路径会有所区别,并且返回的可能不一定是图片的完整路径。 下面给出用策略模式解决这种情况的代码示例。

创建策略接口:

1
2
3
4
5
6
7
8
9
10
11
//server address
let ResourceHost = "https://www.devzhang.cn"

protocol IResourcePathStrategy{

/// 路径拼接方法
///
/// - Parameter generateID: 资源id()
/// - Returns: 资源全路径
func resourcePath(generateID:String)->String
}

创建具体策略:

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
/// 礼物资源图片路径拼接策略
class GiftResourcePathStrategy :IResourcePathStrategy{
let giftJointPath = "/img/gift/"
func resourcePath(generateID:String) -> String {
let giftPath = ResourceHost.appending(giftJointPath).appending(generateID).appending(".png")
return giftPath;
}
}

/// 动画资源路径拼接策略
class AnimationResourcePathStrategy: IResourcePathStrategy {
let animationJointPath = "/img/animation/"
func resourcePath(generateID:String) -> String {
let animationPath = ResourceHost.appending(animationJointPath).appending(generateID).appending(".zip")
return animationPath;
}
}

/// 用户头像图片路径拼接策略
class AvatarResourcePathStrategy: IResourcePathStrategy {
let avatarJointPath = "/img/avatar/"
func resourcePath(generateID:String) -> String {
let giftPath = ResourceHost.appending(avatarJointPath).appending(generateID).appending(".png")
return giftPath;
}
}

创建策略上下文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class ResourcePathContext {
//持有策略
private var strategy:IResourcePathStrategy

/// 资源id
private var generateID:String

///初始化策略上下文
init(stategy:IResourcePathStrategy, generateID:String) {
self.strategy = stategy
self.generateID = generateID
}


/// 调用具体策略实现
func generateResourcePath() -> String {
return self.strategy.resourcePath(generateID: self.generateID);
}
}

客户端调用代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//策略模式
//gift
let giftStrategy = GiftResourcePathStrategy()
let giftContext = ResourcePathContext(stategy: giftStrategy, generateID: "111")
let giftResourcePath = giftContext.generateResourcePath()
print("礼物id为111d的图片地址是" + giftResourcePath)


//animation
let animationStrategy = AnimationResourcePathStrategy()
let animationContext = ResourcePathContext(stategy: animationStrategy, generateID: "222")
let animationResourcePath = animationContext.generateResourcePath()
print("动画id为222的资源地址是" + animationResourcePath)

//avatar
let avatarStrategy = AvatarResourcePathStrategy()
let avatarContext = ResourcePathContext(stategy: avatarStrategy, generateID: "333")
let avatarResourcePath = avatarContext.generateResourcePath()
print("用户id为333的用户头像地址是" + avatarResourcePath)

策略枚举代码示例

策略枚举即在一个类中同时实现以上三种策略。

创建策略枚举类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum ResourcePath{
case giftResourcePath(giftID: String)
case animationResourcePath(animation:String)
case avatarResourcePath(avatar:String)

private static var giftJointPath = "/img/gift/"
private static var animationJointPath = "/img/animation/"
private static var avatarJointPath = "/img/avatar/"

var generateResourcePath:String {
switch self {
case .giftResourcePath(let giftID):
return ResourceHost.appending(ResourcePath.giftJointPath).appending(giftID).appending(".png")
case .animationResourcePath(let animationID):
return ResourceHost.appending(ResourcePath.animationJointPath).appending(animationID).appending(".zip")
case .avatarResourcePath(let userID):
return ResourceHost.appending(ResourcePath.avatarJointPath).appending(userID).appending(".png")
}

}
}

客户端调用:

1
2
3
4
//策略枚举
print(ResourcePath.giftResourcePath(giftID: "111").generateResourcePath)
print(ResourcePath.animationResourcePath(animation: "222").generateResourcePath)
print(ResourcePath.avatarResourcePath(avatar: "333").generateResourcePath)