C#设计模式如何实现
这篇文章主要介绍“C#设计模式如何实现”,在日常操作中,相信很多人在C#设计模式如何实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C#设计模式如何实现”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
游戏背景
这是一个简单的打怪游戏,有玩家,有怪兽,玩家作为主角光环,有如下三个特殊能力
攻击怪兽有暴击几率
有几率回避怪兽攻击
可以自己治疗一定生命值
游戏实现
角色类
角色基类
首先是角色类,角色类提供玩家和怪兽最基本的抽象,比如血量、攻击力、攻击和治疗。(对于怪兽来说,治疗是没有提供实现的,坏人肯定不能再治疗了)
class Character{ public int HealthPoint { get; set; } public int AttackPoint { get; set; } public virtual void AttackChracter(Character opponent) { opponent.HealthPoint -= this.AttackPoint; if (opponent.HealthPoint < 0) { opponent.HealthPoint = 0; } } public virtual void Cure() {//故意留空给子类实现 }}
玩家类
玩家实现了治疗功能并且有暴击几率。
class Player : Character{ private float playerCriticalPossible; public Player(float critical) { playerCriticalPossible = critical; } public override void AttackChracter(Character opponent) { base.AttackChracter(opponent); Console.WriteLine("Player Attacked Monster"); Random r = new Random(); bool critical = r.Next(0, 100) < playerCriticalPossible * 100; if (critical) { base.AttackChracter(opponent); Console.WriteLine("Player Attacked Monster again"); } } public override void Cure() { Random r = new Random(); HealthPoint += r.Next(5, 10); Console.WriteLine("Player cured himself"); }}
怪兽类
怪兽没有治疗能力但是有一定的几率丢失攻击目标。
class Monster : Character{ private float monsterMissingPossible; public Monster(float missing) { monsterMissingPossible = missing; } public override void AttackChracter(Character opponent) { Random r = new Random(); bool missing = r.Next(0, 100) < monsterMissingPossible * 100; if (missing) { Console.WriteLine("Monster missed it"); } else { base.AttackChracter(opponent); Console.WriteLine("Monster Attacked player"); } }}
游戏类
游戏类负责实例化玩家和怪兽、记录回合数、判断游戏是否结束,暴露可调用的公共方法给游戏操作类。
class Game{ private Character m_player; private Character m_monster; private int m_round; private float playerCriticalPossible = 0.6f; private float monsterMissingPossible = 0.2f; public Game() { m_player = new Player(playerCriticalPossible) { HealthPoint = 15, AttackPoint = 2 }; m_monster = new Monster(monsterMissingPossible) { HealthPoint = 20, AttackPoint = 6 }; } public bool IsGameOver => m_monster.HealthPoint == 0 || m_player.HealthPoint == 0; public void AttackMonster() { m_player.AttackChracter(m_monster); } public void AttackPlayer() { m_monster.AttackChracter(m_player); } public void CurePlayer() { m_player.Cure(); } public void BeginNewRound() { m_round++; } public void ShowGameState() { Console.WriteLine("".PadLeft(20, '-')); Console.WriteLine("Round:{0}", m_round); Console.WriteLine("player health:{0}", "".PadLeft(m_player.HealthPoint, '*')); Console.WriteLine("monster health:{0}", "".PadLeft(m_monster.HealthPoint, '*')); }}
游戏操作类
在我们这个简易游戏中,没有UI代码,游戏操作类负责在用户输入和游戏中搭建一个桥梁,解释用户的输入。
class GameRunner{ private Game m_game; public GameRunner(Game game) { m_game = game; } public void Run() { while (!m_game.IsGameOver) { m_game.BeginNewRound(); bool validSelection = false; while (!validSelection) { m_game.ShowGameState(); Console.WriteLine("Make your choice: 1. attack 2. Cure"); var str = Console.ReadLine(); if (str.Length != 1) { continue; } switch (str[0]) { case '1': { validSelection = true; m_game.AttackMonster(); break; } case '2': { validSelection = true; m_game.CurePlayer(); break; } default: break; } } if(!m_game.IsGameOver) { m_game.AttackPlayer(); } } }}
客户端
客户端的代码就非常简单了,只需要实例化一个游戏操作类,然后让其运行就可以了。
class Program{ static void Main(string[] args) { Game game = new Game(); GameRunner runner = new GameRunner(game); runner.Run(); }}
试着运行一下,
看起来一切都好。
加上存档
虽然游戏可以正常运行,但是总感觉还是少了点什么。嗯,存档功能,一个游戏没有存档是不健全的,毕竟,人生虽然没有存档,但是游戏可是有的!让我们加上存档功能吧,首先想想怎么设计。
需要存档的数据
首先我们要明确,有哪些数据是需要存档的,在这个游戏中,玩家的生命值、攻击力、暴击率;怪兽的生命值、攻击力和丢失率,游戏的回合数,都是需要存储的对象。
存档定义
这是一个需要仔细思考的地方,一般来说,需要考虑以下几个地方:
存档需要访问一些游戏中的私有字段,比如暴击率,需要在不破坏游戏封装的情况下实现这个功能
存档自身需要实现信息隐藏,即除了游戏,其他类不应该访问存档的详细信息
存档不应该和游戏存放在一起,以防不经意间游戏破坏了存档数据,应该有专门的类存放存档
备忘录模式出场
这个时候应该是主角出场的时候了。看看备忘录模式的定义
在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
再看看UML,
看起来完全符合我们的需求啊,Originator就是游戏类,知道如何创造存档和从存档中恢复状态,Memento类就是存档类,Caretaker是一个新类,负责保存存档。
经过思考,我们决定采取备忘录模式,同时加入以下措施:
将存档定义为游戏中的私有嵌套类,这样存档可以毫无压力的访问游戏中的私有字段,同时外界永远没有办法去实例化或者尝试通过转型来获得这个类,完美的保护了存档类
存档类是一个简单的数据集合,不包含任何其他逻辑
添加一个存档管理器,可以放在游戏操作类中,可以通过它看到我们当前有没有存档
存档放在存档管理器中
存档实现一个空接口,在存档管理器中以空接口形式出现,这样外部类在访问存档的时候,仅能看到这个空接口。而在游戏类内部,我们在使用存档之前先通过向下转型实现类型转换(是的,向下转型不怎么好,但是偶尔可以用一下)
空接口
interface IGameSave{}
私有嵌套存档类
该类存放在game里面,无压力地在不破坏封装的情况下访问game私有字段
private class GameSave : IGameSave{ public int PlayerHealth { get; set; } public int PlayerAttack { get; set; } public float PlayerCritialAttackPossible { get; set; } public int MonsterHealth { get; set; } public int MonsterAttack { get; set; } public float MonsterMissingPossible { get; set; } public int GameRound { get; set; }}
创建存档和从存档恢复
在game中添加创建存档和从存档恢复的代码,在从存档恢复的时候,使用了向下转型,因为从存档管理器读出来的只是空接口而已
public IGameSave CreateSave(){ var save = new GameSave() { PlayerHealth = m_player.HealthPoint, PlayerAttack = m_player.AttackPoint, PlayerCritialAttackPossible = playerCriticalPossible, MonsterAttack = m_monster.AttackPoint, MonsterHealth = m_monster.HealthPoint, MonsterMissingPossible = monsterMissingPossible, GameRound = m_round }; Console.WriteLine("game saved"); return save;}public void RestoreFromGameSave(IGameSave gamesave){ GameSave save = gamesave as GameSave; if(save != null) { m_player = new Player(save.PlayerCritialAttackPossible) { HealthPoint = save.PlayerHealth, AttackPoint = save.PlayerAttack }; m_monster = new Player(save.MonsterMissingPossible) { HealthPoint = save.MonsterHealth, AttackPoint = save.MonsterAttack }; m_round = save.GameRound; } Console.WriteLine("game restored");}
存档管理器类
添加一个类专门管理存档,此类非常简单,只有一个存档,要支持多存档可以考虑使用List
class GameSaveStore { public IGameSave GameSave { get; set; } }
在游戏操作类添加玩家选项
首先在游戏操作类中添加一个存档管理器
private GameSaveStore m_gameSaveStore = new GameSaveStore();
接着修改Run方法添加用户操作
public void Run(){ while (!m_game.IsGameOver) { m_game.BeginNewRound(); bool validSelection = false; while (!validSelection) { m_game.ShowGameState(); Console.WriteLine("Make your choice: 1. attack 2. Cure 3. Save 4. Load"); var str = Console.ReadLine(); if (str.Length != 1) { continue; } switch (str[0]) { case '1': { validSelection = true; m_game.AttackMonster(); break; } case '2': { validSelection = true; m_game.CurePlayer(); break; } case '3': { validSelection = false; m_gameSaveStore.GameSave = m_game.CreateSave(); break; } case '4': { validSelection = false; if(m_gameSaveStore.GameSave == null) { Console.WriteLine("no save to load"); } else { m_game.RestoreFromGameSave(m_gameSaveStore.GameSave); } break; } default: break; } } if(!m_game.IsGameOver) { m_game.AttackPlayer(); } } }
注意,上面的3和4是新添加的存档相关的操作。试着运行一下。
看起来一切正常,这样我们就使用备忘录模式,完成了存档读档的功能。
到此,关于“C#设计模式如何实现”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341