做者: 点先生, 时间: 2018.6.26 java
不知道是否有许多萌新跟我同样,在看java源码的时候,脑壳容易晕。一般查一个方法,要跳几个类出来,有些类动不动就上千行。像我这样血气方刚的少年,哪静得下心来理解这么多结构复杂的代码!还不如看点番剧,喝点快乐肥宅水! 编程
提倡使用设计模式的根本缘由是为了代码复用,增长可维护性。如今被命名的23种设计模式就是遵照了如下六大设计原则,才达到了代码复用,增长可维护性的目的。设计模式
看了这么多理论东西了,再不来点代码刺激刺激神经,我都要点右上角了。接下来,咱们就经过一个例子,小写一点代码,让咱们更好的理解设计模式! 如今咱们来设计一个游戏人物。(为了突出重点,更好的理解,只讲部分功能,理解要表达的意思便可,并不会真正的把全部功能实现。)bash
class Character {
//使用武器
fun useWeapon() {
Log.i("TAG", "fist")
}
}
复制代码
如今有了一个初步人物模型的设计,实现了攻击的方法。当人物到了必定等级,能够转职为魔法师,道士,战士的话,咱们能够这样写:ide
class Character {
//使用武器
fun useWeapon(profession : String) {
if(profession.equals("magician")){
Log.i("TAG", "火墙风咆哮")
}
if(profession.equals("taoist")){
Log.i("TAG", "召唤4级宝宝")
}
if(profession.equals("warrior")){
Log.i("TAG", "刀刀烈火")
}
}
}
复制代码
若是每一个方法都这样写,当方法数量增多的时候,这样的写法就变得很杂乱无章。**还致使了影响类变化缘由不止一个,也就违反了“单一职责原则”。**那咱们如今换一种方式来写:建立Magician,Taoist,Warrior三个类。在父类中采用重载的方式来实现useWeapon()。函数
//魔法师使用武器
fun useWeapon(magician: Magician) {
Log.i("TAG", "火墙风咆哮")
}
//道士使用武器
fun useWeapon(taoist: Taoist) {
Log.i("TAG", "召唤4级宝宝")
}
//战士使用武器
fun useWeapon(warrior: Warrior) {
Log.i("TAG", "刀刀烈火")
}
复制代码
这样作更傻,不只没有遵照单一职责原则,还违反了迪米特法则。 实际上,当咱们一看到这种需求,有点经验的都知道,应该写三个类,分别对应魔法师,道士,战士,而不是把全部东西写进一个类里面来。useWeapon方法写在父类中的缺点已经暴露出来了,直觉告诉咱们,这个方法是应该写在子类中的。那父类中的这个方法留不留呢?里式替换原则告诉咱们,子类能够扩展父类的功能,但不能改变父类原有的功能。因而乎……post
class Magician : Character(){
fun useWeapon(){
Log.i("TAG", "火墙风咆哮")
}
}
复制代码
class Taoist : Character(){
fun useWeapon(){
Log.i("TAG", "召唤4级宝宝")
}
}
复制代码
class Warrior : Character() {
fun useWeapon(){
Log.i("TAG", "刀刀烈火")
}
}
复制代码
如今来看,好像没什么问题哦。可是却没有遵照依赖倒置原则。咱们应该尽可能的针对接口编程,而不是针对实现编程。而如今咱们把实际的行为都写在子类当中!这样的坏处的是你必须去在每个子类中手写useWeapon方法,修改起来至关麻烦!并且在代码执行时没办法更改具体行为(除非写更多代码,但那样并划不来)。学习
如今这样写还有点怪怪的,明明每个类有useWeapon(),却不能写进父类中。很气有没有! 那咱们把character写成一个接口?把方法写进去? 牛逼!真是太聪明了!既不违反单一职责原则,也不违反迪米特法则,还遵照了里式替换原则。 ui
如今咱们只考虑了玩家的角色,游戏里的NPC怎么办? 你打得过NPC? 假如咱们已经把父类写成了接口,再建立一个npc类,看看吧。this
interface Character {
//使用武器
fun useWeapon()
}
复制代码
class NPC :Character {
override fun useWeapon() {
//空方法
}
}
复制代码
这样形成了npc类里面有一个空方法。你可能永远都不会去用它。那放这儿有什么意思?这样写还违反了接口隔离原则:不要对外暴露没有实际意义的接口!不要对外暴露没有实际意义的接口!不要对外暴露没有实际意义的接口! 问题不大!只须要打一个响指!咱们从新理一理思绪!
如今问题在于咱们要让某些子类实现useWeapon(),而不是所有子类都要去实现useWeapon()。 okk的!useWeapon()既然不能放进接口里面,也不能放进父类里面,那咱们就把这个方法单独提出来,新写一个useWeapon接口!
interface IUseWeapon {
fun useWeapon()
}
复制代码
而后让有攻击功能的子类来实现IUseWeapon接口。 emmmmmmmmm…… 这样使用接口仍是得一个个去写子类实现的具体方法,并且也没有遵照依赖倒置原则,在上面我已经写过了,依赖倒置原则的核心思想就在于面向接口编程,那面向接口编程是个啥意思呢?
“针对接口编程”真正的意思是“针对超类型编程”。
//针对实现编程
var magician : Magician = Magician()
//针对接口\超类型编程
var character : Character = Magician()
复制代码
此时咱们已经有了一个IUseWeapon接口,里面只有一个useWeapon()方法。咱们不能用子类直接实现IUseWeapon,也不能用父类直接实现IUseWeapon,那咱们就专门建立一个“行为类”来实现行为接口!
class UseFireWall : IUseWeapon {
override fun useWeapon() {
Log.i("TAG", "火墙风咆哮")
}
}
复制代码
class UseDogBaby : IUseWeapon{
override fun useWeapon() {
Log.i("TAG", "召唤4级宝宝")
}
}
复制代码
class UseFireKnife : IUseWeapon{
override fun useWeapon() {
Log.i("TAG", "刀刀烈火")
}
}
复制代码
这样的设计,就让使用武器这个行为跟character类无关了,还能够被其余对象服复用。而新增一些使用武器行为时候,不会影响到既有的行为类,也不会影响使用到行为类的character类。
如今咱们在整合一下整我的物的设计:
目标:遵照六大设计原则的条件下,使Magician, Taoist, Warrior 才有useWeapon()
要实现攻击的功能,那父类确定得有调用useWeapon()的方法,也必须得拥有行为接口。因此……
open class Character {
lateinit var iUseWeapon :IUseWeapon
fun coverUseWeapon(){
iUseWeapon.useWeapon()
}
}
复制代码
在编译时,已经能经过charater.coverUseWeapon()调用使用武器的方法。在代码真正执行时,子类还没对iUseWeapon进行声明,因此在子类中要作的只是声明iUseWeapon而已。这个时候,行为类实现行为接口的好处就体现出来了,咱们但愿子类作什么样的攻击,就能够声明为何样的行为类,要想有新的新的攻击动做,再建立一个行为类去实现IUseWeapon就能够了。反正实现的代码没有写在子类中,而是在行为类中,不用更改以前写的全部代码。
class Magician : Character(){
init {
iUseWeapon = UseFireWall()
}
}
复制代码
美滋滋!就这么简单的一行代码!但这样仍是不够灵活,咱们仍是在子类中作了一小部分的具体实现(建立iUseWeapon的实例),也就是没有彻底的作到针对接口编程,因此咱们须要有一个能够更改iUseWeapon实例的方法。
java代码中,咱们能够在父类中加入set方法。
public void setIUseWeapon(iUseWeapon : IUseWeapon) {
this.iUseWeapon = iUseWeapon ;
}
复制代码
kotlin代码中,直接在声明character对象处
//让魔法师召唤4级宝宝
var character : Character = Magician()
character.iUseWeapon = UseDogBaby()
复制代码
子类里面的init方法,就能够彻底不写了。
相信各位看到这里对六大原则其中五个都有了必定的理解,剩下一个开闭原则没有提到,是由于,这玩意儿很差讲! 设计模式就是个经验性的东西,你彻底能够不照着这样去写你的代码,只要遵照六大设计原则,都是好的设计。
如下是我“设计模式系列”文章,欢迎你们关注留言投币丢香蕉。