设计模式-策略模式

本文通过设计简单的勇者游戏角色,探讨了如何应对战斗方式的变化。在游戏更新引入新职业吟游诗人时,传统的继承方式导致战斗方法需要频繁修改。通过采用策略模式,将战斗行为抽象为接口,实现了战斗方式的灵活切换,减少了代码重复,增强了系统的可扩展性。同时,文章强调了设计模式中的开闭原则、依赖倒置原则和组合复用原则在实际问题解决中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设计简单的勇者游戏角色

在一款勇者冒险的游戏中,主角团的每个角色会有不同的职业King,Queen, Knight, Troll……,而且每个角色都可以战斗,但是每个人的相貌不一样
在这里插入图片描述

那么如果我们想设计这样的游戏角色,可以设置一个抽象超类Character
超类Character中有个方法:每个职业都会的行为fight()
另一个是方法:display(),由于每个职业的外貌不同,所以设置为抽象类,由不同职业自己实现自己的外貌

abstract class Character{
	// 长相
	abstract public void display();
	
	// 战斗方式
	public void fight() {
		System.out.println("Hit"); // 打击
	};
}

之后的每个职业都继承这个Character类,同时实现自己的长相display()
这样各自的外貌(display())有区别,又因为每个职业继承了父类,每个职业都会战斗(fight()),

class King extends Character{
	@Override
	public void display() {
		System.out.println("I wearing a crown"); //国王带着皇冠
		
	}
}

class Knight extends Character{……//其他职业省略

到此为止我们就设计了一套简单的勇者游戏角色

战斗方式发生变化

当当当当!游戏更新了,出现一种船新职业——Minstrel(吟游诗人)
就像其他职业一样,我们把她继承Character……实现自己的外貌……会战斗……等等!
本次更新的Minstrel不会战斗啊!她的作用是给队友加Buff

class Minstrel extends Character{
	@Override
	public void display() {
		System.out.println("Minstrel hold a lute"); //吟游诗人抱着鲁特琴
	}
}

这可怎么办,而且之后出现的很多个角色如果战斗方式都不一样,这个问题就更复杂了,很容易会想到下面的解决方法

Characterfight()变成抽象方法,让每个角色自己实现战斗

abstract class Character{
	// 这里表示其他方法…………
	
	// 战斗
	abstract public void fight();
}

class Minstrel extends Character{
	// 这里表示其他方法…………
	
	@Override
	public void fight() {
		System.out.println("Dont know how to fight");
	}
}

class King extends Character{
	// 这里表示其他方法…………
	
	@Override
	public void fight() {
		System.out.println("Hit");
	}
}

可以是可以,但是如果游戏中有几十个几百个职业,难道我要把每个职业的fight()都实现改一遍吗?那也太恐怖了,换方法

将会战斗(Fightable)和不会战斗(Fightless)设置为两个接口,让两类角色实现两类接口

interface Fightable{
	void fight();
}

interface Fightless{
	void fight();
}

class Minstrel extends Character implements Fightless{
	// 这里表示其他方法…………
	
	@Override
	public void fight() {
		System.out.println("Dont know how to fight");
	}
}

class King extends Character{
	// 这里表示其他方法…………
	
	@Override
	public void fight() {
		System.out.println("Hit");
	}
}

这个方法和上个方法一样,虽然实现了松耦合,但是代码重复率更高了,也没有起到什么实质性作用

把战斗方式提取出来

如果我们把职业中会发生变化的方法,单独提取出来封装,不和其他不变化的混合在一起,那么我们就可以只关心变化的部分

这里会变化的方法是战斗方法fight(),那我们就把所有战斗方法都提取出来
不妨设置一个Fight接口,这个接口中包装着战斗方法fight()

interface Fight{
	void fight();
}

比如会出现Cut,Hit, AddBuff, Raid等战斗方式

class CutFight implements Fight{
	public void fight(){
		System.out.println("Cut!");
	}
}

class HitFight implements Fight{
	public void fight(){
		System.out.println("Hit!");
	}
}

class AddBuffFight implements Fight{
	public void fight(){
		System.out.println("AddBuff!");
	}
}

class RaidFight implements Fight{
	public void fight(){
		System.out.println("Raid!");
	}
}

这样我们就可以在职业的超类Character中装入一个Fight接口变量,战斗的执行交给Fight接口变量的fight()方法执行

(了解单例模式的朋友也可以使用单例模式,这段代码并未使用)

interface Fight{
	void fight();
}

// 提取出的Cut战斗方式
class CutFight implements Fight{
	public void fight(){
		System.out.println("Cut!");
	}
}

abstract class Character{
	// Fight接口的变量
	Fight fightRef;
	// 长相
	abstract public void display();
	// 战斗方式
	public void fight() {
		fightRef.fight();
	};
}

class King extends Character{
	@Override
	public void display() {
		System.out.println("King wearing a crown"); //国王带着皇冠
	}
	
	public King(){
		fightRef = new Cut();
	}
}

在这里插入图片描述

这样就把每个方法提取出来,将变化的方法与不变方法的分离,使系统更有弹性
还可以在Character中对fightRef加入set()方法,这样就可以在运行时改变战斗方式

abstract class Character{
	// Fight接口的变量
	Fight fightRef;

	public setFightRef(Fight newFight){
		fightRef = newFight;
	}
	// 其他方法……
}

public static void main(){
	Character king = new King(); // Fight方式使用构造函数中的Cut
	king.fight();
	king.setFightRef(new RaidFight); // 修改Fight方式为Raid
	king.fight();
}

输出结果:

Cut!
Raid!

策略模式

恭喜你,你已经使用了策略模式
在刚刚我们修改角色设计时,用到的思想就是策略模式,它的正式定义为

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

我们还用了以下设计原则

  • 找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起
  • 针对接口(超类型)编程,而不是针对实现编程
  • 多用组合,少用继承

设计模式更像是一种思想,如果工程中找不到该使用哪种设计模式,就多考虑设计原则,学会设计模式也方便业内人士交流


参考:《Head first 设计模式》(封面真的chou)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值