摘要:而且修改老代码,大大增加了出现的概率。这里菜菜再强调一遍架构设计的一项重要原则类应该对修改关闭,对扩展开放。附加在对象最外层的行为,不应该窥视被包装的类型内部的一些特性。基于以上的设计思想,扩展的行为完全有能力修改,覆盖玩家的某些行为。
初级版本
这是玩家的抽象基础类,这个设计很好,把一些玩家共有的特性抽象出来
//玩家的基础抽象类 abstract class Player { //玩家的级别 public int Level { get; set; } //其他属性代码省略一万字 }
这是新加需求:10级可以跳跃,具体跳跃动作是客户端做处理
//玩家的基础抽象类 abstract class Player { //玩家的级别 public int Level { get; set; } //其他属性代码省略一万字 //新加玩家跳跃动作,由于需要到达10级所以需要判断level public virtual bool Jump() { if (Level >= 10) { return true; } return false; } }
这种代码初级人员很容易犯,有什么问题呢?
跳跃的动作被添加到了基类,那所有的子类就都有了这个行为,如果子类机器人玩家不需要这个跳跃的行为呢?
为了新需求,修改了基类,如果每次需求都需要修改基类,时间长了,项目大了,这个是比较要命的。
优化版本由于需求是增加玩家一个行为,根据上一节的介绍,我们应该了解到,行为在代码级别更倾向于用接口来表示。而且不是所有的玩家类型都需要附加跳跃这个行为。据此优化如下:
//玩家跳跃的行为 interface IJump { bool Jump(); } //玩家的基础抽象类 abstract class Player { //玩家的级别 public int Level { get; set; } //其他属性代码省略一万字 } //真实玩家 class PersonPlayer : Player, IJump { public bool Jump() { if (Level >= 10) { return true; } return false; } }
不错,到此我们已经避免了初级人员所犯的错误了,每种玩家类型可以根据需要自行去扩展行为,改天产品狗在加一个10级玩家可以飞的行为,顶多在加一个IFly的行为接口,然后实现即可。但是这样的设计就没有问题了吗?有,当然有
每次需求其实还是改动了已经存在的并且稳定运行的老代码,这是不可取的。而且修改老代码,大大增加了bug出现的概率。
假如现在我们的游戏有20种玩家类型,其中19种需要添加跳跃的行为,那我们需要修改19个玩家的子类,工作量是如此之大。
利用类似继承的方式扩展对象的行为,是在编译期就把对象的行为确定了。也就是说在设计层面,其实你已经把代码写死了。
有很多同学的代码就到目前为止了
假设以下为产品狗一个月之后的新需求:
能跳跃的等级调整为11级
玩家添加能遁地的行为
新加了10种玩家类型
如果你读到了这里,说明大家都是对于设计追求卓越的技术人。这里菜菜再强调一遍架构设计的一项重要原则
类应该对修改关闭,对扩展开放。
这里需要强调一点,设计的每个部分想要都遵循开放-关闭原则,通常很难做到。因为要想在不修改现有代码的情况下,你需要花费许多时间和精力。遵循开放关闭原则,通常需要引入更多的抽象,增加更多的层次,增大代码的复杂度。因此菜菜建议把注意力集中在业务中最有可能变化的点上,这些地方应用开放关闭原则。至于怎么确定哪些是变化的点,这需要对业务领域很强的理解和经验了。
现在我们分析一下我们要做的事情,我们希望一个对象(player)在不改动的情况下动态的给它赋予新的行为,在业务上实现的功能和用继承的结果类似。总之一句话:
现有的类型优雅的添加新行为,并且可以灵活叠加和替换
理想中的设计图大致如下:
现在我们认真分析一下,如果每个新的行为要想扩展对象而又能保持该对象的自身特性,新行为对象必须是扩展对象的子类,还必须包含对象的一个引用才能实现。
在系统设计过程中,实现一个接口泛指实现某个对象的超类型,也就是说可以是类或者接口。
在你系统设计中,如果你的代码依赖于某个具体的类型,并非抽象的超类型,应用此篇介绍的设计方法可能会受到影响。
附加在对象最外层的行为,不应该窥视被包装的类型内部的一些特性。
附加在对象外层的行为,可以在内层对象的行为前后加入自己的行为,甚至可以覆盖掉内层对象的行为。
如果扩展的行为过多,会出现很多小对象,过度使用会使程序变的很复杂,所以设计扩展行为时候需要注意。
落实到代码假设现在真实玩家的定义如下:
//玩家的基础抽象类 public abstract class Player { //玩家的级别 public int Level { get; set; } //其他属性代码省略一万字 } //真实玩家 public class PersonPlayer : Player { }
现在的需求是给真实玩家添加一个10级能跳跃的行为,在不修改原有玩家代码的情况下,扩展跳跃行为代码如下
//玩家行为的扩展积累 public class PlayerExtension : Player { protected Player player; } //跳跃玩家的行为扩展类 public class PlayerJumpExtension: PlayerExtension { public PlayerJumpExtension(Player _player) { player = _player; } public bool Jump() { if (player. Level >= 10) { return true; } return false; } }
测试代码如下:
PersonPlayer player = new PersonPlayer(); //给用户动态添加跳跃的行为 PlayerJumpExtension jumpPlayer = new PlayerJumpExtension(player); var ret= jumpPlayer.Jump(); Console.WriteLine("玩家能不能跳跃:"+ret); //现在玩家升级到10级了 player.Level = 10; ret = jumpPlayer.Jump(); Console.WriteLine("玩家能不能跳跃:" + ret);
测试加过如下:
玩家能不能跳跃:False 玩家能不能跳跃:True
一个月后产品狗新加一个需求:真实玩家20级获得飞行的行为,无序改动现有代码,只需继续添加一个可以飞行的新扩展
//玩家可以飞行的扩展 public class PlayerFlyExtension : PlayerExtension { public PlayerFlyExtension(Player _player) { player = _player; } public bool Fly() { if (player.Level >= 20) { return true; } return false; } }
测试代码如下:
PlayerFlyExtension flyPlayer = new PlayerFlyExtension(player); Console.WriteLine( "玩家能不能飞行"+flyPlayer.Fly()); player.Level = 20; Console.WriteLine("玩家能不能飞行" + flyPlayer.Fly());
测试结果:
玩家能不能飞行False 玩家能不能飞行True重要提示
以上代码级别上属于演示代码,但是设计的理念却很重要。基于以上的设计思想,扩展的行为完全有能力修改,覆盖玩家的某些行为。比如玩家对象本身有一个喊话的行为,那扩展类根据业务完全可以让喊话行为执行两次等等修改。
添加关注,查看更精美版本,收获更多精彩
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/74744.html
摘要:里氏替换原则里氏代换原则面向对象设计的基本原则之一。里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。里氏代换原则是对开闭原则的补充。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。 showImg(https://segmentfault.com/img/bVbuXAu?w=640&h=361); 本文为本次系列文章的第一篇,接下...
摘要:介绍超文本传输协议,是互联网上应用最为广泛的一种网络协议。年美国人构思了一种通过计算机处理文本信息的方法,并称之为超文本这成为了超文本传输协议标准架构的发展根基。 Http介绍 超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML...
摘要:以及之前说过的,当程序员就是为了提高社会效率。写高效的代码是每个程序员的追求,写易懂的代码是每个程序员的美德。让正确的程序更快,要比让快速的程序正确容易得多。我觉得这样才能当一个有格局的程序员。 在博客阅读:https://ssshooter.com/2019-04... 工作 写程序不是为了炫耀自己的技术,是要给公司创造价值,要确实帮助使用这个程序的人。以及之前说过的,当程序员就是为...
摘要:幸而,提供了造物主的接口这便是,或者称为元类。接下来我们将通过一个栗子感受的黑魔法,不过在此之前,我们要先了解一个语法糖。此外,在一些小型的库中,也有元类的身影。 首发于 我的博客 转载请注明出处 接触过 Django 的同学都应该十分熟悉它的 ORM 系统。对于 python 新手而言,这是一项几乎可以被称作黑科技的特性:只要你在models.py中随便定义一个Model的子类,Dj...
阅读 2682·2021-09-26 10:19
阅读 2135·2021-09-24 10:27
阅读 2519·2021-09-01 10:42
阅读 2301·2019-08-29 16:09
阅读 2484·2019-08-29 15:17
阅读 1447·2019-08-29 15:09
阅读 632·2019-08-29 11:14
阅读 2301·2019-08-26 13:25